libopenmpt-0.6.1+release.autotools/0000755000175000017500000000000014175541576014353 500000000000000libopenmpt-0.6.1+release.autotools/build-aux/0000755000175000017500000000000014175541574016243 500000000000000libopenmpt-0.6.1+release.autotools/build-aux/ar-lib0000755000175000017500000001336314175541545017263 00000000000000#! /bin/sh # Wrapper for Microsoft lib.exe me=ar-lib scriptversion=2019-07-04.01; # UTC # Copyright (C) 2010-2020 Free Software Foundation, Inc. # Written by Peter Rosin . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # This file is maintained in Automake, please report # bugs to or send patches to # . # func_error message func_error () { echo "$me: $1" 1>&2 exit 1 } file_conv= # func_file_conv build_file # Convert a $build file to $host form and store it in $file # Currently only supports Windows hosts. func_file_conv () { file=$1 case $file in / | /[!/]*) # absolute file, and not a UNC file if test -z "$file_conv"; then # lazily determine how to convert abs files case `uname -s` in MINGW*) file_conv=mingw ;; CYGWIN* | MSYS*) file_conv=cygwin ;; *) file_conv=wine ;; esac fi case $file_conv in mingw) file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` ;; cygwin | msys) file=`cygpath -m "$file" || echo "$file"` ;; wine) file=`winepath -w "$file" || echo "$file"` ;; esac ;; esac } # func_at_file at_file operation archive # Iterate over all members in AT_FILE performing OPERATION on ARCHIVE # for each of them. # When interpreting the content of the @FILE, do NOT use func_file_conv, # since the user would need to supply preconverted file names to # binutils ar, at least for MinGW. func_at_file () { operation=$2 archive=$3 at_file_contents=`cat "$1"` eval set x "$at_file_contents" shift for member do $AR -NOLOGO $operation:"$member" "$archive" || exit $? done } case $1 in '') func_error "no command. Try '$0 --help' for more information." ;; -h | --h*) cat <. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # This file is maintained in Automake, please report # bugs to or send patches to # . nl=' ' # We need space, tab and new line, in precisely that order. Quoting is # there to prevent tools from complaining about whitespace usage. IFS=" "" $nl" file_conv= # func_file_conv build_file lazy # Convert a $build file to $host form and store it in $file # Currently only supports Windows hosts. If the determined conversion # type is listed in (the comma separated) LAZY, no conversion will # take place. func_file_conv () { file=$1 case $file in / | /[!/]*) # absolute file, and not a UNC file if test -z "$file_conv"; then # lazily determine how to convert abs files case `uname -s` in MINGW*) file_conv=mingw ;; CYGWIN* | MSYS*) file_conv=cygwin ;; *) file_conv=wine ;; esac fi case $file_conv/,$2, in *,$file_conv,*) ;; mingw/*) file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` ;; cygwin/* | msys/*) file=`cygpath -m "$file" || echo "$file"` ;; wine/*) file=`winepath -w "$file" || echo "$file"` ;; esac ;; esac } # func_cl_dashL linkdir # Make cl look for libraries in LINKDIR func_cl_dashL () { func_file_conv "$1" if test -z "$lib_path"; then lib_path=$file else lib_path="$lib_path;$file" fi linker_opts="$linker_opts -LIBPATH:$file" } # func_cl_dashl library # Do a library search-path lookup for cl func_cl_dashl () { lib=$1 found=no save_IFS=$IFS IFS=';' for dir in $lib_path $LIB do IFS=$save_IFS if $shared && test -f "$dir/$lib.dll.lib"; then found=yes lib=$dir/$lib.dll.lib break fi if test -f "$dir/$lib.lib"; then found=yes lib=$dir/$lib.lib break fi if test -f "$dir/lib$lib.a"; then found=yes lib=$dir/lib$lib.a break fi done IFS=$save_IFS if test "$found" != yes; then lib=$lib.lib fi } # func_cl_wrapper cl arg... # Adjust compile command to suit cl func_cl_wrapper () { # Assume a capable shell lib_path= shared=: linker_opts= for arg do if test -n "$eat"; then eat= else case $1 in -o) # configure might choose to run compile as 'compile cc -o foo foo.c'. eat=1 case $2 in *.o | *.[oO][bB][jJ]) func_file_conv "$2" set x "$@" -Fo"$file" shift ;; *) func_file_conv "$2" set x "$@" -Fe"$file" shift ;; esac ;; -I) eat=1 func_file_conv "$2" mingw set x "$@" -I"$file" shift ;; -I*) func_file_conv "${1#-I}" mingw set x "$@" -I"$file" shift ;; -l) eat=1 func_cl_dashl "$2" set x "$@" "$lib" shift ;; -l*) func_cl_dashl "${1#-l}" set x "$@" "$lib" shift ;; -L) eat=1 func_cl_dashL "$2" ;; -L*) func_cl_dashL "${1#-L}" ;; -static) shared=false ;; -Wl,*) arg=${1#-Wl,} save_ifs="$IFS"; IFS=',' for flag in $arg; do IFS="$save_ifs" linker_opts="$linker_opts $flag" done IFS="$save_ifs" ;; -Xlinker) eat=1 linker_opts="$linker_opts $2" ;; -*) set x "$@" "$1" shift ;; *.cc | *.CC | *.cxx | *.CXX | *.[cC]++) func_file_conv "$1" set x "$@" -Tp"$file" shift ;; *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO]) func_file_conv "$1" mingw set x "$@" "$file" shift ;; *) set x "$@" "$1" shift ;; esac fi shift done if test -n "$linker_opts"; then linker_opts="-link$linker_opts" fi exec "$@" $linker_opts exit 1 } eat= case $1 in '') echo "$0: No command. Try '$0 --help' for more information." 1>&2 exit 1; ;; -h | --h*) cat <<\EOF Usage: compile [--help] [--version] PROGRAM [ARGS] Wrapper for compilers which do not understand '-c -o'. Remove '-o dest.o' from ARGS, run PROGRAM with the remaining arguments, and rename the output as expected. If you are trying to build a whole package this is not the right script to run: please start by reading the file 'INSTALL'. Report bugs to . EOF exit $? ;; -v | --v*) echo "compile $scriptversion" exit $? ;; cl | *[/\\]cl | cl.exe | *[/\\]cl.exe | \ icl | *[/\\]icl | icl.exe | *[/\\]icl.exe ) func_cl_wrapper "$@" # Doesn't return... ;; esac ofile= cfile= for arg do if test -n "$eat"; then eat= else case $1 in -o) # configure might choose to run compile as 'compile cc -o foo foo.c'. # So we strip '-o arg' only if arg is an object. eat=1 case $2 in *.o | *.obj) ofile=$2 ;; *) set x "$@" -o "$2" shift ;; esac ;; *.c) cfile=$1 set x "$@" "$1" shift ;; *) set x "$@" "$1" shift ;; esac fi shift done if test -z "$ofile" || test -z "$cfile"; then # If no '-o' option was seen then we might have been invoked from a # pattern rule where we don't need one. That is ok -- this is a # normal compilation that the losing compiler can handle. If no # '.c' file was seen then we are probably linking. That is also # ok. exec "$@" fi # Name of file we expect compiler to create. cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'` # Create the lock directory. # Note: use '[/\\:.-]' here to ensure that we don't use the same name # that we are using for the .o file. Also, base the name on the expected # object file name, since that is what matters with a parallel build. lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d while true; do if mkdir "$lockdir" >/dev/null 2>&1; then break fi sleep 1 done # FIXME: race condition here if user kills between mkdir and trap. trap "rmdir '$lockdir'; exit 1" 1 2 15 # Run the compile. "$@" ret=$? if test -f "$cofile"; then test "$cofile" = "$ofile" || mv "$cofile" "$ofile" elif test -f "${cofile}bj"; then test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile" fi rmdir "$lockdir" exit $ret # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: libopenmpt-0.6.1+release.autotools/build-aux/config.guess0000755000175000017500000012637314175541545020515 00000000000000#! /bin/sh # Attempt to guess a canonical system name. # Copyright 1992-2018 Free Software Foundation, Inc. timestamp='2018-02-24' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # # Originally written by Per Bothner; maintained since 2000 by Ben Elliston. # # You can get the latest version of this script from: # https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess # # Please send patches to . me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] Output the configuration name of the system \`$me' is run on. Options: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. Copyright 1992-2018 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" >&2 exit 1 ;; * ) break ;; esac done if test $# != 0; then echo "$me: too many arguments$help" >&2 exit 1 fi trap 'exit 1' 1 2 15 # CC_FOR_BUILD -- compiler used by this script. Note that the use of a # compiler to aid in system detection is discouraged as it requires # temporary files to be created and, as you can see below, it is a # headache to deal with in a portable fashion. # Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still # use `HOST_CC' if defined, but it is deprecated. # Portable tmp directory creation inspired by the Autoconf team. set_cc_for_build=' trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; : ${TMPDIR=/tmp} ; { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; dummy=$tmp/dummy ; tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; case $CC_FOR_BUILD,$HOST_CC,$CC in ,,) echo "int x;" > "$dummy.c" ; for c in cc gcc c89 c99 ; do if ($c -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then CC_FOR_BUILD="$c"; break ; fi ; done ; if test x"$CC_FOR_BUILD" = x ; then CC_FOR_BUILD=no_compiler_found ; fi ;; ,,*) CC_FOR_BUILD=$CC ;; ,*,*) CC_FOR_BUILD=$HOST_CC ;; esac ; set_cc_for_build= ;' # This is needed to find uname on a Pyramid OSx when run in the BSD universe. # (ghazi@noc.rutgers.edu 1994-08-24) if (test -f /.attbin/uname) >/dev/null 2>&1 ; then PATH=$PATH:/.attbin ; export PATH fi UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown case "$UNAME_SYSTEM" in Linux|GNU|GNU/*) # If the system lacks a compiler, then just pick glibc. # We could probably try harder. LIBC=gnu eval "$set_cc_for_build" cat <<-EOF > "$dummy.c" #include #if defined(__UCLIBC__) LIBC=uclibc #elif defined(__dietlibc__) LIBC=dietlibc #else LIBC=gnu #endif EOF eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`" # If ldd exists, use it to detect musl libc. if command -v ldd >/dev/null && \ ldd --version 2>&1 | grep -q ^musl then LIBC=musl fi ;; esac # Note: order is significant - the case branches are not exclusive. case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in *:NetBSD:*:*) # NetBSD (nbsd) targets should (where applicable) match one or # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently # switched to ELF, *-*-netbsd* would select the old # object file format. This provides both forward # compatibility and a consistent mechanism for selecting the # object file format. # # Note: NetBSD doesn't particularly care about the vendor # portion of the name. We always set it to "unknown". sysctl="sysctl -n hw.machine_arch" UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \ "/sbin/$sysctl" 2>/dev/null || \ "/usr/sbin/$sysctl" 2>/dev/null || \ echo unknown)` case "$UNAME_MACHINE_ARCH" in armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; sh3eb) machine=sh-unknown ;; sh5el) machine=sh5le-unknown ;; earmv*) arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,'` endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'` machine="${arch}${endian}"-unknown ;; *) machine="$UNAME_MACHINE_ARCH"-unknown ;; esac # The Operating System including object format, if it has switched # to ELF recently (or will in the future) and ABI. case "$UNAME_MACHINE_ARCH" in earm*) os=netbsdelf ;; arm*|i386|m68k|ns32k|sh3*|sparc|vax) eval "$set_cc_for_build" if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ELF__ then # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). # Return netbsd for either. FIX? os=netbsd else os=netbsdelf fi ;; *) os=netbsd ;; esac # Determine ABI tags. case "$UNAME_MACHINE_ARCH" in earm*) expr='s/^earmv[0-9]/-eabi/;s/eb$//' abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"` ;; esac # The OS release # Debian GNU/NetBSD machines have a different userland, and # thus, need a distinct triplet. However, they do not need # kernel version information, so it can be replaced with a # suitable tag, in the style of linux-gnu. case "$UNAME_VERSION" in Debian*) release='-gnu' ;; *) release=`echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2` ;; esac # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # contains redundant information, the shorter form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. echo "$machine-${os}${release}${abi}" exit ;; *:Bitrig:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` echo "$UNAME_MACHINE_ARCH"-unknown-bitrig"$UNAME_RELEASE" exit ;; *:OpenBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` echo "$UNAME_MACHINE_ARCH"-unknown-openbsd"$UNAME_RELEASE" exit ;; *:LibertyBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'` echo "$UNAME_MACHINE_ARCH"-unknown-libertybsd"$UNAME_RELEASE" exit ;; *:MidnightBSD:*:*) echo "$UNAME_MACHINE"-unknown-midnightbsd"$UNAME_RELEASE" exit ;; *:ekkoBSD:*:*) echo "$UNAME_MACHINE"-unknown-ekkobsd"$UNAME_RELEASE" exit ;; *:SolidBSD:*:*) echo "$UNAME_MACHINE"-unknown-solidbsd"$UNAME_RELEASE" exit ;; macppc:MirBSD:*:*) echo powerpc-unknown-mirbsd"$UNAME_RELEASE" exit ;; *:MirBSD:*:*) echo "$UNAME_MACHINE"-unknown-mirbsd"$UNAME_RELEASE" exit ;; *:Sortix:*:*) echo "$UNAME_MACHINE"-unknown-sortix exit ;; *:Redox:*:*) echo "$UNAME_MACHINE"-unknown-redox exit ;; mips:OSF1:*.*) echo mips-dec-osf1 exit ;; alpha:OSF1:*:*) case $UNAME_RELEASE in *4.0) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` ;; *5.*) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` ;; esac # According to Compaq, /usr/sbin/psrinfo has been available on # OSF/1 and Tru64 systems produced since 1995. I hope that # covers most systems running today. This code pipes the CPU # types through head -n 1, so we only detect the type of CPU 0. ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` case "$ALPHA_CPU_TYPE" in "EV4 (21064)") UNAME_MACHINE=alpha ;; "EV4.5 (21064)") UNAME_MACHINE=alpha ;; "LCA4 (21066/21068)") UNAME_MACHINE=alpha ;; "EV5 (21164)") UNAME_MACHINE=alphaev5 ;; "EV5.6 (21164A)") UNAME_MACHINE=alphaev56 ;; "EV5.6 (21164PC)") UNAME_MACHINE=alphapca56 ;; "EV5.7 (21164PC)") UNAME_MACHINE=alphapca57 ;; "EV6 (21264)") UNAME_MACHINE=alphaev6 ;; "EV6.7 (21264A)") UNAME_MACHINE=alphaev67 ;; "EV6.8CB (21264C)") UNAME_MACHINE=alphaev68 ;; "EV6.8AL (21264B)") UNAME_MACHINE=alphaev68 ;; "EV6.8CX (21264D)") UNAME_MACHINE=alphaev68 ;; "EV6.9A (21264/EV69A)") UNAME_MACHINE=alphaev69 ;; "EV7 (21364)") UNAME_MACHINE=alphaev7 ;; "EV7.9 (21364A)") UNAME_MACHINE=alphaev79 ;; esac # A Pn.n version is a patched version. # A Vn.n version is a released version. # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. echo "$UNAME_MACHINE"-dec-osf"`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`" # Reset EXIT trap before exiting to avoid spurious non-zero exit code. exitcode=$? trap '' 0 exit $exitcode ;; Amiga*:UNIX_System_V:4.0:*) echo m68k-unknown-sysv4 exit ;; *:[Aa]miga[Oo][Ss]:*:*) echo "$UNAME_MACHINE"-unknown-amigaos exit ;; *:[Mm]orph[Oo][Ss]:*:*) echo "$UNAME_MACHINE"-unknown-morphos exit ;; *:OS/390:*:*) echo i370-ibm-openedition exit ;; *:z/VM:*:*) echo s390-ibm-zvmoe exit ;; *:OS400:*:*) echo powerpc-ibm-os400 exit ;; arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) echo arm-acorn-riscix"$UNAME_RELEASE" exit ;; arm*:riscos:*:*|arm*:RISCOS:*:*) echo arm-unknown-riscos exit ;; SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) echo hppa1.1-hitachi-hiuxmpp exit ;; Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. if test "`(/bin/universe) 2>/dev/null`" = att ; then echo pyramid-pyramid-sysv3 else echo pyramid-pyramid-bsd fi exit ;; NILE*:*:*:dcosx) echo pyramid-pyramid-svr4 exit ;; DRS?6000:unix:4.0:6*) echo sparc-icl-nx6 exit ;; DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) case `/usr/bin/uname -p` in sparc) echo sparc-icl-nx7; exit ;; esac ;; s390x:SunOS:*:*) echo "$UNAME_MACHINE"-ibm-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`" exit ;; sun4H:SunOS:5.*:*) echo sparc-hal-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" exit ;; sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) echo sparc-sun-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`" exit ;; i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) echo i386-pc-auroraux"$UNAME_RELEASE" exit ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) eval "$set_cc_for_build" SUN_ARCH=i386 # If there is a compiler, see if it is configured for 64-bit objects. # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. # This test works for both compilers. if [ "$CC_FOR_BUILD" != no_compiler_found ]; then if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then SUN_ARCH=x86_64 fi fi echo "$SUN_ARCH"-pc-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" exit ;; sun4*:SunOS:6*:*) # According to config.sub, this is the proper way to canonicalize # SunOS6. Hard to guess exactly what SunOS6 will be like, but # it's likely to be more like Solaris than SunOS4. echo sparc-sun-solaris3"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" exit ;; sun4*:SunOS:*:*) case "`/usr/bin/arch -k`" in Series*|S4*) UNAME_RELEASE=`uname -v` ;; esac # Japanese Language versions have a version number like `4.1.3-JL'. echo sparc-sun-sunos"`echo "$UNAME_RELEASE"|sed -e 's/-/_/'`" exit ;; sun3*:SunOS:*:*) echo m68k-sun-sunos"$UNAME_RELEASE" exit ;; sun*:*:4.2BSD:*) UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3 case "`/bin/arch`" in sun3) echo m68k-sun-sunos"$UNAME_RELEASE" ;; sun4) echo sparc-sun-sunos"$UNAME_RELEASE" ;; esac exit ;; aushp:SunOS:*:*) echo sparc-auspex-sunos"$UNAME_RELEASE" exit ;; # The situation for MiNT is a little confusing. The machine name # can be virtually everything (everything which is not # "atarist" or "atariste" at least should have a processor # > m68000). The system name ranges from "MiNT" over "FreeMiNT" # to the lowercase version "mint" (or "freemint"). Finally # the system name "TOS" denotes a system which is actually not # MiNT. But MiNT is downward compatible to TOS, so this should # be no problem. atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint"$UNAME_RELEASE" exit ;; atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint"$UNAME_RELEASE" exit ;; *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) echo m68k-atari-mint"$UNAME_RELEASE" exit ;; milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) echo m68k-milan-mint"$UNAME_RELEASE" exit ;; hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) echo m68k-hades-mint"$UNAME_RELEASE" exit ;; *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) echo m68k-unknown-mint"$UNAME_RELEASE" exit ;; m68k:machten:*:*) echo m68k-apple-machten"$UNAME_RELEASE" exit ;; powerpc:machten:*:*) echo powerpc-apple-machten"$UNAME_RELEASE" exit ;; RISC*:Mach:*:*) echo mips-dec-mach_bsd4.3 exit ;; RISC*:ULTRIX:*:*) echo mips-dec-ultrix"$UNAME_RELEASE" exit ;; VAX*:ULTRIX*:*:*) echo vax-dec-ultrix"$UNAME_RELEASE" exit ;; 2020:CLIX:*:* | 2430:CLIX:*:*) echo clipper-intergraph-clix"$UNAME_RELEASE" exit ;; mips:*:*:UMIPS | mips:*:*:RISCos) eval "$set_cc_for_build" sed 's/^ //' << EOF > "$dummy.c" #ifdef __cplusplus #include /* for printf() prototype */ int main (int argc, char *argv[]) { #else int main (argc, argv) int argc; char *argv[]; { #endif #if defined (host_mips) && defined (MIPSEB) #if defined (SYSTYPE_SYSV) printf ("mips-mips-riscos%ssysv\\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_SVR4) printf ("mips-mips-riscos%ssvr4\\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) printf ("mips-mips-riscos%sbsd\\n", argv[1]); exit (0); #endif #endif exit (-1); } EOF $CC_FOR_BUILD -o "$dummy" "$dummy.c" && dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` && SYSTEM_NAME=`"$dummy" "$dummyarg"` && { echo "$SYSTEM_NAME"; exit; } echo mips-mips-riscos"$UNAME_RELEASE" exit ;; Motorola:PowerMAX_OS:*:*) echo powerpc-motorola-powermax exit ;; Motorola:*:4.3:PL8-*) echo powerpc-harris-powermax exit ;; Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) echo powerpc-harris-powermax exit ;; Night_Hawk:Power_UNIX:*:*) echo powerpc-harris-powerunix exit ;; m88k:CX/UX:7*:*) echo m88k-harris-cxux7 exit ;; m88k:*:4*:R4*) echo m88k-motorola-sysv4 exit ;; m88k:*:3*:R3*) echo m88k-motorola-sysv3 exit ;; AViiON:dgux:*:*) # DG/UX returns AViiON for all architectures UNAME_PROCESSOR=`/usr/bin/uname -p` if [ "$UNAME_PROCESSOR" = mc88100 ] || [ "$UNAME_PROCESSOR" = mc88110 ] then if [ "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx ] || \ [ "$TARGET_BINARY_INTERFACE"x = x ] then echo m88k-dg-dgux"$UNAME_RELEASE" else echo m88k-dg-dguxbcs"$UNAME_RELEASE" fi else echo i586-dg-dgux"$UNAME_RELEASE" fi exit ;; M88*:DolphinOS:*:*) # DolphinOS (SVR3) echo m88k-dolphin-sysv3 exit ;; M88*:*:R3*:*) # Delta 88k system running SVR3 echo m88k-motorola-sysv3 exit ;; XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) echo m88k-tektronix-sysv3 exit ;; Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) echo m68k-tektronix-bsd exit ;; *:IRIX*:*:*) echo mips-sgi-irix"`echo "$UNAME_RELEASE"|sed -e 's/-/_/g'`" exit ;; ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' i*86:AIX:*:*) echo i386-ibm-aix exit ;; ia64:AIX:*:*) if [ -x /usr/bin/oslevel ] ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV="$UNAME_VERSION.$UNAME_RELEASE" fi echo "$UNAME_MACHINE"-ibm-aix"$IBM_REV" exit ;; *:AIX:2:3) if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then eval "$set_cc_for_build" sed 's/^ //' << EOF > "$dummy.c" #include main() { if (!__power_pc()) exit(1); puts("powerpc-ibm-aix3.2.5"); exit(0); } EOF if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` then echo "$SYSTEM_NAME" else echo rs6000-ibm-aix3.2.5 fi elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then echo rs6000-ibm-aix3.2.4 else echo rs6000-ibm-aix3.2 fi exit ;; *:AIX:*:[4567]) IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then IBM_ARCH=rs6000 else IBM_ARCH=powerpc fi if [ -x /usr/bin/lslpp ] ; then IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | awk -F: '{ print $3 }' | sed s/[0-9]*$/0/` else IBM_REV="$UNAME_VERSION.$UNAME_RELEASE" fi echo "$IBM_ARCH"-ibm-aix"$IBM_REV" exit ;; *:AIX:*:*) echo rs6000-ibm-aix exit ;; ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*) echo romp-ibm-bsd4.4 exit ;; ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and echo romp-ibm-bsd"$UNAME_RELEASE" # 4.3 with uname added to exit ;; # report: romp-ibm BSD 4.3 *:BOSX:*:*) echo rs6000-bull-bosx exit ;; DPX/2?00:B.O.S.:*:*) echo m68k-bull-sysv3 exit ;; 9000/[34]??:4.3bsd:1.*:*) echo m68k-hp-bsd exit ;; hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) echo m68k-hp-bsd4.4 exit ;; 9000/[34678]??:HP-UX:*:*) HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'` case "$UNAME_MACHINE" in 9000/31?) HP_ARCH=m68000 ;; 9000/[34]??) HP_ARCH=m68k ;; 9000/[678][0-9][0-9]) if [ -x /usr/bin/getconf ]; then sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` case "$sc_cpu_version" in 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1 532) # CPU_PA_RISC2_0 case "$sc_kernel_bits" in 32) HP_ARCH=hppa2.0n ;; 64) HP_ARCH=hppa2.0w ;; '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20 esac ;; esac fi if [ "$HP_ARCH" = "" ]; then eval "$set_cc_for_build" sed 's/^ //' << EOF > "$dummy.c" #define _HPUX_SOURCE #include #include int main () { #if defined(_SC_KERNEL_BITS) long bits = sysconf(_SC_KERNEL_BITS); #endif long cpu = sysconf (_SC_CPU_VERSION); switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0"); break; case CPU_PA_RISC1_1: puts ("hppa1.1"); break; case CPU_PA_RISC2_0: #if defined(_SC_KERNEL_BITS) switch (bits) { case 64: puts ("hppa2.0w"); break; case 32: puts ("hppa2.0n"); break; default: puts ("hppa2.0"); break; } break; #else /* !defined(_SC_KERNEL_BITS) */ puts ("hppa2.0"); break; #endif default: puts ("hppa1.0"); break; } exit (0); } EOF (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=`"$dummy"` test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac if [ "$HP_ARCH" = hppa2.0w ] then eval "$set_cc_for_build" # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler # generating 64-bit code. GNU and HP use different nomenclature: # # $ CC_FOR_BUILD=cc ./config.guess # => hppa2.0w-hp-hpux11.23 # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess # => hppa64-hp-hpux11.23 if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | grep -q __LP64__ then HP_ARCH=hppa2.0w else HP_ARCH=hppa64 fi fi echo "$HP_ARCH"-hp-hpux"$HPUX_REV" exit ;; ia64:HP-UX:*:*) HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'` echo ia64-hp-hpux"$HPUX_REV" exit ;; 3050*:HI-UX:*:*) eval "$set_cc_for_build" sed 's/^ //' << EOF > "$dummy.c" #include int main () { long cpu = sysconf (_SC_CPU_VERSION); /* The order matters, because CPU_IS_HP_MC68K erroneously returns true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct results, however. */ if (CPU_IS_PA_RISC (cpu)) { switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; default: puts ("hppa-hitachi-hiuxwe2"); break; } } else if (CPU_IS_HP_MC68K (cpu)) puts ("m68k-hitachi-hiuxwe2"); else puts ("unknown-hitachi-hiuxwe2"); exit (0); } EOF $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` && { echo "$SYSTEM_NAME"; exit; } echo unknown-hitachi-hiuxwe2 exit ;; 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*) echo hppa1.1-hp-bsd exit ;; 9000/8??:4.3bsd:*:*) echo hppa1.0-hp-bsd exit ;; *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) echo hppa1.0-hp-mpeix exit ;; hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*) echo hppa1.1-hp-osf exit ;; hp8??:OSF1:*:*) echo hppa1.0-hp-osf exit ;; i*86:OSF1:*:*) if [ -x /usr/sbin/sysversion ] ; then echo "$UNAME_MACHINE"-unknown-osf1mk else echo "$UNAME_MACHINE"-unknown-osf1 fi exit ;; parisc*:Lites*:*:*) echo hppa1.1-hp-lites exit ;; C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) echo c1-convex-bsd exit ;; C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit ;; C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) echo c34-convex-bsd exit ;; C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) echo c38-convex-bsd exit ;; C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) echo c4-convex-bsd exit ;; CRAY*Y-MP:*:*:*) echo ymp-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*[A-Z]90:*:*:*) echo "$UNAME_MACHINE"-cray-unicos"$UNAME_RELEASE" \ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ -e 's/\.[^.]*$/.X/' exit ;; CRAY*TS:*:*:*) echo t90-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*T3E:*:*:*) echo alphaev5-cray-unicosmk"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*SV1:*:*:*) echo sv1-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; *:UNICOS/mp:*:*) echo craynv-cray-unicosmp"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` FUJITSU_REL=`echo "$UNAME_RELEASE" | sed -e 's/ /_/'` echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; 5000:UNIX_System_V:4.*:*) FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` FUJITSU_REL=`echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'` echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) echo "$UNAME_MACHINE"-pc-bsdi"$UNAME_RELEASE" exit ;; sparc*:BSD/OS:*:*) echo sparc-unknown-bsdi"$UNAME_RELEASE" exit ;; *:BSD/OS:*:*) echo "$UNAME_MACHINE"-unknown-bsdi"$UNAME_RELEASE" exit ;; *:FreeBSD:*:*) UNAME_PROCESSOR=`/usr/bin/uname -p` case "$UNAME_PROCESSOR" in amd64) UNAME_PROCESSOR=x86_64 ;; i386) UNAME_PROCESSOR=i586 ;; esac echo "$UNAME_PROCESSOR"-unknown-freebsd"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`" exit ;; i*:CYGWIN*:*) echo "$UNAME_MACHINE"-pc-cygwin exit ;; *:MINGW64*:*) echo "$UNAME_MACHINE"-pc-mingw64 exit ;; *:MINGW*:*) echo "$UNAME_MACHINE"-pc-mingw32 exit ;; *:MSYS*:*) echo "$UNAME_MACHINE"-pc-msys exit ;; i*:PW*:*) echo "$UNAME_MACHINE"-pc-pw32 exit ;; *:Interix*:*) case "$UNAME_MACHINE" in x86) echo i586-pc-interix"$UNAME_RELEASE" exit ;; authenticamd | genuineintel | EM64T) echo x86_64-unknown-interix"$UNAME_RELEASE" exit ;; IA64) echo ia64-unknown-interix"$UNAME_RELEASE" exit ;; esac ;; i*:UWIN*:*) echo "$UNAME_MACHINE"-pc-uwin exit ;; amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) echo x86_64-unknown-cygwin exit ;; prep*:SunOS:5.*:*) echo powerpcle-unknown-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" exit ;; *:GNU:*:*) # the GNU system echo "`echo "$UNAME_MACHINE"|sed -e 's,[-/].*$,,'`-unknown-$LIBC`echo "$UNAME_RELEASE"|sed -e 's,/.*$,,'`" exit ;; *:GNU/*:*:*) # other systems with GNU libc and userland echo "$UNAME_MACHINE-unknown-`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"``echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`-$LIBC" exit ;; i*86:Minix:*:*) echo "$UNAME_MACHINE"-pc-minix exit ;; aarch64:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; aarch64_be:Linux:*:*) UNAME_MACHINE=aarch64_be echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; alpha:Linux:*:*) case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in EV5) UNAME_MACHINE=alphaev5 ;; EV56) UNAME_MACHINE=alphaev56 ;; PCA56) UNAME_MACHINE=alphapca56 ;; PCA57) UNAME_MACHINE=alphapca56 ;; EV6) UNAME_MACHINE=alphaev6 ;; EV67) UNAME_MACHINE=alphaev67 ;; EV68*) UNAME_MACHINE=alphaev68 ;; esac objdump --private-headers /bin/sh | grep -q ld.so.1 if test "$?" = 0 ; then LIBC=gnulibc1 ; fi echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; arc:Linux:*:* | arceb:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; arm*:Linux:*:*) eval "$set_cc_for_build" if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_EABI__ then echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" else if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabi else echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabihf fi fi exit ;; avr32*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; cris:Linux:*:*) echo "$UNAME_MACHINE"-axis-linux-"$LIBC" exit ;; crisv32:Linux:*:*) echo "$UNAME_MACHINE"-axis-linux-"$LIBC" exit ;; e2k:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; frv:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; hexagon:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; i*86:Linux:*:*) echo "$UNAME_MACHINE"-pc-linux-"$LIBC" exit ;; ia64:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; k1om:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; m32r*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; m68*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; mips:Linux:*:* | mips64:Linux:*:*) eval "$set_cc_for_build" sed 's/^ //' << EOF > "$dummy.c" #undef CPU #undef ${UNAME_MACHINE} #undef ${UNAME_MACHINE}el #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) CPU=${UNAME_MACHINE}el #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) CPU=${UNAME_MACHINE} #else CPU= #endif #endif EOF eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU'`" test "x$CPU" != x && { echo "$CPU-unknown-linux-$LIBC"; exit; } ;; mips64el:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; openrisc*:Linux:*:*) echo or1k-unknown-linux-"$LIBC" exit ;; or32:Linux:*:* | or1k*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; padre:Linux:*:*) echo sparc-unknown-linux-"$LIBC" exit ;; parisc64:Linux:*:* | hppa64:Linux:*:*) echo hppa64-unknown-linux-"$LIBC" exit ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in PA7*) echo hppa1.1-unknown-linux-"$LIBC" ;; PA8*) echo hppa2.0-unknown-linux-"$LIBC" ;; *) echo hppa-unknown-linux-"$LIBC" ;; esac exit ;; ppc64:Linux:*:*) echo powerpc64-unknown-linux-"$LIBC" exit ;; ppc:Linux:*:*) echo powerpc-unknown-linux-"$LIBC" exit ;; ppc64le:Linux:*:*) echo powerpc64le-unknown-linux-"$LIBC" exit ;; ppcle:Linux:*:*) echo powerpcle-unknown-linux-"$LIBC" exit ;; riscv32:Linux:*:* | riscv64:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; s390:Linux:*:* | s390x:Linux:*:*) echo "$UNAME_MACHINE"-ibm-linux-"$LIBC" exit ;; sh64*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; sh*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; sparc:Linux:*:* | sparc64:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; tile*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; vax:Linux:*:*) echo "$UNAME_MACHINE"-dec-linux-"$LIBC" exit ;; x86_64:Linux:*:*) if objdump -f /bin/sh | grep -q elf32-x86-64; then echo "$UNAME_MACHINE"-pc-linux-"$LIBC"x32 else echo "$UNAME_MACHINE"-pc-linux-"$LIBC" fi exit ;; xtensa*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. # earlier versions are messed up and put the nodename in both # sysname and nodename. echo i386-sequent-sysv4 exit ;; i*86:UNIX_SV:4.2MP:2.*) # Unixware is an offshoot of SVR4, but it has its own version # number series starting with 2... # I am not positive that other SVR4 systems won't match this, # I just have to hope. -- rms. # Use sysv4.2uw... so that sysv4* matches it. echo "$UNAME_MACHINE"-pc-sysv4.2uw"$UNAME_VERSION" exit ;; i*86:OS/2:*:*) # If we were able to find `uname', then EMX Unix compatibility # is probably installed. echo "$UNAME_MACHINE"-pc-os2-emx exit ;; i*86:XTS-300:*:STOP) echo "$UNAME_MACHINE"-unknown-stop exit ;; i*86:atheos:*:*) echo "$UNAME_MACHINE"-unknown-atheos exit ;; i*86:syllable:*:*) echo "$UNAME_MACHINE"-pc-syllable exit ;; i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) echo i386-unknown-lynxos"$UNAME_RELEASE" exit ;; i*86:*DOS:*:*) echo "$UNAME_MACHINE"-pc-msdosdjgpp exit ;; i*86:*:4.*:*) UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'` if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then echo "$UNAME_MACHINE"-univel-sysv"$UNAME_REL" else echo "$UNAME_MACHINE"-pc-sysv"$UNAME_REL" fi exit ;; i*86:*:5:[678]*) # UnixWare 7.x, OpenUNIX and OpenServer 6. case `/bin/uname -X | grep "^Machine"` in *486*) UNAME_MACHINE=i486 ;; *Pentium) UNAME_MACHINE=i586 ;; *Pent*|*Celeron) UNAME_MACHINE=i686 ;; esac echo "$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}{$UNAME_VERSION}" exit ;; i*86:*:3.2:*) if test -f /usr/options/cb.name; then UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ && UNAME_MACHINE=i586 (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ && UNAME_MACHINE=i686 (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ && UNAME_MACHINE=i686 echo "$UNAME_MACHINE"-pc-sco"$UNAME_REL" else echo "$UNAME_MACHINE"-pc-sysv32 fi exit ;; pc:*:*:*) # Left here for compatibility: # uname -m prints for DJGPP always 'pc', but it prints nothing about # the processor, so we play safe by assuming i586. # Note: whatever this is, it MUST be the same as what config.sub # prints for the "djgpp" host, or else GDB configure will decide that # this is a cross-build. echo i586-pc-msdosdjgpp exit ;; Intel:Mach:3*:*) echo i386-pc-mach3 exit ;; paragon:*:*:*) echo i860-intel-osf1 exit ;; i860:*:4.*:*) # i860-SVR4 if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then echo i860-stardent-sysv"$UNAME_RELEASE" # Stardent Vistra i860-SVR4 else # Add other i860-SVR4 vendors below as they are discovered. echo i860-unknown-sysv"$UNAME_RELEASE" # Unknown i860-SVR4 fi exit ;; mini*:CTIX:SYS*5:*) # "miniframe" echo m68010-convergent-sysv exit ;; mc68k:UNIX:SYSTEM5:3.51m) echo m68k-convergent-sysv exit ;; M680?0:D-NIX:5.3:*) echo m68k-diab-dnix exit ;; M68*:*:R3V[5678]*:*) test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) OS_REL='' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4; exit; } ;; NCR*:*:4.2:* | MPRAS*:*:4.2:*) OS_REL='.3' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) echo m68k-unknown-lynxos"$UNAME_RELEASE" exit ;; mc68030:UNIX_System_V:4.*:*) echo m68k-atari-sysv4 exit ;; TSUNAMI:LynxOS:2.*:*) echo sparc-unknown-lynxos"$UNAME_RELEASE" exit ;; rs6000:LynxOS:2.*:*) echo rs6000-unknown-lynxos"$UNAME_RELEASE" exit ;; PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) echo powerpc-unknown-lynxos"$UNAME_RELEASE" exit ;; SM[BE]S:UNIX_SV:*:*) echo mips-dde-sysv"$UNAME_RELEASE" exit ;; RM*:ReliantUNIX-*:*:*) echo mips-sni-sysv4 exit ;; RM*:SINIX-*:*:*) echo mips-sni-sysv4 exit ;; *:SINIX-*:*:*) if uname -p 2>/dev/null >/dev/null ; then UNAME_MACHINE=`(uname -p) 2>/dev/null` echo "$UNAME_MACHINE"-sni-sysv4 else echo ns32k-sni-sysv fi exit ;; PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort # says echo i586-unisys-sysv4 exit ;; *:UNIX_System_V:4*:FTX*) # From Gerald Hewes . # How about differentiating between stratus architectures? -djm echo hppa1.1-stratus-sysv4 exit ;; *:*:*:FTX*) # From seanf@swdc.stratus.com. echo i860-stratus-sysv4 exit ;; i*86:VOS:*:*) # From Paul.Green@stratus.com. echo "$UNAME_MACHINE"-stratus-vos exit ;; *:VOS:*:*) # From Paul.Green@stratus.com. echo hppa1.1-stratus-vos exit ;; mc68*:A/UX:*:*) echo m68k-apple-aux"$UNAME_RELEASE" exit ;; news*:NEWS-OS:6*:*) echo mips-sony-newsos6 exit ;; R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) if [ -d /usr/nec ]; then echo mips-nec-sysv"$UNAME_RELEASE" else echo mips-unknown-sysv"$UNAME_RELEASE" fi exit ;; BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. echo powerpc-be-beos exit ;; BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. echo powerpc-apple-beos exit ;; BePC:BeOS:*:*) # BeOS running on Intel PC compatible. echo i586-pc-beos exit ;; BePC:Haiku:*:*) # Haiku running on Intel PC compatible. echo i586-pc-haiku exit ;; x86_64:Haiku:*:*) echo x86_64-unknown-haiku exit ;; SX-4:SUPER-UX:*:*) echo sx4-nec-superux"$UNAME_RELEASE" exit ;; SX-5:SUPER-UX:*:*) echo sx5-nec-superux"$UNAME_RELEASE" exit ;; SX-6:SUPER-UX:*:*) echo sx6-nec-superux"$UNAME_RELEASE" exit ;; SX-7:SUPER-UX:*:*) echo sx7-nec-superux"$UNAME_RELEASE" exit ;; SX-8:SUPER-UX:*:*) echo sx8-nec-superux"$UNAME_RELEASE" exit ;; SX-8R:SUPER-UX:*:*) echo sx8r-nec-superux"$UNAME_RELEASE" exit ;; SX-ACE:SUPER-UX:*:*) echo sxace-nec-superux"$UNAME_RELEASE" exit ;; Power*:Rhapsody:*:*) echo powerpc-apple-rhapsody"$UNAME_RELEASE" exit ;; *:Rhapsody:*:*) echo "$UNAME_MACHINE"-apple-rhapsody"$UNAME_RELEASE" exit ;; *:Darwin:*:*) UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown eval "$set_cc_for_build" if test "$UNAME_PROCESSOR" = unknown ; then UNAME_PROCESSOR=powerpc fi if test "`echo "$UNAME_RELEASE" | sed -e 's/\..*//'`" -le 10 ; then if [ "$CC_FOR_BUILD" != no_compiler_found ]; then if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then case $UNAME_PROCESSOR in i386) UNAME_PROCESSOR=x86_64 ;; powerpc) UNAME_PROCESSOR=powerpc64 ;; esac fi # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_PPC >/dev/null then UNAME_PROCESSOR=powerpc fi fi elif test "$UNAME_PROCESSOR" = i386 ; then # Avoid executing cc on OS X 10.9, as it ships with a stub # that puts up a graphical alert prompting to install # developer tools. Any system running Mac OS X 10.7 or # later (Darwin 11 and later) is required to have a 64-bit # processor. This is not true of the ARM version of Darwin # that Apple uses in portable devices. UNAME_PROCESSOR=x86_64 fi echo "$UNAME_PROCESSOR"-apple-darwin"$UNAME_RELEASE" exit ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) UNAME_PROCESSOR=`uname -p` if test "$UNAME_PROCESSOR" = x86; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi echo "$UNAME_PROCESSOR"-"$UNAME_MACHINE"-nto-qnx"$UNAME_RELEASE" exit ;; *:QNX:*:4*) echo i386-pc-qnx exit ;; NEO-*:NONSTOP_KERNEL:*:*) echo neo-tandem-nsk"$UNAME_RELEASE" exit ;; NSE-*:NONSTOP_KERNEL:*:*) echo nse-tandem-nsk"$UNAME_RELEASE" exit ;; NSR-*:NONSTOP_KERNEL:*:*) echo nsr-tandem-nsk"$UNAME_RELEASE" exit ;; NSV-*:NONSTOP_KERNEL:*:*) echo nsv-tandem-nsk"$UNAME_RELEASE" exit ;; NSX-*:NONSTOP_KERNEL:*:*) echo nsx-tandem-nsk"$UNAME_RELEASE" exit ;; *:NonStop-UX:*:*) echo mips-compaq-nonstopux exit ;; BS2000:POSIX*:*:*) echo bs2000-siemens-sysv exit ;; DS/*:UNIX_System_V:*:*) echo "$UNAME_MACHINE"-"$UNAME_SYSTEM"-"$UNAME_RELEASE" exit ;; *:Plan9:*:*) # "uname -m" is not consistent, so use $cputype instead. 386 # is converted to i386 for consistency with other x86 # operating systems. if test "$cputype" = 386; then UNAME_MACHINE=i386 else UNAME_MACHINE="$cputype" fi echo "$UNAME_MACHINE"-unknown-plan9 exit ;; *:TOPS-10:*:*) echo pdp10-unknown-tops10 exit ;; *:TENEX:*:*) echo pdp10-unknown-tenex exit ;; KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) echo pdp10-dec-tops20 exit ;; XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) echo pdp10-xkl-tops20 exit ;; *:TOPS-20:*:*) echo pdp10-unknown-tops20 exit ;; *:ITS:*:*) echo pdp10-unknown-its exit ;; SEI:*:*:SEIUX) echo mips-sei-seiux"$UNAME_RELEASE" exit ;; *:DragonFly:*:*) echo "$UNAME_MACHINE"-unknown-dragonfly"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`" exit ;; *:*VMS:*:*) UNAME_MACHINE=`(uname -p) 2>/dev/null` case "$UNAME_MACHINE" in A*) echo alpha-dec-vms ; exit ;; I*) echo ia64-dec-vms ; exit ;; V*) echo vax-dec-vms ; exit ;; esac ;; *:XENIX:*:SysV) echo i386-pc-xenix exit ;; i*86:skyos:*:*) echo "$UNAME_MACHINE"-pc-skyos"`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'`" exit ;; i*86:rdos:*:*) echo "$UNAME_MACHINE"-pc-rdos exit ;; i*86:AROS:*:*) echo "$UNAME_MACHINE"-pc-aros exit ;; x86_64:VMkernel:*:*) echo "$UNAME_MACHINE"-unknown-esx exit ;; amd64:Isilon\ OneFS:*:*) echo x86_64-unknown-onefs exit ;; esac echo "$0: unable to guess system type" >&2 case "$UNAME_MACHINE:$UNAME_SYSTEM" in mips:Linux | mips64:Linux) # If we got here on MIPS GNU/Linux, output extra information. cat >&2 <&2 </dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` /bin/uname -X = `(/bin/uname -X) 2>/dev/null` hostinfo = `(hostinfo) 2>/dev/null` /bin/universe = `(/bin/universe) 2>/dev/null` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` /bin/arch = `(/bin/arch) 2>/dev/null` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` UNAME_MACHINE = "$UNAME_MACHINE" UNAME_RELEASE = "$UNAME_RELEASE" UNAME_SYSTEM = "$UNAME_SYSTEM" UNAME_VERSION = "$UNAME_VERSION" EOF exit 1 # Local variables: # eval: (add-hook 'write-file-functions 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: libopenmpt-0.6.1+release.autotools/build-aux/config.sub0000755000175000017500000010645014175541545020152 00000000000000#! /bin/sh # Configuration validation subroutine script. # Copyright 1992-2018 Free Software Foundation, Inc. timestamp='2018-02-22' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # Please send patches to . # # Configuration subroutine to validate and canonicalize a configuration type. # Supply the specified configuration type as an argument. # If it is invalid, we print an error message on stderr and exit with code 1. # Otherwise, we print the canonical config type on stdout and succeed. # You can get the latest version of this script from: # https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub # This file is supposed to be the same for all GNU packages # and recognize all the CPU types, system types and aliases # that are meaningful with *any* GNU software. # Each package is responsible for reporting which valid configurations # it does not support. The user should be able to distinguish # a failure to support a valid configuration from a meaningless # configuration. # The goal of this file is to map all the various variations of a given # machine specification into a single specification in the form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM # or in some cases, the newer four-part form: # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM # It is wrong to echo any other type of specification. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS Canonicalize a configuration name. Options: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.sub ($timestamp) Copyright 1992-2018 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" exit 1 ;; *local*) # First pass through any local machine types. echo "$1" exit ;; * ) break ;; esac done case $# in 0) echo "$me: missing argument$help" >&2 exit 1;; 1) ;; *) echo "$me: too many arguments$help" >&2 exit 1;; esac # Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). # Here we must recognize all the valid KERNEL-OS combinations. maybe_os=`echo "$1" | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` case $maybe_os in nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ knetbsd*-gnu* | netbsd*-gnu* | netbsd*-eabi* | \ kopensolaris*-gnu* | cloudabi*-eabi* | \ storm-chaos* | os2-emx* | rtmk-nova*) os=-$maybe_os basic_machine=`echo "$1" | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` ;; android-linux) os=-linux-android basic_machine=`echo "$1" | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown ;; *) basic_machine=`echo "$1" | sed 's/-[^-]*$//'` if [ "$basic_machine" != "$1" ] then os=`echo "$1" | sed 's/.*-/-/'` else os=; fi ;; esac ### Let's recognize common machines as not being operating systems so ### that things like config.sub decstation-3100 work. We also ### recognize some manufacturers as not being operating systems, so we ### can provide default operating systems below. case $os in -sun*os*) # Prevent following clause from handling this invalid input. ;; -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ -apple | -axis | -knuth | -cray | -microblaze*) os= basic_machine=$1 ;; -bluegene*) os=-cnk ;; -sim | -cisco | -oki | -wec | -winbond) os= basic_machine=$1 ;; -scout) ;; -wrs) os=-vxworks basic_machine=$1 ;; -chorusos*) os=-chorusos basic_machine=$1 ;; -chorusrdb) os=-chorusrdb basic_machine=$1 ;; -hiux*) os=-hiuxwe2 ;; -sco6) os=-sco5v6 basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -sco5) os=-sco3.2v5 basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -sco4) os=-sco3.2v4 basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -sco3.2.[4-9]*) os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -sco3.2v[4-9]*) # Don't forget version if it is 3.2v4 or newer. basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -sco5v6*) # Don't forget version if it is 3.2v4 or newer. basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -sco*) os=-sco3.2v2 basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -udk*) basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -isc) os=-isc2.2 basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -clix*) basic_machine=clipper-intergraph ;; -isc*) basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -lynx*178) os=-lynxos178 ;; -lynx*5) os=-lynxos5 ;; -lynx*) os=-lynxos ;; -ptx*) basic_machine=`echo "$1" | sed -e 's/86-.*/86-sequent/'` ;; -psos*) os=-psos ;; -mint | -mint[0-9]*) basic_machine=m68k-atari os=-mint ;; esac # Decode aliases for certain CPU-COMPANY combinations. case $basic_machine in # Recognize the basic CPU types without company name. # Some are omitted here because they have special meanings below. 1750a | 580 \ | a29k \ | aarch64 | aarch64_be \ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ | am33_2.0 \ | arc | arceb \ | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \ | avr | avr32 \ | ba \ | be32 | be64 \ | bfin \ | c4x | c8051 | clipper \ | d10v | d30v | dlx | dsp16xx \ | e2k | epiphany \ | fido | fr30 | frv | ft32 \ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ | hexagon \ | i370 | i860 | i960 | ia16 | ia64 \ | ip2k | iq2000 \ | k1om \ | le32 | le64 \ | lm32 \ | m32c | m32r | m32rle | m68000 | m68k | m88k \ | maxq | mb | microblaze | microblazeel | mcore | mep | metag \ | mips | mipsbe | mipseb | mipsel | mipsle \ | mips16 \ | mips64 | mips64el \ | mips64octeon | mips64octeonel \ | mips64orion | mips64orionel \ | mips64r5900 | mips64r5900el \ | mips64vr | mips64vrel \ | mips64vr4100 | mips64vr4100el \ | mips64vr4300 | mips64vr4300el \ | mips64vr5000 | mips64vr5000el \ | mips64vr5900 | mips64vr5900el \ | mipsisa32 | mipsisa32el \ | mipsisa32r2 | mipsisa32r2el \ | mipsisa32r6 | mipsisa32r6el \ | mipsisa64 | mipsisa64el \ | mipsisa64r2 | mipsisa64r2el \ | mipsisa64r6 | mipsisa64r6el \ | mipsisa64sb1 | mipsisa64sb1el \ | mipsisa64sr71k | mipsisa64sr71kel \ | mipsr5900 | mipsr5900el \ | mipstx39 | mipstx39el \ | mn10200 | mn10300 \ | moxie \ | mt \ | msp430 \ | nds32 | nds32le | nds32be \ | nios | nios2 | nios2eb | nios2el \ | ns16k | ns32k \ | open8 | or1k | or1knd | or32 \ | pdp10 | pj | pjl \ | powerpc | powerpc64 | powerpc64le | powerpcle \ | pru \ | pyramid \ | riscv32 | riscv64 \ | rl78 | rx \ | score \ | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[234]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ | sh64 | sh64le \ | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ | spu \ | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ | ubicom32 \ | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ | visium \ | wasm32 \ | x86 | xc16x | xstormy16 | xtensa \ | z8k | z80) basic_machine=$basic_machine-unknown ;; c54x) basic_machine=tic54x-unknown ;; c55x) basic_machine=tic55x-unknown ;; c6x) basic_machine=tic6x-unknown ;; leon|leon[3-9]) basic_machine=sparc-$basic_machine ;; m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip) basic_machine=$basic_machine-unknown os=-none ;; m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65) ;; ms1) basic_machine=mt-unknown ;; strongarm | thumb | xscale) basic_machine=arm-unknown ;; xgate) basic_machine=$basic_machine-unknown os=-none ;; xscaleeb) basic_machine=armeb-unknown ;; xscaleel) basic_machine=armel-unknown ;; # We use `pc' rather than `unknown' # because (1) that's what they normally are, and # (2) the word "unknown" tends to confuse beginning users. i*86 | x86_64) basic_machine=$basic_machine-pc ;; # Object if more than one company name word. *-*-*) echo Invalid configuration \`"$1"\': machine \`"$basic_machine"\' not recognized 1>&2 exit 1 ;; # Recognize the basic CPU types with company name. 580-* \ | a29k-* \ | aarch64-* | aarch64_be-* \ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \ | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ | avr-* | avr32-* \ | ba-* \ | be32-* | be64-* \ | bfin-* | bs2000-* \ | c[123]* | c30-* | [cjt]90-* | c4x-* \ | c8051-* | clipper-* | craynv-* | cydra-* \ | d10v-* | d30v-* | dlx-* \ | e2k-* | elxsi-* \ | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ | h8300-* | h8500-* \ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ | hexagon-* \ | i*86-* | i860-* | i960-* | ia16-* | ia64-* \ | ip2k-* | iq2000-* \ | k1om-* \ | le32-* | le64-* \ | lm32-* \ | m32c-* | m32r-* | m32rle-* \ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \ | microblaze-* | microblazeel-* \ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ | mips16-* \ | mips64-* | mips64el-* \ | mips64octeon-* | mips64octeonel-* \ | mips64orion-* | mips64orionel-* \ | mips64r5900-* | mips64r5900el-* \ | mips64vr-* | mips64vrel-* \ | mips64vr4100-* | mips64vr4100el-* \ | mips64vr4300-* | mips64vr4300el-* \ | mips64vr5000-* | mips64vr5000el-* \ | mips64vr5900-* | mips64vr5900el-* \ | mipsisa32-* | mipsisa32el-* \ | mipsisa32r2-* | mipsisa32r2el-* \ | mipsisa32r6-* | mipsisa32r6el-* \ | mipsisa64-* | mipsisa64el-* \ | mipsisa64r2-* | mipsisa64r2el-* \ | mipsisa64r6-* | mipsisa64r6el-* \ | mipsisa64sb1-* | mipsisa64sb1el-* \ | mipsisa64sr71k-* | mipsisa64sr71kel-* \ | mipsr5900-* | mipsr5900el-* \ | mipstx39-* | mipstx39el-* \ | mmix-* \ | mt-* \ | msp430-* \ | nds32-* | nds32le-* | nds32be-* \ | nios-* | nios2-* | nios2eb-* | nios2el-* \ | none-* | np1-* | ns16k-* | ns32k-* \ | open8-* \ | or1k*-* \ | orion-* \ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ | pru-* \ | pyramid-* \ | riscv32-* | riscv64-* \ | rl78-* | romp-* | rs6000-* | rx-* \ | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ | sparclite-* \ | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx*-* \ | tahoe-* \ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ | tile*-* \ | tron-* \ | ubicom32-* \ | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ | vax-* \ | visium-* \ | wasm32-* \ | we32k-* \ | x86-* | x86_64-* | xc16x-* | xps100-* \ | xstormy16-* | xtensa*-* \ | ymp-* \ | z8k-* | z80-*) ;; # Recognize the basic CPU types without company name, with glob match. xtensa*) basic_machine=$basic_machine-unknown ;; # Recognize the various machine names and aliases which stand # for a CPU type and a company and sometimes even an OS. 386bsd) basic_machine=i386-pc os=-bsd ;; 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) basic_machine=m68000-att ;; 3b*) basic_machine=we32k-att ;; a29khif) basic_machine=a29k-amd os=-udi ;; abacus) basic_machine=abacus-unknown ;; adobe68k) basic_machine=m68010-adobe os=-scout ;; alliant | fx80) basic_machine=fx80-alliant ;; altos | altos3068) basic_machine=m68k-altos ;; am29k) basic_machine=a29k-none os=-bsd ;; amd64) basic_machine=x86_64-pc ;; amd64-*) basic_machine=x86_64-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; amdahl) basic_machine=580-amdahl os=-sysv ;; amiga | amiga-*) basic_machine=m68k-unknown ;; amigaos | amigados) basic_machine=m68k-unknown os=-amigaos ;; amigaunix | amix) basic_machine=m68k-unknown os=-sysv4 ;; apollo68) basic_machine=m68k-apollo os=-sysv ;; apollo68bsd) basic_machine=m68k-apollo os=-bsd ;; aros) basic_machine=i386-pc os=-aros ;; asmjs) basic_machine=asmjs-unknown ;; aux) basic_machine=m68k-apple os=-aux ;; balance) basic_machine=ns32k-sequent os=-dynix ;; blackfin) basic_machine=bfin-unknown os=-linux ;; blackfin-*) basic_machine=bfin-`echo "$basic_machine" | sed 's/^[^-]*-//'` os=-linux ;; bluegene*) basic_machine=powerpc-ibm os=-cnk ;; c54x-*) basic_machine=tic54x-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; c55x-*) basic_machine=tic55x-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; c6x-*) basic_machine=tic6x-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; c90) basic_machine=c90-cray os=-unicos ;; cegcc) basic_machine=arm-unknown os=-cegcc ;; convex-c1) basic_machine=c1-convex os=-bsd ;; convex-c2) basic_machine=c2-convex os=-bsd ;; convex-c32) basic_machine=c32-convex os=-bsd ;; convex-c34) basic_machine=c34-convex os=-bsd ;; convex-c38) basic_machine=c38-convex os=-bsd ;; cray | j90) basic_machine=j90-cray os=-unicos ;; craynv) basic_machine=craynv-cray os=-unicosmp ;; cr16 | cr16-*) basic_machine=cr16-unknown os=-elf ;; crds | unos) basic_machine=m68k-crds ;; crisv32 | crisv32-* | etraxfs*) basic_machine=crisv32-axis ;; cris | cris-* | etrax*) basic_machine=cris-axis ;; crx) basic_machine=crx-unknown os=-elf ;; da30 | da30-*) basic_machine=m68k-da30 ;; decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) basic_machine=mips-dec ;; decsystem10* | dec10*) basic_machine=pdp10-dec os=-tops10 ;; decsystem20* | dec20*) basic_machine=pdp10-dec os=-tops20 ;; delta | 3300 | motorola-3300 | motorola-delta \ | 3300-motorola | delta-motorola) basic_machine=m68k-motorola ;; delta88) basic_machine=m88k-motorola os=-sysv3 ;; dicos) basic_machine=i686-pc os=-dicos ;; djgpp) basic_machine=i586-pc os=-msdosdjgpp ;; dpx20 | dpx20-*) basic_machine=rs6000-bull os=-bosx ;; dpx2*) basic_machine=m68k-bull os=-sysv3 ;; e500v[12]) basic_machine=powerpc-unknown os=$os"spe" ;; e500v[12]-*) basic_machine=powerpc-`echo "$basic_machine" | sed 's/^[^-]*-//'` os=$os"spe" ;; ebmon29k) basic_machine=a29k-amd os=-ebmon ;; elxsi) basic_machine=elxsi-elxsi os=-bsd ;; encore | umax | mmax) basic_machine=ns32k-encore ;; es1800 | OSE68k | ose68k | ose | OSE) basic_machine=m68k-ericsson os=-ose ;; fx2800) basic_machine=i860-alliant ;; genix) basic_machine=ns32k-ns ;; gmicro) basic_machine=tron-gmicro os=-sysv ;; go32) basic_machine=i386-pc os=-go32 ;; h3050r* | hiux*) basic_machine=hppa1.1-hitachi os=-hiuxwe2 ;; h8300hms) basic_machine=h8300-hitachi os=-hms ;; h8300xray) basic_machine=h8300-hitachi os=-xray ;; h8500hms) basic_machine=h8500-hitachi os=-hms ;; harris) basic_machine=m88k-harris os=-sysv3 ;; hp300-*) basic_machine=m68k-hp ;; hp300bsd) basic_machine=m68k-hp os=-bsd ;; hp300hpux) basic_machine=m68k-hp os=-hpux ;; hp3k9[0-9][0-9] | hp9[0-9][0-9]) basic_machine=hppa1.0-hp ;; hp9k2[0-9][0-9] | hp9k31[0-9]) basic_machine=m68000-hp ;; hp9k3[2-9][0-9]) basic_machine=m68k-hp ;; hp9k6[0-9][0-9] | hp6[0-9][0-9]) basic_machine=hppa1.0-hp ;; hp9k7[0-79][0-9] | hp7[0-79][0-9]) basic_machine=hppa1.1-hp ;; hp9k78[0-9] | hp78[0-9]) # FIXME: really hppa2.0-hp basic_machine=hppa1.1-hp ;; hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) # FIXME: really hppa2.0-hp basic_machine=hppa1.1-hp ;; hp9k8[0-9][13679] | hp8[0-9][13679]) basic_machine=hppa1.1-hp ;; hp9k8[0-9][0-9] | hp8[0-9][0-9]) basic_machine=hppa1.0-hp ;; hppaosf) basic_machine=hppa1.1-hp os=-osf ;; hppro) basic_machine=hppa1.1-hp os=-proelf ;; i370-ibm* | ibm*) basic_machine=i370-ibm ;; i*86v32) basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'` os=-sysv32 ;; i*86v4*) basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'` os=-sysv4 ;; i*86v) basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'` os=-sysv ;; i*86sol2) basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'` os=-solaris2 ;; i386mach) basic_machine=i386-mach os=-mach ;; vsta) basic_machine=i386-unknown os=-vsta ;; iris | iris4d) basic_machine=mips-sgi case $os in -irix*) ;; *) os=-irix4 ;; esac ;; isi68 | isi) basic_machine=m68k-isi os=-sysv ;; leon-*|leon[3-9]-*) basic_machine=sparc-`echo "$basic_machine" | sed 's/-.*//'` ;; m68knommu) basic_machine=m68k-unknown os=-linux ;; m68knommu-*) basic_machine=m68k-`echo "$basic_machine" | sed 's/^[^-]*-//'` os=-linux ;; magnum | m3230) basic_machine=mips-mips os=-sysv ;; merlin) basic_machine=ns32k-utek os=-sysv ;; microblaze*) basic_machine=microblaze-xilinx ;; mingw64) basic_machine=x86_64-pc os=-mingw64 ;; mingw32) basic_machine=i686-pc os=-mingw32 ;; mingw32ce) basic_machine=arm-unknown os=-mingw32ce ;; miniframe) basic_machine=m68000-convergent ;; *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) basic_machine=m68k-atari os=-mint ;; mips3*-*) basic_machine=`echo "$basic_machine" | sed -e 's/mips3/mips64/'` ;; mips3*) basic_machine=`echo "$basic_machine" | sed -e 's/mips3/mips64/'`-unknown ;; monitor) basic_machine=m68k-rom68k os=-coff ;; morphos) basic_machine=powerpc-unknown os=-morphos ;; moxiebox) basic_machine=moxie-unknown os=-moxiebox ;; msdos) basic_machine=i386-pc os=-msdos ;; ms1-*) basic_machine=`echo "$basic_machine" | sed -e 's/ms1-/mt-/'` ;; msys) basic_machine=i686-pc os=-msys ;; mvs) basic_machine=i370-ibm os=-mvs ;; nacl) basic_machine=le32-unknown os=-nacl ;; ncr3000) basic_machine=i486-ncr os=-sysv4 ;; netbsd386) basic_machine=i386-unknown os=-netbsd ;; netwinder) basic_machine=armv4l-rebel os=-linux ;; news | news700 | news800 | news900) basic_machine=m68k-sony os=-newsos ;; news1000) basic_machine=m68030-sony os=-newsos ;; news-3600 | risc-news) basic_machine=mips-sony os=-newsos ;; necv70) basic_machine=v70-nec os=-sysv ;; next | m*-next) basic_machine=m68k-next case $os in -nextstep* ) ;; -ns2*) os=-nextstep2 ;; *) os=-nextstep3 ;; esac ;; nh3000) basic_machine=m68k-harris os=-cxux ;; nh[45]000) basic_machine=m88k-harris os=-cxux ;; nindy960) basic_machine=i960-intel os=-nindy ;; mon960) basic_machine=i960-intel os=-mon960 ;; nonstopux) basic_machine=mips-compaq os=-nonstopux ;; np1) basic_machine=np1-gould ;; neo-tandem) basic_machine=neo-tandem ;; nse-tandem) basic_machine=nse-tandem ;; nsr-tandem) basic_machine=nsr-tandem ;; nsv-tandem) basic_machine=nsv-tandem ;; nsx-tandem) basic_machine=nsx-tandem ;; op50n-* | op60c-*) basic_machine=hppa1.1-oki os=-proelf ;; openrisc | openrisc-*) basic_machine=or32-unknown ;; os400) basic_machine=powerpc-ibm os=-os400 ;; OSE68000 | ose68000) basic_machine=m68000-ericsson os=-ose ;; os68k) basic_machine=m68k-none os=-os68k ;; pa-hitachi) basic_machine=hppa1.1-hitachi os=-hiuxwe2 ;; paragon) basic_machine=i860-intel os=-osf ;; parisc) basic_machine=hppa-unknown os=-linux ;; parisc-*) basic_machine=hppa-`echo "$basic_machine" | sed 's/^[^-]*-//'` os=-linux ;; pbd) basic_machine=sparc-tti ;; pbb) basic_machine=m68k-tti ;; pc532 | pc532-*) basic_machine=ns32k-pc532 ;; pc98) basic_machine=i386-pc ;; pc98-*) basic_machine=i386-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; pentium | p5 | k5 | k6 | nexgen | viac3) basic_machine=i586-pc ;; pentiumpro | p6 | 6x86 | athlon | athlon_*) basic_machine=i686-pc ;; pentiumii | pentium2 | pentiumiii | pentium3) basic_machine=i686-pc ;; pentium4) basic_machine=i786-pc ;; pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) basic_machine=i586-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; pentiumpro-* | p6-* | 6x86-* | athlon-*) basic_machine=i686-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) basic_machine=i686-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; pentium4-*) basic_machine=i786-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; pn) basic_machine=pn-gould ;; power) basic_machine=power-ibm ;; ppc | ppcbe) basic_machine=powerpc-unknown ;; ppc-* | ppcbe-*) basic_machine=powerpc-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; ppcle | powerpclittle) basic_machine=powerpcle-unknown ;; ppcle-* | powerpclittle-*) basic_machine=powerpcle-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; ppc64) basic_machine=powerpc64-unknown ;; ppc64-*) basic_machine=powerpc64-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; ppc64le | powerpc64little) basic_machine=powerpc64le-unknown ;; ppc64le-* | powerpc64little-*) basic_machine=powerpc64le-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; ps2) basic_machine=i386-ibm ;; pw32) basic_machine=i586-unknown os=-pw32 ;; rdos | rdos64) basic_machine=x86_64-pc os=-rdos ;; rdos32) basic_machine=i386-pc os=-rdos ;; rom68k) basic_machine=m68k-rom68k os=-coff ;; rm[46]00) basic_machine=mips-siemens ;; rtpc | rtpc-*) basic_machine=romp-ibm ;; s390 | s390-*) basic_machine=s390-ibm ;; s390x | s390x-*) basic_machine=s390x-ibm ;; sa29200) basic_machine=a29k-amd os=-udi ;; sb1) basic_machine=mipsisa64sb1-unknown ;; sb1el) basic_machine=mipsisa64sb1el-unknown ;; sde) basic_machine=mipsisa32-sde os=-elf ;; sei) basic_machine=mips-sei os=-seiux ;; sequent) basic_machine=i386-sequent ;; sh5el) basic_machine=sh5le-unknown ;; simso-wrs) basic_machine=sparclite-wrs os=-vxworks ;; sps7) basic_machine=m68k-bull os=-sysv2 ;; spur) basic_machine=spur-unknown ;; st2000) basic_machine=m68k-tandem ;; stratus) basic_machine=i860-stratus os=-sysv4 ;; strongarm-* | thumb-*) basic_machine=arm-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; sun2) basic_machine=m68000-sun ;; sun2os3) basic_machine=m68000-sun os=-sunos3 ;; sun2os4) basic_machine=m68000-sun os=-sunos4 ;; sun3os3) basic_machine=m68k-sun os=-sunos3 ;; sun3os4) basic_machine=m68k-sun os=-sunos4 ;; sun4os3) basic_machine=sparc-sun os=-sunos3 ;; sun4os4) basic_machine=sparc-sun os=-sunos4 ;; sun4sol2) basic_machine=sparc-sun os=-solaris2 ;; sun3 | sun3-*) basic_machine=m68k-sun ;; sun4) basic_machine=sparc-sun ;; sun386 | sun386i | roadrunner) basic_machine=i386-sun ;; sv1) basic_machine=sv1-cray os=-unicos ;; symmetry) basic_machine=i386-sequent os=-dynix ;; t3e) basic_machine=alphaev5-cray os=-unicos ;; t90) basic_machine=t90-cray os=-unicos ;; tile*) basic_machine=$basic_machine-unknown os=-linux-gnu ;; tx39) basic_machine=mipstx39-unknown ;; tx39el) basic_machine=mipstx39el-unknown ;; toad1) basic_machine=pdp10-xkl os=-tops20 ;; tower | tower-32) basic_machine=m68k-ncr ;; tpf) basic_machine=s390x-ibm os=-tpf ;; udi29k) basic_machine=a29k-amd os=-udi ;; ultra3) basic_machine=a29k-nyu os=-sym1 ;; v810 | necv810) basic_machine=v810-nec os=-none ;; vaxv) basic_machine=vax-dec os=-sysv ;; vms) basic_machine=vax-dec os=-vms ;; vpp*|vx|vx-*) basic_machine=f301-fujitsu ;; vxworks960) basic_machine=i960-wrs os=-vxworks ;; vxworks68) basic_machine=m68k-wrs os=-vxworks ;; vxworks29k) basic_machine=a29k-wrs os=-vxworks ;; w65*) basic_machine=w65-wdc os=-none ;; w89k-*) basic_machine=hppa1.1-winbond os=-proelf ;; x64) basic_machine=x86_64-pc ;; xbox) basic_machine=i686-pc os=-mingw32 ;; xps | xps100) basic_machine=xps100-honeywell ;; xscale-* | xscalee[bl]-*) basic_machine=`echo "$basic_machine" | sed 's/^xscale/arm/'` ;; ymp) basic_machine=ymp-cray os=-unicos ;; none) basic_machine=none-none os=-none ;; # Here we handle the default manufacturer of certain CPU types. It is in # some cases the only manufacturer, in others, it is the most popular. w89k) basic_machine=hppa1.1-winbond ;; op50n) basic_machine=hppa1.1-oki ;; op60c) basic_machine=hppa1.1-oki ;; romp) basic_machine=romp-ibm ;; mmix) basic_machine=mmix-knuth ;; rs6000) basic_machine=rs6000-ibm ;; vax) basic_machine=vax-dec ;; pdp11) basic_machine=pdp11-dec ;; we32k) basic_machine=we32k-att ;; sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele) basic_machine=sh-unknown ;; cydra) basic_machine=cydra-cydrome ;; orion) basic_machine=orion-highlevel ;; orion105) basic_machine=clipper-highlevel ;; mac | mpw | mac-mpw) basic_machine=m68k-apple ;; pmac | pmac-mpw) basic_machine=powerpc-apple ;; *-unknown) # Make sure to match an already-canonicalized machine name. ;; *) echo Invalid configuration \`"$1"\': machine \`"$basic_machine"\' not recognized 1>&2 exit 1 ;; esac # Here we canonicalize certain aliases for manufacturers. case $basic_machine in *-digital*) basic_machine=`echo "$basic_machine" | sed 's/digital.*/dec/'` ;; *-commodore*) basic_machine=`echo "$basic_machine" | sed 's/commodore.*/cbm/'` ;; *) ;; esac # Decode manufacturer-specific aliases for certain operating systems. if [ x"$os" != x"" ] then case $os in # First match some system type aliases that might get confused # with valid system types. # -solaris* is a basic system type, with this one exception. -auroraux) os=-auroraux ;; -solaris1 | -solaris1.*) os=`echo $os | sed -e 's|solaris1|sunos4|'` ;; -solaris) os=-solaris2 ;; -unixware*) os=-sysv4.2uw ;; -gnu/linux*) os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` ;; # es1800 is here to avoid being matched by es* (a different OS) -es1800*) os=-ose ;; # Now accept the basic system types. # The portable systems comes first. # Each alternative MUST end in a * to match a version number. # -sysv* is not here because it comes later, after sysvr4. -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ | -sym* | -kopensolaris* | -plan9* \ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ | -aos* | -aros* | -cloudabi* | -sortix* \ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ | -hiux* | -knetbsd* | -mirbsd* | -netbsd* \ | -bitrig* | -openbsd* | -solidbsd* | -libertybsd* \ | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ | -chorusos* | -chorusrdb* | -cegcc* | -glidix* \ | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ | -midipix* | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \ | -linux-newlib* | -linux-musl* | -linux-uclibc* \ | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* \ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ | -morphos* | -superux* | -rtmk* | -windiss* \ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* \ | -onefs* | -tirtos* | -phoenix* | -fuchsia* | -redox* | -bme* \ | -midnightbsd*) # Remember, each alternative MUST END IN *, to match a version number. ;; -qnx*) case $basic_machine in x86-* | i*86-*) ;; *) os=-nto$os ;; esac ;; -nto-qnx*) ;; -nto*) os=`echo $os | sed -e 's|nto|nto-qnx|'` ;; -sim | -xray | -os68k* | -v88r* \ | -windows* | -osx | -abug | -netware* | -os9* \ | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) ;; -mac*) os=`echo "$os" | sed -e 's|mac|macos|'` ;; -linux-dietlibc) os=-linux-dietlibc ;; -linux*) os=`echo $os | sed -e 's|linux|linux-gnu|'` ;; -sunos5*) os=`echo "$os" | sed -e 's|sunos5|solaris2|'` ;; -sunos6*) os=`echo "$os" | sed -e 's|sunos6|solaris3|'` ;; -opened*) os=-openedition ;; -os400*) os=-os400 ;; -wince*) os=-wince ;; -utek*) os=-bsd ;; -dynix*) os=-bsd ;; -acis*) os=-aos ;; -atheos*) os=-atheos ;; -syllable*) os=-syllable ;; -386bsd) os=-bsd ;; -ctix* | -uts*) os=-sysv ;; -nova*) os=-rtmk-nova ;; -ns2) os=-nextstep2 ;; -nsk*) os=-nsk ;; # Preserve the version number of sinix5. -sinix5.*) os=`echo $os | sed -e 's|sinix|sysv|'` ;; -sinix*) os=-sysv4 ;; -tpf*) os=-tpf ;; -triton*) os=-sysv3 ;; -oss*) os=-sysv3 ;; -svr4*) os=-sysv4 ;; -svr3) os=-sysv3 ;; -sysvr4) os=-sysv4 ;; # This must come after -sysvr4. -sysv*) ;; -ose*) os=-ose ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) os=-mint ;; -zvmoe) os=-zvmoe ;; -dicos*) os=-dicos ;; -pikeos*) # Until real need of OS specific support for # particular features comes up, bare metal # configurations are quite functional. case $basic_machine in arm*) os=-eabi ;; *) os=-elf ;; esac ;; -nacl*) ;; -ios) ;; -none) ;; *) # Get rid of the `-' at the beginning of $os. os=`echo $os | sed 's/[^-]*-//'` echo Invalid configuration \`"$1"\': system \`"$os"\' not recognized 1>&2 exit 1 ;; esac else # Here we handle the default operating systems that come with various machines. # The value should be what the vendor currently ships out the door with their # machine or put another way, the most popular os provided with the machine. # Note that if you're going to try to match "-MANUFACTURER" here (say, # "-sun"), then you have to tell the case statement up towards the top # that MANUFACTURER isn't an operating system. Otherwise, code above # will signal an error saying that MANUFACTURER isn't an operating # system, and we'll never get to this point. case $basic_machine in score-*) os=-elf ;; spu-*) os=-elf ;; *-acorn) os=-riscix1.2 ;; arm*-rebel) os=-linux ;; arm*-semi) os=-aout ;; c4x-* | tic4x-*) os=-coff ;; c8051-*) os=-elf ;; hexagon-*) os=-elf ;; tic54x-*) os=-coff ;; tic55x-*) os=-coff ;; tic6x-*) os=-coff ;; # This must come before the *-dec entry. pdp10-*) os=-tops20 ;; pdp11-*) os=-none ;; *-dec | vax-*) os=-ultrix4.2 ;; m68*-apollo) os=-domain ;; i386-sun) os=-sunos4.0.2 ;; m68000-sun) os=-sunos3 ;; m68*-cisco) os=-aout ;; mep-*) os=-elf ;; mips*-cisco) os=-elf ;; mips*-*) os=-elf ;; or32-*) os=-coff ;; *-tti) # must be before sparc entry or we get the wrong os. os=-sysv3 ;; sparc-* | *-sun) os=-sunos4.1.1 ;; pru-*) os=-elf ;; *-be) os=-beos ;; *-ibm) os=-aix ;; *-knuth) os=-mmixware ;; *-wec) os=-proelf ;; *-winbond) os=-proelf ;; *-oki) os=-proelf ;; *-hp) os=-hpux ;; *-hitachi) os=-hiux ;; i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) os=-sysv ;; *-cbm) os=-amigaos ;; *-dg) os=-dgux ;; *-dolphin) os=-sysv3 ;; m68k-ccur) os=-rtu ;; m88k-omron*) os=-luna ;; *-next) os=-nextstep ;; *-sequent) os=-ptx ;; *-crds) os=-unos ;; *-ns) os=-genix ;; i370-*) os=-mvs ;; *-gould) os=-sysv ;; *-highlevel) os=-bsd ;; *-encore) os=-bsd ;; *-sgi) os=-irix ;; *-siemens) os=-sysv4 ;; *-masscomp) os=-rtu ;; f30[01]-fujitsu | f700-fujitsu) os=-uxpv ;; *-rom68k) os=-coff ;; *-*bug) os=-coff ;; *-apple) os=-macos ;; *-atari*) os=-mint ;; *) os=-none ;; esac fi # Here we handle the case where we know the os, and the CPU type, but not the # manufacturer. We pick the logical manufacturer. vendor=unknown case $basic_machine in *-unknown) case $os in -riscix*) vendor=acorn ;; -sunos*) vendor=sun ;; -cnk*|-aix*) vendor=ibm ;; -beos*) vendor=be ;; -hpux*) vendor=hp ;; -mpeix*) vendor=hp ;; -hiux*) vendor=hitachi ;; -unos*) vendor=crds ;; -dgux*) vendor=dg ;; -luna*) vendor=omron ;; -genix*) vendor=ns ;; -mvs* | -opened*) vendor=ibm ;; -os400*) vendor=ibm ;; -ptx*) vendor=sequent ;; -tpf*) vendor=ibm ;; -vxsim* | -vxworks* | -windiss*) vendor=wrs ;; -aux*) vendor=apple ;; -hms*) vendor=hitachi ;; -mpw* | -macos*) vendor=apple ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) vendor=atari ;; -vos*) vendor=stratus ;; esac basic_machine=`echo "$basic_machine" | sed "s/unknown/$vendor/"` ;; esac echo "$basic_machine$os" exit # Local variables: # eval: (add-hook 'write-file-functions 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: libopenmpt-0.6.1+release.autotools/build-aux/depcomp0000755000175000017500000005602014175541545017541 00000000000000#! /bin/sh # depcomp - compile a program generating dependencies as side-effects scriptversion=2018-03-07.03; # UTC # Copyright (C) 1999-2020 Free Software Foundation, Inc. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # Originally written by Alexandre Oliva . case $1 in '') echo "$0: No command. Try '$0 --help' for more information." 1>&2 exit 1; ;; -h | --h*) cat <<\EOF Usage: depcomp [--help] [--version] PROGRAM [ARGS] Run PROGRAMS ARGS to compile a file, generating dependencies as side-effects. Environment variables: depmode Dependency tracking mode. source Source file read by 'PROGRAMS ARGS'. object Object file output by 'PROGRAMS ARGS'. DEPDIR directory where to store dependencies. depfile Dependency file to output. tmpdepfile Temporary file to use when outputting dependencies. libtool Whether libtool is used (yes/no). Report bugs to . EOF exit $? ;; -v | --v*) echo "depcomp $scriptversion" exit $? ;; esac # Get the directory component of the given path, and save it in the # global variables '$dir'. Note that this directory component will # be either empty or ending with a '/' character. This is deliberate. set_dir_from () { case $1 in */*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;; *) dir=;; esac } # Get the suffix-stripped basename of the given path, and save it the # global variable '$base'. set_base_from () { base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'` } # If no dependency file was actually created by the compiler invocation, # we still have to create a dummy depfile, to avoid errors with the # Makefile "include basename.Plo" scheme. make_dummy_depfile () { echo "#dummy" > "$depfile" } # Factor out some common post-processing of the generated depfile. # Requires the auxiliary global variable '$tmpdepfile' to be set. aix_post_process_depfile () { # If the compiler actually managed to produce a dependency file, # post-process it. if test -f "$tmpdepfile"; then # Each line is of the form 'foo.o: dependency.h'. # Do two passes, one to just change these to # $object: dependency.h # and one to simply output # dependency.h: # which is needed to avoid the deleted-header problem. { sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile" sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile" } > "$depfile" rm -f "$tmpdepfile" else make_dummy_depfile fi } # A tabulation character. tab=' ' # A newline character. nl=' ' # Character ranges might be problematic outside the C locale. # These definitions help. upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ lower=abcdefghijklmnopqrstuvwxyz digits=0123456789 alpha=${upper}${lower} if test -z "$depmode" || test -z "$source" || test -z "$object"; then echo "depcomp: Variables source, object and depmode must be set" 1>&2 exit 1 fi # Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po. depfile=${depfile-`echo "$object" | sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`} tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} rm -f "$tmpdepfile" # Avoid interferences from the environment. gccflag= dashmflag= # Some modes work just like other modes, but use different flags. We # parameterize here, but still list the modes in the big case below, # to make depend.m4 easier to write. Note that we *cannot* use a case # here, because this file can only contain one case statement. if test "$depmode" = hp; then # HP compiler uses -M and no extra arg. gccflag=-M depmode=gcc fi if test "$depmode" = dashXmstdout; then # This is just like dashmstdout with a different argument. dashmflag=-xM depmode=dashmstdout fi cygpath_u="cygpath -u -f -" if test "$depmode" = msvcmsys; then # This is just like msvisualcpp but w/o cygpath translation. # Just convert the backslash-escaped backslashes to single forward # slashes to satisfy depend.m4 cygpath_u='sed s,\\\\,/,g' depmode=msvisualcpp fi if test "$depmode" = msvc7msys; then # This is just like msvc7 but w/o cygpath translation. # Just convert the backslash-escaped backslashes to single forward # slashes to satisfy depend.m4 cygpath_u='sed s,\\\\,/,g' depmode=msvc7 fi if test "$depmode" = xlc; then # IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information. gccflag=-qmakedep=gcc,-MF depmode=gcc fi case "$depmode" in gcc3) ## gcc 3 implements dependency tracking that does exactly what ## we want. Yay! Note: for some reason libtool 1.4 doesn't like ## it if -MD -MP comes after the -MF stuff. Hmm. ## Unfortunately, FreeBSD c89 acceptance of flags depends upon ## the command line argument order; so add the flags where they ## appear in depend2.am. Note that the slowdown incurred here ## affects only configure: in makefiles, %FASTDEP% shortcuts this. for arg do case $arg in -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;; *) set fnord "$@" "$arg" ;; esac shift # fnord shift # $arg done "$@" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi mv "$tmpdepfile" "$depfile" ;; gcc) ## Note that this doesn't just cater to obsosete pre-3.x GCC compilers. ## but also to in-use compilers like IMB xlc/xlC and the HP C compiler. ## (see the conditional assignment to $gccflag above). ## There are various ways to get dependency output from gcc. Here's ## why we pick this rather obscure method: ## - Don't want to use -MD because we'd like the dependencies to end ## up in a subdir. Having to rename by hand is ugly. ## (We might end up doing this anyway to support other compilers.) ## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like ## -MM, not -M (despite what the docs say). Also, it might not be ## supported by the other compilers which use the 'gcc' depmode. ## - Using -M directly means running the compiler twice (even worse ## than renaming). if test -z "$gccflag"; then gccflag=-MD, fi "$@" -Wp,"$gccflag$tmpdepfile" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" echo "$object : \\" > "$depfile" # The second -e expression handles DOS-style file names with drive # letters. sed -e 's/^[^:]*: / /' \ -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" ## This next piece of magic avoids the "deleted header file" problem. ## The problem is that when a header file which appears in a .P file ## is deleted, the dependency causes make to die (because there is ## typically no way to rebuild the header). We avoid this by adding ## dummy dependencies for each header file. Too bad gcc doesn't do ## this for us directly. ## Some versions of gcc put a space before the ':'. On the theory ## that the space means something, we add a space to the output as ## well. hp depmode also adds that space, but also prefixes the VPATH ## to the object. Take care to not repeat it in the output. ## Some versions of the HPUX 10.20 sed can't process this invocation ## correctly. Breaking it into two sed invocations is a workaround. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; hp) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; sgi) if test "$libtool" = yes; then "$@" "-Wp,-MDupdate,$tmpdepfile" else "$@" -MDupdate "$tmpdepfile" fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files echo "$object : \\" > "$depfile" # Clip off the initial element (the dependent). Don't try to be # clever and replace this with sed code, as IRIX sed won't handle # lines with more than a fixed number of characters (4096 in # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; # the IRIX cc adds comments like '#:fec' to the end of the # dependency line. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \ | tr "$nl" ' ' >> "$depfile" echo >> "$depfile" # The second pass generates a dummy entry for each header file. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ >> "$depfile" else make_dummy_depfile fi rm -f "$tmpdepfile" ;; xlc) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; aix) # The C for AIX Compiler uses -M and outputs the dependencies # in a .u file. In older versions, this file always lives in the # current directory. Also, the AIX compiler puts '$object:' at the # start of each line; $object doesn't have directory information. # Version 6 uses the directory in both cases. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then tmpdepfile1=$dir$base.u tmpdepfile2=$base.u tmpdepfile3=$dir.libs/$base.u "$@" -Wc,-M else tmpdepfile1=$dir$base.u tmpdepfile2=$dir$base.u tmpdepfile3=$dir$base.u "$@" -M fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" do test -f "$tmpdepfile" && break done aix_post_process_depfile ;; tcc) # tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26 # FIXME: That version still under development at the moment of writing. # Make that this statement remains true also for stable, released # versions. # It will wrap lines (doesn't matter whether long or short) with a # trailing '\', as in: # # foo.o : \ # foo.c \ # foo.h \ # # It will put a trailing '\' even on the last line, and will use leading # spaces rather than leading tabs (at least since its commit 0394caf7 # "Emit spaces for -MD"). "$@" -MD -MF "$tmpdepfile" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" # Each non-empty line is of the form 'foo.o : \' or ' dep.h \'. # We have to change lines of the first kind to '$object: \'. sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile" # And for each line of the second kind, we have to emit a 'dep.h:' # dummy dependency, to avoid the deleted-header problem. sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile" rm -f "$tmpdepfile" ;; ## The order of this option in the case statement is important, since the ## shell code in configure will try each of these formats in the order ## listed in this file. A plain '-MD' option would be understood by many ## compilers, so we must ensure this comes after the gcc and icc options. pgcc) # Portland's C compiler understands '-MD'. # Will always output deps to 'file.d' where file is the root name of the # source file under compilation, even if file resides in a subdirectory. # The object file name does not affect the name of the '.d' file. # pgcc 10.2 will output # foo.o: sub/foo.c sub/foo.h # and will wrap long lines using '\' : # foo.o: sub/foo.c ... \ # sub/foo.h ... \ # ... set_dir_from "$object" # Use the source, not the object, to determine the base name, since # that's sadly what pgcc will do too. set_base_from "$source" tmpdepfile=$base.d # For projects that build the same source file twice into different object # files, the pgcc approach of using the *source* file root name can cause # problems in parallel builds. Use a locking strategy to avoid stomping on # the same $tmpdepfile. lockdir=$base.d-lock trap " echo '$0: caught signal, cleaning up...' >&2 rmdir '$lockdir' exit 1 " 1 2 13 15 numtries=100 i=$numtries while test $i -gt 0; do # mkdir is a portable test-and-set. if mkdir "$lockdir" 2>/dev/null; then # This process acquired the lock. "$@" -MD stat=$? # Release the lock. rmdir "$lockdir" break else # If the lock is being held by a different process, wait # until the winning process is done or we timeout. while test -d "$lockdir" && test $i -gt 0; do sleep 1 i=`expr $i - 1` done fi i=`expr $i - 1` done trap - 1 2 13 15 if test $i -le 0; then echo "$0: failed to acquire lock after $numtries attempts" >&2 echo "$0: check lockdir '$lockdir'" >&2 exit 1 fi if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" # Each line is of the form `foo.o: dependent.h', # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'. # Do two passes, one to just change these to # `$object: dependent.h' and one to simply `dependent.h:'. sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process this invocation # correctly. Breaking it into two sed invocations is a workaround. sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; hp2) # The "hp" stanza above does not work with aCC (C++) and HP's ia64 # compilers, which have integrated preprocessors. The correct option # to use with these is +Maked; it writes dependencies to a file named # 'foo.d', which lands next to the object file, wherever that # happens to be. # Much of this is similar to the tru64 case; see comments there. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then tmpdepfile1=$dir$base.d tmpdepfile2=$dir.libs/$base.d "$@" -Wc,+Maked else tmpdepfile1=$dir$base.d tmpdepfile2=$dir$base.d "$@" +Maked fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" do test -f "$tmpdepfile" && break done if test -f "$tmpdepfile"; then sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile" # Add 'dependent.h:' lines. sed -ne '2,${ s/^ *// s/ \\*$// s/$/:/ p }' "$tmpdepfile" >> "$depfile" else make_dummy_depfile fi rm -f "$tmpdepfile" "$tmpdepfile2" ;; tru64) # The Tru64 compiler uses -MD to generate dependencies as a side # effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'. # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put # dependencies in 'foo.d' instead, so we check for that too. # Subdirectories are respected. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then # Libtool generates 2 separate objects for the 2 libraries. These # two compilations output dependencies in $dir.libs/$base.o.d and # in $dir$base.o.d. We have to check for both files, because # one of the two compilations can be disabled. We should prefer # $dir$base.o.d over $dir.libs/$base.o.d because the latter is # automatically cleaned when .libs/ is deleted, while ignoring # the former would cause a distcleancheck panic. tmpdepfile1=$dir$base.o.d # libtool 1.5 tmpdepfile2=$dir.libs/$base.o.d # Likewise. tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504 "$@" -Wc,-MD else tmpdepfile1=$dir$base.d tmpdepfile2=$dir$base.d tmpdepfile3=$dir$base.d "$@" -MD fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" do test -f "$tmpdepfile" && break done # Same post-processing that is required for AIX mode. aix_post_process_depfile ;; msvc7) if test "$libtool" = yes; then showIncludes=-Wc,-showIncludes else showIncludes=-showIncludes fi "$@" $showIncludes > "$tmpdepfile" stat=$? grep -v '^Note: including file: ' "$tmpdepfile" if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" echo "$object : \\" > "$depfile" # The first sed program below extracts the file names and escapes # backslashes for cygpath. The second sed program outputs the file # name when reading, but also accumulates all include files in the # hold buffer in order to output them again at the end. This only # works with sed implementations that can handle large buffers. sed < "$tmpdepfile" -n ' /^Note: including file: *\(.*\)/ { s//\1/ s/\\/\\\\/g p }' | $cygpath_u | sort -u | sed -n ' s/ /\\ /g s/\(.*\)/'"$tab"'\1 \\/p s/.\(.*\) \\/\1:/ H $ { s/.*/'"$tab"'/ G p }' >> "$depfile" echo >> "$depfile" # make sure the fragment doesn't end with a backslash rm -f "$tmpdepfile" ;; msvc7msys) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; #nosideeffect) # This comment above is used by automake to tell side-effect # dependency tracking mechanisms from slower ones. dashmstdout) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout, regardless of -o. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # Remove '-o $object'. IFS=" " for arg do case $arg in -o) shift ;; $object) shift ;; *) set fnord "$@" "$arg" shift # fnord shift # $arg ;; esac done test -z "$dashmflag" && dashmflag=-M # Require at least two characters before searching for ':' # in the target name. This is to cope with DOS-style filenames: # a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise. "$@" $dashmflag | sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile" rm -f "$depfile" cat < "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process this sed invocation # correctly. Breaking it into two sed invocations is a workaround. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; dashXmstdout) # This case only exists to satisfy depend.m4. It is never actually # run, as this mode is specially recognized in the preamble. exit 1 ;; makedepend) "$@" || exit $? # Remove any Libtool call if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # X makedepend shift cleared=no eat=no for arg do case $cleared in no) set ""; shift cleared=yes ;; esac if test $eat = yes; then eat=no continue fi case "$arg" in -D*|-I*) set fnord "$@" "$arg"; shift ;; # Strip any option that makedepend may not understand. Remove # the object too, otherwise makedepend will parse it as a source file. -arch) eat=yes ;; -*|$object) ;; *) set fnord "$@" "$arg"; shift ;; esac done obj_suffix=`echo "$object" | sed 's/^.*\././'` touch "$tmpdepfile" ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@" rm -f "$depfile" # makedepend may prepend the VPATH from the source file name to the object. # No need to regex-escape $object, excess matching of '.' is harmless. sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process the last invocation # correctly. Breaking it into two sed invocations is a workaround. sed '1,2d' "$tmpdepfile" \ | tr ' ' "$nl" \ | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" "$tmpdepfile".bak ;; cpp) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # Remove '-o $object'. IFS=" " for arg do case $arg in -o) shift ;; $object) shift ;; *) set fnord "$@" "$arg" shift # fnord shift # $arg ;; esac done "$@" -E \ | sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ | sed '$ s: \\$::' > "$tmpdepfile" rm -f "$depfile" echo "$object : \\" > "$depfile" cat < "$tmpdepfile" >> "$depfile" sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; msvisualcpp) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi IFS=" " for arg do case "$arg" in -o) shift ;; $object) shift ;; "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") set fnord "$@" shift shift ;; *) set fnord "$@" "$arg" shift shift ;; esac done "$@" -E 2>/dev/null | sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile" rm -f "$depfile" echo "$object : \\" > "$depfile" sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile" echo "$tab" >> "$depfile" sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile" rm -f "$tmpdepfile" ;; msvcmsys) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; none) exec "$@" ;; *) echo "Unknown depmode $depmode" 1>&2 exit 1 ;; esac exit 0 # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: libopenmpt-0.6.1+release.autotools/build-aux/install-sh0000755000175000017500000003577614175541545020207 00000000000000#!/bin/sh # install - install a program, script, or datafile scriptversion=2020-11-14.01; # UTC # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the # following copyright and license. # # Copyright (C) 1994 X Consortium # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN # AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- # TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the name of the X Consortium shall not # be used in advertising or otherwise to promote the sale, use or other deal- # ings in this Software without prior written authorization from the X Consor- # tium. # # # FSF changes to this file are in the public domain. # # Calling this script install-sh is preferred over install.sh, to prevent # 'make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. tab=' ' nl=' ' IFS=" $tab$nl" # Set DOITPROG to "echo" to test this script. doit=${DOITPROG-} doit_exec=${doit:-exec} # Put in absolute file names if you don't have them in your path; # or use environment vars. chgrpprog=${CHGRPPROG-chgrp} chmodprog=${CHMODPROG-chmod} chownprog=${CHOWNPROG-chown} cmpprog=${CMPPROG-cmp} cpprog=${CPPROG-cp} mkdirprog=${MKDIRPROG-mkdir} mvprog=${MVPROG-mv} rmprog=${RMPROG-rm} stripprog=${STRIPPROG-strip} posix_mkdir= # Desired mode of installed file. mode=0755 # Create dirs (including intermediate dirs) using mode 755. # This is like GNU 'install' as of coreutils 8.32 (2020). mkdir_umask=22 backupsuffix= chgrpcmd= chmodcmd=$chmodprog chowncmd= mvcmd=$mvprog rmcmd="$rmprog -f" stripcmd= src= dst= dir_arg= dst_arg= copy_on_change=false is_target_a_directory=possibly usage="\ Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE or: $0 [OPTION]... SRCFILES... DIRECTORY or: $0 [OPTION]... -t DIRECTORY SRCFILES... or: $0 [OPTION]... -d DIRECTORIES... In the 1st form, copy SRCFILE to DSTFILE. In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. In the 4th, create DIRECTORIES. Options: --help display this help and exit. --version display version info and exit. -c (ignored) -C install only if different (preserve data modification time) -d create directories instead of installing files. -g GROUP $chgrpprog installed files to GROUP. -m MODE $chmodprog installed files to MODE. -o USER $chownprog installed files to USER. -p pass -p to $cpprog. -s $stripprog installed files. -S SUFFIX attempt to back up existing files, with suffix SUFFIX. -t DIRECTORY install into DIRECTORY. -T report an error if DSTFILE is a directory. Environment variables override the default commands: CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG By default, rm is invoked with -f; when overridden with RMPROG, it's up to you to specify -f if you want it. If -S is not specified, no backups are attempted. Email bug reports to bug-automake@gnu.org. Automake home page: https://www.gnu.org/software/automake/ " while test $# -ne 0; do case $1 in -c) ;; -C) copy_on_change=true;; -d) dir_arg=true;; -g) chgrpcmd="$chgrpprog $2" shift;; --help) echo "$usage"; exit $?;; -m) mode=$2 case $mode in *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*) echo "$0: invalid mode: $mode" >&2 exit 1;; esac shift;; -o) chowncmd="$chownprog $2" shift;; -p) cpprog="$cpprog -p";; -s) stripcmd=$stripprog;; -S) backupsuffix="$2" shift;; -t) is_target_a_directory=always dst_arg=$2 # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac shift;; -T) is_target_a_directory=never;; --version) echo "$0 $scriptversion"; exit $?;; --) shift break;; -*) echo "$0: invalid option: $1" >&2 exit 1;; *) break;; esac shift done # We allow the use of options -d and -T together, by making -d # take the precedence; this is for compatibility with GNU install. if test -n "$dir_arg"; then if test -n "$dst_arg"; then echo "$0: target directory not allowed when installing a directory." >&2 exit 1 fi fi if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then # When -d is used, all remaining arguments are directories to create. # When -t is used, the destination is already specified. # Otherwise, the last argument is the destination. Remove it from $@. for arg do if test -n "$dst_arg"; then # $@ is not empty: it contains at least $arg. set fnord "$@" "$dst_arg" shift # fnord fi shift # arg dst_arg=$arg # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac done fi if test $# -eq 0; then if test -z "$dir_arg"; then echo "$0: no input file specified." >&2 exit 1 fi # It's OK to call 'install-sh -d' without argument. # This can happen when creating conditional directories. exit 0 fi if test -z "$dir_arg"; then if test $# -gt 1 || test "$is_target_a_directory" = always; then if test ! -d "$dst_arg"; then echo "$0: $dst_arg: Is not a directory." >&2 exit 1 fi fi fi if test -z "$dir_arg"; then do_exit='(exit $ret); exit $ret' trap "ret=129; $do_exit" 1 trap "ret=130; $do_exit" 2 trap "ret=141; $do_exit" 13 trap "ret=143; $do_exit" 15 # Set umask so as not to create temps with too-generous modes. # However, 'strip' requires both read and write access to temps. case $mode in # Optimize common cases. *644) cp_umask=133;; *755) cp_umask=22;; *[0-7]) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw='% 200' fi cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; *) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw=,u+rw fi cp_umask=$mode$u_plus_rw;; esac fi for src do # Protect names problematic for 'test' and other utilities. case $src in -* | [=\(\)!]) src=./$src;; esac if test -n "$dir_arg"; then dst=$src dstdir=$dst test -d "$dstdir" dstdir_status=$? # Don't chown directories that already exist. if test $dstdir_status = 0; then chowncmd="" fi else # Waiting for this to be detected by the "$cpprog $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if test ! -f "$src" && test ! -d "$src"; then echo "$0: $src does not exist." >&2 exit 1 fi if test -z "$dst_arg"; then echo "$0: no destination specified." >&2 exit 1 fi dst=$dst_arg # If destination is a directory, append the input filename. if test -d "$dst"; then if test "$is_target_a_directory" = never; then echo "$0: $dst_arg: Is a directory" >&2 exit 1 fi dstdir=$dst dstbase=`basename "$src"` case $dst in */) dst=$dst$dstbase;; *) dst=$dst/$dstbase;; esac dstdir_status=0 else dstdir=`dirname "$dst"` test -d "$dstdir" dstdir_status=$? fi fi case $dstdir in */) dstdirslash=$dstdir;; *) dstdirslash=$dstdir/;; esac obsolete_mkdir_used=false if test $dstdir_status != 0; then case $posix_mkdir in '') # With -d, create the new directory with the user-specified mode. # Otherwise, rely on $mkdir_umask. if test -n "$dir_arg"; then mkdir_mode=-m$mode else mkdir_mode= fi posix_mkdir=false # The $RANDOM variable is not portable (e.g., dash). Use it # here however when possible just to lower collision chance. tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ trap ' ret=$? rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null exit $ret ' 0 # Because "mkdir -p" follows existing symlinks and we likely work # directly in world-writeable /tmp, make sure that the '$tmpdir' # directory is successfully created first before we actually test # 'mkdir -p'. if (umask $mkdir_umask && $mkdirprog $mkdir_mode "$tmpdir" && exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1 then if test -z "$dir_arg" || { # Check for POSIX incompatibilities with -m. # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or # other-writable bit of parent directory when it shouldn't. # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. test_tmpdir="$tmpdir/a" ls_ld_tmpdir=`ls -ld "$test_tmpdir"` case $ls_ld_tmpdir in d????-?r-*) different_mode=700;; d????-?--*) different_mode=755;; *) false;; esac && $mkdirprog -m$different_mode -p -- "$test_tmpdir" && { ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"` test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" } } then posix_mkdir=: fi rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" else # Remove any dirs left behind by ancient mkdir implementations. rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null fi trap '' 0;; esac if $posix_mkdir && ( umask $mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" ) then : else # mkdir does not conform to POSIX, # or it failed possibly due to a race condition. Create the # directory the slow way, step by step, checking for races as we go. case $dstdir in /*) prefix='/';; [-=\(\)!]*) prefix='./';; *) prefix='';; esac oIFS=$IFS IFS=/ set -f set fnord $dstdir shift set +f IFS=$oIFS prefixes= for d do test X"$d" = X && continue prefix=$prefix$d if test -d "$prefix"; then prefixes= else if $posix_mkdir; then (umask $mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break # Don't fail if two instances are running concurrently. test -d "$prefix" || exit 1 else case $prefix in *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; *) qprefix=$prefix;; esac prefixes="$prefixes '$qprefix'" fi fi prefix=$prefix/ done if test -n "$prefixes"; then # Don't fail if two instances are running concurrently. (umask $mkdir_umask && eval "\$doit_exec \$mkdirprog $prefixes") || test -d "$dstdir" || exit 1 obsolete_mkdir_used=true fi fi fi if test -n "$dir_arg"; then { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 else # Make a couple of temp file names in the proper directory. dsttmp=${dstdirslash}_inst.$$_ rmtmp=${dstdirslash}_rm.$$_ # Trap to clean up those temp files at exit. trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 # Copy the file name to the temp name. (umask $cp_umask && { test -z "$stripcmd" || { # Create $dsttmp read-write so that cp doesn't create it read-only, # which would cause strip to fail. if test -z "$doit"; then : >"$dsttmp" # No need to fork-exec 'touch'. else $doit touch "$dsttmp" fi } } && $doit_exec $cpprog "$src" "$dsttmp") && # and set any options; do chmod last to preserve setuid bits. # # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $cpprog $src $dsttmp" command. # { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && # If -C, don't bother to copy if it wouldn't change the file. if $copy_on_change && old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && set -f && set X $old && old=:$2:$4:$5:$6 && set X $new && new=:$2:$4:$5:$6 && set +f && test "$old" = "$new" && $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 then rm -f "$dsttmp" else # If $backupsuffix is set, and the file being installed # already exists, attempt a backup. Don't worry if it fails, # e.g., if mv doesn't support -f. if test -n "$backupsuffix" && test -f "$dst"; then $doit $mvcmd -f "$dst" "$dst$backupsuffix" 2>/dev/null fi # Rename the file to the real destination. $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || # The rename failed, perhaps because mv can't rename something else # to itself, or perhaps because mv is so ancient that it does not # support -f. { # Now remove or move aside any old file at destination location. # We try this two ways since rm can't unlink itself on some # systems and the destination file might be busy for other # reasons. In this case, the final cleanup might fail but the new # file should still install successfully. { test ! -f "$dst" || $doit $rmcmd "$dst" 2>/dev/null || { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && { $doit $rmcmd "$rmtmp" 2>/dev/null; :; } } || { echo "$0: cannot unlink or rename $dst" >&2 (exit 1); exit 1 } } && # Now rename the file to the real destination. $doit $mvcmd "$dsttmp" "$dst" } fi || exit 1 trap '' 0 fi done # Local variables: # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: libopenmpt-0.6.1+release.autotools/build-aux/ltmain.sh0000755000175000017500000117716714175541533020025 00000000000000#! /bin/sh ## DO NOT EDIT - This file generated from ./build-aux/ltmain.in ## by inline-source v2014-01-03.01 # libtool (GNU libtool) 2.4.6 # Provide generalized library-building support services. # Written by Gordon Matzigkeit , 1996 # Copyright (C) 1996-2015 Free Software Foundation, Inc. # This is free software; see the source for copying conditions. There is NO # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # GNU Libtool is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # As a special exception to the GNU General Public License, # if you distribute this file as part of a program or library that # is built using GNU Libtool, you may include this file under the # same distribution terms that you use for the rest of that program. # # GNU Libtool is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . PROGRAM=libtool PACKAGE=libtool VERSION="2.4.6 Debian-2.4.6-15" package_revision=2.4.6 ## ------ ## ## Usage. ## ## ------ ## # Run './libtool --help' for help with using this script from the # command line. ## ------------------------------- ## ## User overridable command paths. ## ## ------------------------------- ## # After configure completes, it has a better idea of some of the # shell tools we need than the defaults used by the functions shared # with bootstrap, so set those here where they can still be over- # ridden by the user, but otherwise take precedence. : ${AUTOCONF="autoconf"} : ${AUTOMAKE="automake"} ## -------------------------- ## ## Source external libraries. ## ## -------------------------- ## # Much of our low-level functionality needs to be sourced from external # libraries, which are installed to $pkgauxdir. # Set a version string for this script. scriptversion=2015-01-20.17; # UTC # General shell script boiler plate, and helper functions. # Written by Gary V. Vaughan, 2004 # Copyright (C) 2004-2015 Free Software Foundation, Inc. # This is free software; see the source for copying conditions. There is NO # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # As a special exception to the GNU General Public License, if you distribute # this file as part of a program or library that is built using GNU Libtool, # you may include this file under the same distribution terms that you use # for the rest of that program. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNES FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # Please report bugs or propose patches to gary@gnu.org. ## ------ ## ## Usage. ## ## ------ ## # Evaluate this file near the top of your script to gain access to # the functions and variables defined here: # # . `echo "$0" | ${SED-sed} 's|[^/]*$||'`/build-aux/funclib.sh # # If you need to override any of the default environment variable # settings, do that before evaluating this file. ## -------------------- ## ## Shell normalisation. ## ## -------------------- ## # Some shells need a little help to be as Bourne compatible as possible. # Before doing anything else, make sure all that help has been provided! DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in *posix*) set -o posix ;; esac fi # NLS nuisances: We save the old values in case they are required later. _G_user_locale= _G_safe_locale= for _G_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES do eval "if test set = \"\${$_G_var+set}\"; then save_$_G_var=\$$_G_var $_G_var=C export $_G_var _G_user_locale=\"$_G_var=\\\$save_\$_G_var; \$_G_user_locale\" _G_safe_locale=\"$_G_var=C; \$_G_safe_locale\" fi" done # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # Make sure IFS has a sensible default sp=' ' nl=' ' IFS="$sp $nl" # There are apparently some retarded systems that use ';' as a PATH separator! if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi ## ------------------------- ## ## Locate command utilities. ## ## ------------------------- ## # func_executable_p FILE # ---------------------- # Check that FILE is an executable regular file. func_executable_p () { test -f "$1" && test -x "$1" } # func_path_progs PROGS_LIST CHECK_FUNC [PATH] # -------------------------------------------- # Search for either a program that responds to --version with output # containing "GNU", or else returned by CHECK_FUNC otherwise, by # trying all the directories in PATH with each of the elements of # PROGS_LIST. # # CHECK_FUNC should accept the path to a candidate program, and # set $func_check_prog_result if it truncates its output less than # $_G_path_prog_max characters. func_path_progs () { _G_progs_list=$1 _G_check_func=$2 _G_PATH=${3-"$PATH"} _G_path_prog_max=0 _G_path_prog_found=false _G_save_IFS=$IFS; IFS=${PATH_SEPARATOR-:} for _G_dir in $_G_PATH; do IFS=$_G_save_IFS test -z "$_G_dir" && _G_dir=. for _G_prog_name in $_G_progs_list; do for _exeext in '' .EXE; do _G_path_prog=$_G_dir/$_G_prog_name$_exeext func_executable_p "$_G_path_prog" || continue case `"$_G_path_prog" --version 2>&1` in *GNU*) func_path_progs_result=$_G_path_prog _G_path_prog_found=: ;; *) $_G_check_func $_G_path_prog func_path_progs_result=$func_check_prog_result ;; esac $_G_path_prog_found && break 3 done done done IFS=$_G_save_IFS test -z "$func_path_progs_result" && { echo "no acceptable sed could be found in \$PATH" >&2 exit 1 } } # We want to be able to use the functions in this file before configure # has figured out where the best binaries are kept, which means we have # to search for them ourselves - except when the results are already set # where we skip the searches. # Unless the user overrides by setting SED, search the path for either GNU # sed, or the sed that truncates its output the least. test -z "$SED" && { _G_sed_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ for _G_i in 1 2 3 4 5 6 7; do _G_sed_script=$_G_sed_script$nl$_G_sed_script done echo "$_G_sed_script" 2>/dev/null | sed 99q >conftest.sed _G_sed_script= func_check_prog_sed () { _G_path_prog=$1 _G_count=0 printf 0123456789 >conftest.in while : do cat conftest.in conftest.in >conftest.tmp mv conftest.tmp conftest.in cp conftest.in conftest.nl echo '' >> conftest.nl "$_G_path_prog" -f conftest.sed conftest.out 2>/dev/null || break diff conftest.out conftest.nl >/dev/null 2>&1 || break _G_count=`expr $_G_count + 1` if test "$_G_count" -gt "$_G_path_prog_max"; then # Best one so far, save it but keep looking for a better one func_check_prog_result=$_G_path_prog _G_path_prog_max=$_G_count fi # 10*(2^10) chars as input seems more than enough test 10 -lt "$_G_count" && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out } func_path_progs "sed gsed" func_check_prog_sed $PATH:/usr/xpg4/bin rm -f conftest.sed SED=$func_path_progs_result } # Unless the user overrides by setting GREP, search the path for either GNU # grep, or the grep that truncates its output the least. test -z "$GREP" && { func_check_prog_grep () { _G_path_prog=$1 _G_count=0 _G_path_prog_max=0 printf 0123456789 >conftest.in while : do cat conftest.in conftest.in >conftest.tmp mv conftest.tmp conftest.in cp conftest.in conftest.nl echo 'GREP' >> conftest.nl "$_G_path_prog" -e 'GREP$' -e '-(cannot match)-' conftest.out 2>/dev/null || break diff conftest.out conftest.nl >/dev/null 2>&1 || break _G_count=`expr $_G_count + 1` if test "$_G_count" -gt "$_G_path_prog_max"; then # Best one so far, save it but keep looking for a better one func_check_prog_result=$_G_path_prog _G_path_prog_max=$_G_count fi # 10*(2^10) chars as input seems more than enough test 10 -lt "$_G_count" && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out } func_path_progs "grep ggrep" func_check_prog_grep $PATH:/usr/xpg4/bin GREP=$func_path_progs_result } ## ------------------------------- ## ## User overridable command paths. ## ## ------------------------------- ## # All uppercase variable names are used for environment variables. These # variables can be overridden by the user before calling a script that # uses them if a suitable command of that name is not already available # in the command search PATH. : ${CP="cp -f"} : ${ECHO="printf %s\n"} : ${EGREP="$GREP -E"} : ${FGREP="$GREP -F"} : ${LN_S="ln -s"} : ${MAKE="make"} : ${MKDIR="mkdir"} : ${MV="mv -f"} : ${RM="rm -f"} : ${SHELL="${CONFIG_SHELL-/bin/sh}"} ## -------------------- ## ## Useful sed snippets. ## ## -------------------- ## sed_dirname='s|/[^/]*$||' sed_basename='s|^.*/||' # Sed substitution that helps us do robust quoting. It backslashifies # metacharacters that are still active within double-quoted strings. sed_quote_subst='s|\([`"$\\]\)|\\\1|g' # Same as above, but do not quote variable references. sed_double_quote_subst='s/\(["`\\]\)/\\\1/g' # Sed substitution that turns a string into a regex matching for the # string literally. sed_make_literal_regex='s|[].[^$\\*\/]|\\&|g' # Sed substitution that converts a w32 file name or path # that contains forward slashes, into one that contains # (escaped) backslashes. A very naive implementation. sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g' # Re-'\' parameter expansions in output of sed_double_quote_subst that # were '\'-ed in input to the same. If an odd number of '\' preceded a # '$' in input to sed_double_quote_subst, that '$' was protected from # expansion. Since each input '\' is now two '\'s, look for any number # of runs of four '\'s followed by two '\'s and then a '$'. '\' that '$'. _G_bs='\\' _G_bs2='\\\\' _G_bs4='\\\\\\\\' _G_dollar='\$' sed_double_backslash="\ s/$_G_bs4/&\\ /g s/^$_G_bs2$_G_dollar/$_G_bs&/ s/\\([^$_G_bs]\\)$_G_bs2$_G_dollar/\\1$_G_bs2$_G_bs$_G_dollar/g s/\n//g" ## ----------------- ## ## Global variables. ## ## ----------------- ## # Except for the global variables explicitly listed below, the following # functions in the '^func_' namespace, and the '^require_' namespace # variables initialised in the 'Resource management' section, sourcing # this file will not pollute your global namespace with anything # else. There's no portable way to scope variables in Bourne shell # though, so actually running these functions will sometimes place # results into a variable named after the function, and often use # temporary variables in the '^_G_' namespace. If you are careful to # avoid using those namespaces casually in your sourcing script, things # should continue to work as you expect. And, of course, you can freely # overwrite any of the functions or variables defined here before # calling anything to customize them. EXIT_SUCCESS=0 EXIT_FAILURE=1 EXIT_MISMATCH=63 # $? = 63 is used to indicate version mismatch to missing. EXIT_SKIP=77 # $? = 77 is used to indicate a skipped test to automake. # Allow overriding, eg assuming that you follow the convention of # putting '$debug_cmd' at the start of all your functions, you can get # bash to show function call trace with: # # debug_cmd='echo "${FUNCNAME[0]} $*" >&2' bash your-script-name debug_cmd=${debug_cmd-":"} exit_cmd=: # By convention, finish your script with: # # exit $exit_status # # so that you can set exit_status to non-zero if you want to indicate # something went wrong during execution without actually bailing out at # the point of failure. exit_status=$EXIT_SUCCESS # Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh # is ksh but when the shell is invoked as "sh" and the current value of # the _XPG environment variable is not equal to 1 (one), the special # positional parameter $0, within a function call, is the name of the # function. progpath=$0 # The name of this program. progname=`$ECHO "$progpath" |$SED "$sed_basename"` # Make sure we have an absolute progpath for reexecution: case $progpath in [\\/]*|[A-Za-z]:\\*) ;; *[\\/]*) progdir=`$ECHO "$progpath" |$SED "$sed_dirname"` progdir=`cd "$progdir" && pwd` progpath=$progdir/$progname ;; *) _G_IFS=$IFS IFS=${PATH_SEPARATOR-:} for progdir in $PATH; do IFS=$_G_IFS test -x "$progdir/$progname" && break done IFS=$_G_IFS test -n "$progdir" || progdir=`pwd` progpath=$progdir/$progname ;; esac ## ----------------- ## ## Standard options. ## ## ----------------- ## # The following options affect the operation of the functions defined # below, and should be set appropriately depending on run-time para- # meters passed on the command line. opt_dry_run=false opt_quiet=false opt_verbose=false # Categories 'all' and 'none' are always available. Append any others # you will pass as the first argument to func_warning from your own # code. warning_categories= # By default, display warnings according to 'opt_warning_types'. Set # 'warning_func' to ':' to elide all warnings, or func_fatal_error to # treat the next displayed warning as a fatal error. warning_func=func_warn_and_continue # Set to 'all' to display all warnings, 'none' to suppress all # warnings, or a space delimited list of some subset of # 'warning_categories' to display only the listed warnings. opt_warning_types=all ## -------------------- ## ## Resource management. ## ## -------------------- ## # This section contains definitions for functions that each ensure a # particular resource (a file, or a non-empty configuration variable for # example) is available, and if appropriate to extract default values # from pertinent package files. Call them using their associated # 'require_*' variable to ensure that they are executed, at most, once. # # It's entirely deliberate that calling these functions can set # variables that don't obey the namespace limitations obeyed by the rest # of this file, in order that that they be as useful as possible to # callers. # require_term_colors # ------------------- # Allow display of bold text on terminals that support it. require_term_colors=func_require_term_colors func_require_term_colors () { $debug_cmd test -t 1 && { # COLORTERM and USE_ANSI_COLORS environment variables take # precedence, because most terminfo databases neglect to describe # whether color sequences are supported. test -n "${COLORTERM+set}" && : ${USE_ANSI_COLORS="1"} if test 1 = "$USE_ANSI_COLORS"; then # Standard ANSI escape sequences tc_reset='' tc_bold=''; tc_standout='' tc_red=''; tc_green='' tc_blue=''; tc_cyan='' else # Otherwise trust the terminfo database after all. test -n "`tput sgr0 2>/dev/null`" && { tc_reset=`tput sgr0` test -n "`tput bold 2>/dev/null`" && tc_bold=`tput bold` tc_standout=$tc_bold test -n "`tput smso 2>/dev/null`" && tc_standout=`tput smso` test -n "`tput setaf 1 2>/dev/null`" && tc_red=`tput setaf 1` test -n "`tput setaf 2 2>/dev/null`" && tc_green=`tput setaf 2` test -n "`tput setaf 4 2>/dev/null`" && tc_blue=`tput setaf 4` test -n "`tput setaf 5 2>/dev/null`" && tc_cyan=`tput setaf 5` } fi } require_term_colors=: } ## ----------------- ## ## Function library. ## ## ----------------- ## # This section contains a variety of useful functions to call in your # scripts. Take note of the portable wrappers for features provided by # some modern shells, which will fall back to slower equivalents on # less featureful shells. # func_append VAR VALUE # --------------------- # Append VALUE onto the existing contents of VAR. # We should try to minimise forks, especially on Windows where they are # unreasonably slow, so skip the feature probes when bash or zsh are # being used: if test set = "${BASH_VERSION+set}${ZSH_VERSION+set}"; then : ${_G_HAVE_ARITH_OP="yes"} : ${_G_HAVE_XSI_OPS="yes"} # The += operator was introduced in bash 3.1 case $BASH_VERSION in [12].* | 3.0 | 3.0*) ;; *) : ${_G_HAVE_PLUSEQ_OP="yes"} ;; esac fi # _G_HAVE_PLUSEQ_OP # Can be empty, in which case the shell is probed, "yes" if += is # useable or anything else if it does not work. test -z "$_G_HAVE_PLUSEQ_OP" \ && (eval 'x=a; x+=" b"; test "a b" = "$x"') 2>/dev/null \ && _G_HAVE_PLUSEQ_OP=yes if test yes = "$_G_HAVE_PLUSEQ_OP" then # This is an XSI compatible shell, allowing a faster implementation... eval 'func_append () { $debug_cmd eval "$1+=\$2" }' else # ...otherwise fall back to using expr, which is often a shell builtin. func_append () { $debug_cmd eval "$1=\$$1\$2" } fi # func_append_quoted VAR VALUE # ---------------------------- # Quote VALUE and append to the end of shell variable VAR, separated # by a space. if test yes = "$_G_HAVE_PLUSEQ_OP"; then eval 'func_append_quoted () { $debug_cmd func_quote_for_eval "$2" eval "$1+=\\ \$func_quote_for_eval_result" }' else func_append_quoted () { $debug_cmd func_quote_for_eval "$2" eval "$1=\$$1\\ \$func_quote_for_eval_result" } fi # func_append_uniq VAR VALUE # -------------------------- # Append unique VALUE onto the existing contents of VAR, assuming # entries are delimited by the first character of VALUE. For example: # # func_append_uniq options " --another-option option-argument" # # will only append to $options if " --another-option option-argument " # is not already present somewhere in $options already (note spaces at # each end implied by leading space in second argument). func_append_uniq () { $debug_cmd eval _G_current_value='`$ECHO $'$1'`' _G_delim=`expr "$2" : '\(.\)'` case $_G_delim$_G_current_value$_G_delim in *"$2$_G_delim"*) ;; *) func_append "$@" ;; esac } # func_arith TERM... # ------------------ # Set func_arith_result to the result of evaluating TERMs. test -z "$_G_HAVE_ARITH_OP" \ && (eval 'test 2 = $(( 1 + 1 ))') 2>/dev/null \ && _G_HAVE_ARITH_OP=yes if test yes = "$_G_HAVE_ARITH_OP"; then eval 'func_arith () { $debug_cmd func_arith_result=$(( $* )) }' else func_arith () { $debug_cmd func_arith_result=`expr "$@"` } fi # func_basename FILE # ------------------ # Set func_basename_result to FILE with everything up to and including # the last / stripped. if test yes = "$_G_HAVE_XSI_OPS"; then # If this shell supports suffix pattern removal, then use it to avoid # forking. Hide the definitions single quotes in case the shell chokes # on unsupported syntax... _b='func_basename_result=${1##*/}' _d='case $1 in */*) func_dirname_result=${1%/*}$2 ;; * ) func_dirname_result=$3 ;; esac' else # ...otherwise fall back to using sed. _b='func_basename_result=`$ECHO "$1" |$SED "$sed_basename"`' _d='func_dirname_result=`$ECHO "$1" |$SED "$sed_dirname"` if test "X$func_dirname_result" = "X$1"; then func_dirname_result=$3 else func_append func_dirname_result "$2" fi' fi eval 'func_basename () { $debug_cmd '"$_b"' }' # func_dirname FILE APPEND NONDIR_REPLACEMENT # ------------------------------------------- # Compute the dirname of FILE. If nonempty, add APPEND to the result, # otherwise set result to NONDIR_REPLACEMENT. eval 'func_dirname () { $debug_cmd '"$_d"' }' # func_dirname_and_basename FILE APPEND NONDIR_REPLACEMENT # -------------------------------------------------------- # Perform func_basename and func_dirname in a single function # call: # dirname: Compute the dirname of FILE. If nonempty, # add APPEND to the result, otherwise set result # to NONDIR_REPLACEMENT. # value returned in "$func_dirname_result" # basename: Compute filename of FILE. # value retuned in "$func_basename_result" # For efficiency, we do not delegate to the functions above but instead # duplicate the functionality here. eval 'func_dirname_and_basename () { $debug_cmd '"$_b"' '"$_d"' }' # func_echo ARG... # ---------------- # Echo program name prefixed message. func_echo () { $debug_cmd _G_message=$* func_echo_IFS=$IFS IFS=$nl for _G_line in $_G_message; do IFS=$func_echo_IFS $ECHO "$progname: $_G_line" done IFS=$func_echo_IFS } # func_echo_all ARG... # -------------------- # Invoke $ECHO with all args, space-separated. func_echo_all () { $ECHO "$*" } # func_echo_infix_1 INFIX ARG... # ------------------------------ # Echo program name, followed by INFIX on the first line, with any # additional lines not showing INFIX. func_echo_infix_1 () { $debug_cmd $require_term_colors _G_infix=$1; shift _G_indent=$_G_infix _G_prefix="$progname: $_G_infix: " _G_message=$* # Strip color escape sequences before counting printable length for _G_tc in "$tc_reset" "$tc_bold" "$tc_standout" "$tc_red" "$tc_green" "$tc_blue" "$tc_cyan" do test -n "$_G_tc" && { _G_esc_tc=`$ECHO "$_G_tc" | $SED "$sed_make_literal_regex"` _G_indent=`$ECHO "$_G_indent" | $SED "s|$_G_esc_tc||g"` } done _G_indent="$progname: "`echo "$_G_indent" | $SED 's|.| |g'`" " ## exclude from sc_prohibit_nested_quotes func_echo_infix_1_IFS=$IFS IFS=$nl for _G_line in $_G_message; do IFS=$func_echo_infix_1_IFS $ECHO "$_G_prefix$tc_bold$_G_line$tc_reset" >&2 _G_prefix=$_G_indent done IFS=$func_echo_infix_1_IFS } # func_error ARG... # ----------------- # Echo program name prefixed message to standard error. func_error () { $debug_cmd $require_term_colors func_echo_infix_1 " $tc_standout${tc_red}error$tc_reset" "$*" >&2 } # func_fatal_error ARG... # ----------------------- # Echo program name prefixed message to standard error, and exit. func_fatal_error () { $debug_cmd func_error "$*" exit $EXIT_FAILURE } # func_grep EXPRESSION FILENAME # ----------------------------- # Check whether EXPRESSION matches any line of FILENAME, without output. func_grep () { $debug_cmd $GREP "$1" "$2" >/dev/null 2>&1 } # func_len STRING # --------------- # Set func_len_result to the length of STRING. STRING may not # start with a hyphen. test -z "$_G_HAVE_XSI_OPS" \ && (eval 'x=a/b/c; test 5aa/bb/cc = "${#x}${x%%/*}${x%/*}${x#*/}${x##*/}"') 2>/dev/null \ && _G_HAVE_XSI_OPS=yes if test yes = "$_G_HAVE_XSI_OPS"; then eval 'func_len () { $debug_cmd func_len_result=${#1} }' else func_len () { $debug_cmd func_len_result=`expr "$1" : ".*" 2>/dev/null || echo $max_cmd_len` } fi # func_mkdir_p DIRECTORY-PATH # --------------------------- # Make sure the entire path to DIRECTORY-PATH is available. func_mkdir_p () { $debug_cmd _G_directory_path=$1 _G_dir_list= if test -n "$_G_directory_path" && test : != "$opt_dry_run"; then # Protect directory names starting with '-' case $_G_directory_path in -*) _G_directory_path=./$_G_directory_path ;; esac # While some portion of DIR does not yet exist... while test ! -d "$_G_directory_path"; do # ...make a list in topmost first order. Use a colon delimited # list incase some portion of path contains whitespace. _G_dir_list=$_G_directory_path:$_G_dir_list # If the last portion added has no slash in it, the list is done case $_G_directory_path in */*) ;; *) break ;; esac # ...otherwise throw away the child directory and loop _G_directory_path=`$ECHO "$_G_directory_path" | $SED -e "$sed_dirname"` done _G_dir_list=`$ECHO "$_G_dir_list" | $SED 's|:*$||'` func_mkdir_p_IFS=$IFS; IFS=: for _G_dir in $_G_dir_list; do IFS=$func_mkdir_p_IFS # mkdir can fail with a 'File exist' error if two processes # try to create one of the directories concurrently. Don't # stop in that case! $MKDIR "$_G_dir" 2>/dev/null || : done IFS=$func_mkdir_p_IFS # Bail out if we (or some other process) failed to create a directory. test -d "$_G_directory_path" || \ func_fatal_error "Failed to create '$1'" fi } # func_mktempdir [BASENAME] # ------------------------- # Make a temporary directory that won't clash with other running # libtool processes, and avoids race conditions if possible. If # given, BASENAME is the basename for that directory. func_mktempdir () { $debug_cmd _G_template=${TMPDIR-/tmp}/${1-$progname} if test : = "$opt_dry_run"; then # Return a directory name, but don't create it in dry-run mode _G_tmpdir=$_G_template-$$ else # If mktemp works, use that first and foremost _G_tmpdir=`mktemp -d "$_G_template-XXXXXXXX" 2>/dev/null` if test ! -d "$_G_tmpdir"; then # Failing that, at least try and use $RANDOM to avoid a race _G_tmpdir=$_G_template-${RANDOM-0}$$ func_mktempdir_umask=`umask` umask 0077 $MKDIR "$_G_tmpdir" umask $func_mktempdir_umask fi # If we're not in dry-run mode, bomb out on failure test -d "$_G_tmpdir" || \ func_fatal_error "cannot create temporary directory '$_G_tmpdir'" fi $ECHO "$_G_tmpdir" } # func_normal_abspath PATH # ------------------------ # Remove doubled-up and trailing slashes, "." path components, # and cancel out any ".." path components in PATH after making # it an absolute path. func_normal_abspath () { $debug_cmd # These SED scripts presuppose an absolute path with a trailing slash. _G_pathcar='s|^/\([^/]*\).*$|\1|' _G_pathcdr='s|^/[^/]*||' _G_removedotparts=':dotsl s|/\./|/|g t dotsl s|/\.$|/|' _G_collapseslashes='s|/\{1,\}|/|g' _G_finalslash='s|/*$|/|' # Start from root dir and reassemble the path. func_normal_abspath_result= func_normal_abspath_tpath=$1 func_normal_abspath_altnamespace= case $func_normal_abspath_tpath in "") # Empty path, that just means $cwd. func_stripname '' '/' "`pwd`" func_normal_abspath_result=$func_stripname_result return ;; # The next three entries are used to spot a run of precisely # two leading slashes without using negated character classes; # we take advantage of case's first-match behaviour. ///*) # Unusual form of absolute path, do nothing. ;; //*) # Not necessarily an ordinary path; POSIX reserves leading '//' # and for example Cygwin uses it to access remote file shares # over CIFS/SMB, so we conserve a leading double slash if found. func_normal_abspath_altnamespace=/ ;; /*) # Absolute path, do nothing. ;; *) # Relative path, prepend $cwd. func_normal_abspath_tpath=`pwd`/$func_normal_abspath_tpath ;; esac # Cancel out all the simple stuff to save iterations. We also want # the path to end with a slash for ease of parsing, so make sure # there is one (and only one) here. func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ -e "$_G_removedotparts" -e "$_G_collapseslashes" -e "$_G_finalslash"` while :; do # Processed it all yet? if test / = "$func_normal_abspath_tpath"; then # If we ascended to the root using ".." the result may be empty now. if test -z "$func_normal_abspath_result"; then func_normal_abspath_result=/ fi break fi func_normal_abspath_tcomponent=`$ECHO "$func_normal_abspath_tpath" | $SED \ -e "$_G_pathcar"` func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ -e "$_G_pathcdr"` # Figure out what to do with it case $func_normal_abspath_tcomponent in "") # Trailing empty path component, ignore it. ;; ..) # Parent dir; strip last assembled component from result. func_dirname "$func_normal_abspath_result" func_normal_abspath_result=$func_dirname_result ;; *) # Actual path component, append it. func_append func_normal_abspath_result "/$func_normal_abspath_tcomponent" ;; esac done # Restore leading double-slash if one was found on entry. func_normal_abspath_result=$func_normal_abspath_altnamespace$func_normal_abspath_result } # func_notquiet ARG... # -------------------- # Echo program name prefixed message only when not in quiet mode. func_notquiet () { $debug_cmd $opt_quiet || func_echo ${1+"$@"} # A bug in bash halts the script if the last line of a function # fails when set -e is in force, so we need another command to # work around that: : } # func_relative_path SRCDIR DSTDIR # -------------------------------- # Set func_relative_path_result to the relative path from SRCDIR to DSTDIR. func_relative_path () { $debug_cmd func_relative_path_result= func_normal_abspath "$1" func_relative_path_tlibdir=$func_normal_abspath_result func_normal_abspath "$2" func_relative_path_tbindir=$func_normal_abspath_result # Ascend the tree starting from libdir while :; do # check if we have found a prefix of bindir case $func_relative_path_tbindir in $func_relative_path_tlibdir) # found an exact match func_relative_path_tcancelled= break ;; $func_relative_path_tlibdir*) # found a matching prefix func_stripname "$func_relative_path_tlibdir" '' "$func_relative_path_tbindir" func_relative_path_tcancelled=$func_stripname_result if test -z "$func_relative_path_result"; then func_relative_path_result=. fi break ;; *) func_dirname $func_relative_path_tlibdir func_relative_path_tlibdir=$func_dirname_result if test -z "$func_relative_path_tlibdir"; then # Have to descend all the way to the root! func_relative_path_result=../$func_relative_path_result func_relative_path_tcancelled=$func_relative_path_tbindir break fi func_relative_path_result=../$func_relative_path_result ;; esac done # Now calculate path; take care to avoid doubling-up slashes. func_stripname '' '/' "$func_relative_path_result" func_relative_path_result=$func_stripname_result func_stripname '/' '/' "$func_relative_path_tcancelled" if test -n "$func_stripname_result"; then func_append func_relative_path_result "/$func_stripname_result" fi # Normalisation. If bindir is libdir, return '.' else relative path. if test -n "$func_relative_path_result"; then func_stripname './' '' "$func_relative_path_result" func_relative_path_result=$func_stripname_result fi test -n "$func_relative_path_result" || func_relative_path_result=. : } # func_quote_for_eval ARG... # -------------------------- # Aesthetically quote ARGs to be evaled later. # This function returns two values: # i) func_quote_for_eval_result # double-quoted, suitable for a subsequent eval # ii) func_quote_for_eval_unquoted_result # has all characters that are still active within double # quotes backslashified. func_quote_for_eval () { $debug_cmd func_quote_for_eval_unquoted_result= func_quote_for_eval_result= while test 0 -lt $#; do case $1 in *[\\\`\"\$]*) _G_unquoted_arg=`printf '%s\n' "$1" |$SED "$sed_quote_subst"` ;; *) _G_unquoted_arg=$1 ;; esac if test -n "$func_quote_for_eval_unquoted_result"; then func_append func_quote_for_eval_unquoted_result " $_G_unquoted_arg" else func_append func_quote_for_eval_unquoted_result "$_G_unquoted_arg" fi case $_G_unquoted_arg in # Double-quote args containing shell metacharacters to delay # word splitting, command substitution and variable expansion # for a subsequent eval. # Many Bourne shells cannot handle close brackets correctly # in scan sets, so we specify it separately. *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") _G_quoted_arg=\"$_G_unquoted_arg\" ;; *) _G_quoted_arg=$_G_unquoted_arg ;; esac if test -n "$func_quote_for_eval_result"; then func_append func_quote_for_eval_result " $_G_quoted_arg" else func_append func_quote_for_eval_result "$_G_quoted_arg" fi shift done } # func_quote_for_expand ARG # ------------------------- # Aesthetically quote ARG to be evaled later; same as above, # but do not quote variable references. func_quote_for_expand () { $debug_cmd case $1 in *[\\\`\"]*) _G_arg=`$ECHO "$1" | $SED \ -e "$sed_double_quote_subst" -e "$sed_double_backslash"` ;; *) _G_arg=$1 ;; esac case $_G_arg in # Double-quote args containing shell metacharacters to delay # word splitting and command substitution for a subsequent eval. # Many Bourne shells cannot handle close brackets correctly # in scan sets, so we specify it separately. *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") _G_arg=\"$_G_arg\" ;; esac func_quote_for_expand_result=$_G_arg } # func_stripname PREFIX SUFFIX NAME # --------------------------------- # strip PREFIX and SUFFIX from NAME, and store in func_stripname_result. # PREFIX and SUFFIX must not contain globbing or regex special # characters, hashes, percent signs, but SUFFIX may contain a leading # dot (in which case that matches only a dot). if test yes = "$_G_HAVE_XSI_OPS"; then eval 'func_stripname () { $debug_cmd # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are # positional parameters, so assign one to ordinary variable first. func_stripname_result=$3 func_stripname_result=${func_stripname_result#"$1"} func_stripname_result=${func_stripname_result%"$2"} }' else func_stripname () { $debug_cmd case $2 in .*) func_stripname_result=`$ECHO "$3" | $SED -e "s%^$1%%" -e "s%\\\\$2\$%%"`;; *) func_stripname_result=`$ECHO "$3" | $SED -e "s%^$1%%" -e "s%$2\$%%"`;; esac } fi # func_show_eval CMD [FAIL_EXP] # ----------------------------- # Unless opt_quiet is true, then output CMD. Then, if opt_dryrun is # not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP # is given, then evaluate it. func_show_eval () { $debug_cmd _G_cmd=$1 _G_fail_exp=${2-':'} func_quote_for_expand "$_G_cmd" eval "func_notquiet $func_quote_for_expand_result" $opt_dry_run || { eval "$_G_cmd" _G_status=$? if test 0 -ne "$_G_status"; then eval "(exit $_G_status); $_G_fail_exp" fi } } # func_show_eval_locale CMD [FAIL_EXP] # ------------------------------------ # Unless opt_quiet is true, then output CMD. Then, if opt_dryrun is # not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP # is given, then evaluate it. Use the saved locale for evaluation. func_show_eval_locale () { $debug_cmd _G_cmd=$1 _G_fail_exp=${2-':'} $opt_quiet || { func_quote_for_expand "$_G_cmd" eval "func_echo $func_quote_for_expand_result" } $opt_dry_run || { eval "$_G_user_locale $_G_cmd" _G_status=$? eval "$_G_safe_locale" if test 0 -ne "$_G_status"; then eval "(exit $_G_status); $_G_fail_exp" fi } } # func_tr_sh # ---------- # Turn $1 into a string suitable for a shell variable name. # Result is stored in $func_tr_sh_result. All characters # not in the set a-zA-Z0-9_ are replaced with '_'. Further, # if $1 begins with a digit, a '_' is prepended as well. func_tr_sh () { $debug_cmd case $1 in [0-9]* | *[!a-zA-Z0-9_]*) func_tr_sh_result=`$ECHO "$1" | $SED -e 's/^\([0-9]\)/_\1/' -e 's/[^a-zA-Z0-9_]/_/g'` ;; * ) func_tr_sh_result=$1 ;; esac } # func_verbose ARG... # ------------------- # Echo program name prefixed message in verbose mode only. func_verbose () { $debug_cmd $opt_verbose && func_echo "$*" : } # func_warn_and_continue ARG... # ----------------------------- # Echo program name prefixed warning message to standard error. func_warn_and_continue () { $debug_cmd $require_term_colors func_echo_infix_1 "${tc_red}warning$tc_reset" "$*" >&2 } # func_warning CATEGORY ARG... # ---------------------------- # Echo program name prefixed warning message to standard error. Warning # messages can be filtered according to CATEGORY, where this function # elides messages where CATEGORY is not listed in the global variable # 'opt_warning_types'. func_warning () { $debug_cmd # CATEGORY must be in the warning_categories list! case " $warning_categories " in *" $1 "*) ;; *) func_internal_error "invalid warning category '$1'" ;; esac _G_category=$1 shift case " $opt_warning_types " in *" $_G_category "*) $warning_func ${1+"$@"} ;; esac } # func_sort_ver VER1 VER2 # ----------------------- # 'sort -V' is not generally available. # Note this deviates from the version comparison in automake # in that it treats 1.5 < 1.5.0, and treats 1.4.4a < 1.4-p3a # but this should suffice as we won't be specifying old # version formats or redundant trailing .0 in bootstrap.conf. # If we did want full compatibility then we should probably # use m4_version_compare from autoconf. func_sort_ver () { $debug_cmd printf '%s\n%s\n' "$1" "$2" \ | sort -t. -k 1,1n -k 2,2n -k 3,3n -k 4,4n -k 5,5n -k 6,6n -k 7,7n -k 8,8n -k 9,9n } # func_lt_ver PREV CURR # --------------------- # Return true if PREV and CURR are in the correct order according to # func_sort_ver, otherwise false. Use it like this: # # func_lt_ver "$prev_ver" "$proposed_ver" || func_fatal_error "..." func_lt_ver () { $debug_cmd test "x$1" = x`func_sort_ver "$1" "$2" | $SED 1q` } # Local variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-pattern: "10/scriptversion=%:y-%02m-%02d.%02H; # UTC" # time-stamp-time-zone: "UTC" # End: #! /bin/sh # Set a version string for this script. scriptversion=2015-10-07.11; # UTC # A portable, pluggable option parser for Bourne shell. # Written by Gary V. Vaughan, 2010 # Copyright (C) 2010-2015 Free Software Foundation, Inc. # This is free software; see the source for copying conditions. There is NO # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # Please report bugs or propose patches to gary@gnu.org. ## ------ ## ## Usage. ## ## ------ ## # This file is a library for parsing options in your shell scripts along # with assorted other useful supporting features that you can make use # of too. # # For the simplest scripts you might need only: # # #!/bin/sh # . relative/path/to/funclib.sh # . relative/path/to/options-parser # scriptversion=1.0 # func_options ${1+"$@"} # eval set dummy "$func_options_result"; shift # ...rest of your script... # # In order for the '--version' option to work, you will need to have a # suitably formatted comment like the one at the top of this file # starting with '# Written by ' and ending with '# warranty; '. # # For '-h' and '--help' to work, you will also need a one line # description of your script's purpose in a comment directly above the # '# Written by ' line, like the one at the top of this file. # # The default options also support '--debug', which will turn on shell # execution tracing (see the comment above debug_cmd below for another # use), and '--verbose' and the func_verbose function to allow your script # to display verbose messages only when your user has specified # '--verbose'. # # After sourcing this file, you can plug processing for additional # options by amending the variables from the 'Configuration' section # below, and following the instructions in the 'Option parsing' # section further down. ## -------------- ## ## Configuration. ## ## -------------- ## # You should override these variables in your script after sourcing this # file so that they reflect the customisations you have added to the # option parser. # The usage line for option parsing errors and the start of '-h' and # '--help' output messages. You can embed shell variables for delayed # expansion at the time the message is displayed, but you will need to # quote other shell meta-characters carefully to prevent them being # expanded when the contents are evaled. usage='$progpath [OPTION]...' # Short help message in response to '-h' and '--help'. Add to this or # override it after sourcing this library to reflect the full set of # options your script accepts. usage_message="\ --debug enable verbose shell tracing -W, --warnings=CATEGORY report the warnings falling in CATEGORY [all] -v, --verbose verbosely report processing --version print version information and exit -h, --help print short or long help message and exit " # Additional text appended to 'usage_message' in response to '--help'. long_help_message=" Warning categories include: 'all' show all warnings 'none' turn off all the warnings 'error' warnings are treated as fatal errors" # Help message printed before fatal option parsing errors. fatal_help="Try '\$progname --help' for more information." ## ------------------------- ## ## Hook function management. ## ## ------------------------- ## # This section contains functions for adding, removing, and running hooks # to the main code. A hook is just a named list of of function, that can # be run in order later on. # func_hookable FUNC_NAME # ----------------------- # Declare that FUNC_NAME will run hooks added with # 'func_add_hook FUNC_NAME ...'. func_hookable () { $debug_cmd func_append hookable_fns " $1" } # func_add_hook FUNC_NAME HOOK_FUNC # --------------------------------- # Request that FUNC_NAME call HOOK_FUNC before it returns. FUNC_NAME must # first have been declared "hookable" by a call to 'func_hookable'. func_add_hook () { $debug_cmd case " $hookable_fns " in *" $1 "*) ;; *) func_fatal_error "'$1' does not accept hook functions." ;; esac eval func_append ${1}_hooks '" $2"' } # func_remove_hook FUNC_NAME HOOK_FUNC # ------------------------------------ # Remove HOOK_FUNC from the list of functions called by FUNC_NAME. func_remove_hook () { $debug_cmd eval ${1}_hooks='`$ECHO "\$'$1'_hooks" |$SED "s| '$2'||"`' } # func_run_hooks FUNC_NAME [ARG]... # --------------------------------- # Run all hook functions registered to FUNC_NAME. # It is assumed that the list of hook functions contains nothing more # than a whitespace-delimited list of legal shell function names, and # no effort is wasted trying to catch shell meta-characters or preserve # whitespace. func_run_hooks () { $debug_cmd _G_rc_run_hooks=false case " $hookable_fns " in *" $1 "*) ;; *) func_fatal_error "'$1' does not support hook funcions.n" ;; esac eval _G_hook_fns=\$$1_hooks; shift for _G_hook in $_G_hook_fns; do if eval $_G_hook '"$@"'; then # store returned options list back into positional # parameters for next 'cmd' execution. eval _G_hook_result=\$${_G_hook}_result eval set dummy "$_G_hook_result"; shift _G_rc_run_hooks=: fi done $_G_rc_run_hooks && func_run_hooks_result=$_G_hook_result } ## --------------- ## ## Option parsing. ## ## --------------- ## # In order to add your own option parsing hooks, you must accept the # full positional parameter list in your hook function, you may remove/edit # any options that you action, and then pass back the remaining unprocessed # options in '_result', escaped suitably for # 'eval'. In this case you also must return $EXIT_SUCCESS to let the # hook's caller know that it should pay attention to # '_result'. Returning $EXIT_FAILURE signalizes that # arguments are left untouched by the hook and therefore caller will ignore the # result variable. # # Like this: # # my_options_prep () # { # $debug_cmd # # # Extend the existing usage message. # usage_message=$usage_message' # -s, --silent don'\''t print informational messages # ' # # No change in '$@' (ignored completely by this hook). There is # # no need to do the equivalent (but slower) action: # # func_quote_for_eval ${1+"$@"} # # my_options_prep_result=$func_quote_for_eval_result # false # } # func_add_hook func_options_prep my_options_prep # # # my_silent_option () # { # $debug_cmd # # args_changed=false # # # Note that for efficiency, we parse as many options as we can # # recognise in a loop before passing the remainder back to the # # caller on the first unrecognised argument we encounter. # while test $# -gt 0; do # opt=$1; shift # case $opt in # --silent|-s) opt_silent=: # args_changed=: # ;; # # Separate non-argument short options: # -s*) func_split_short_opt "$_G_opt" # set dummy "$func_split_short_opt_name" \ # "-$func_split_short_opt_arg" ${1+"$@"} # shift # args_changed=: # ;; # *) # Make sure the first unrecognised option "$_G_opt" # # is added back to "$@", we could need that later # # if $args_changed is true. # set dummy "$_G_opt" ${1+"$@"}; shift; break ;; # esac # done # # if $args_changed; then # func_quote_for_eval ${1+"$@"} # my_silent_option_result=$func_quote_for_eval_result # fi # # $args_changed # } # func_add_hook func_parse_options my_silent_option # # # my_option_validation () # { # $debug_cmd # # $opt_silent && $opt_verbose && func_fatal_help "\ # '--silent' and '--verbose' options are mutually exclusive." # # false # } # func_add_hook func_validate_options my_option_validation # # You'll also need to manually amend $usage_message to reflect the extra # options you parse. It's preferable to append if you can, so that # multiple option parsing hooks can be added safely. # func_options_finish [ARG]... # ---------------------------- # Finishing the option parse loop (call 'func_options' hooks ATM). func_options_finish () { $debug_cmd _G_func_options_finish_exit=false if func_run_hooks func_options ${1+"$@"}; then func_options_finish_result=$func_run_hooks_result _G_func_options_finish_exit=: fi $_G_func_options_finish_exit } # func_options [ARG]... # --------------------- # All the functions called inside func_options are hookable. See the # individual implementations for details. func_hookable func_options func_options () { $debug_cmd _G_rc_options=false for my_func in options_prep parse_options validate_options options_finish do if eval func_$my_func '${1+"$@"}'; then eval _G_res_var='$'"func_${my_func}_result" eval set dummy "$_G_res_var" ; shift _G_rc_options=: fi done # Save modified positional parameters for caller. As a top-level # options-parser function we always need to set the 'func_options_result' # variable (regardless the $_G_rc_options value). if $_G_rc_options; then func_options_result=$_G_res_var else func_quote_for_eval ${1+"$@"} func_options_result=$func_quote_for_eval_result fi $_G_rc_options } # func_options_prep [ARG]... # -------------------------- # All initialisations required before starting the option parse loop. # Note that when calling hook functions, we pass through the list of # positional parameters. If a hook function modifies that list, and # needs to propagate that back to rest of this script, then the complete # modified list must be put in 'func_run_hooks_result' before # returning $EXIT_SUCCESS (otherwise $EXIT_FAILURE is returned). func_hookable func_options_prep func_options_prep () { $debug_cmd # Option defaults: opt_verbose=false opt_warning_types= _G_rc_options_prep=false if func_run_hooks func_options_prep ${1+"$@"}; then _G_rc_options_prep=: # save modified positional parameters for caller func_options_prep_result=$func_run_hooks_result fi $_G_rc_options_prep } # func_parse_options [ARG]... # --------------------------- # The main option parsing loop. func_hookable func_parse_options func_parse_options () { $debug_cmd func_parse_options_result= _G_rc_parse_options=false # this just eases exit handling while test $# -gt 0; do # Defer to hook functions for initial option parsing, so they # get priority in the event of reusing an option name. if func_run_hooks func_parse_options ${1+"$@"}; then eval set dummy "$func_run_hooks_result"; shift _G_rc_parse_options=: fi # Break out of the loop if we already parsed every option. test $# -gt 0 || break _G_match_parse_options=: _G_opt=$1 shift case $_G_opt in --debug|-x) debug_cmd='set -x' func_echo "enabling shell trace mode" $debug_cmd ;; --no-warnings|--no-warning|--no-warn) set dummy --warnings none ${1+"$@"} shift ;; --warnings|--warning|-W) if test $# = 0 && func_missing_arg $_G_opt; then _G_rc_parse_options=: break fi case " $warning_categories $1" in *" $1 "*) # trailing space prevents matching last $1 above func_append_uniq opt_warning_types " $1" ;; *all) opt_warning_types=$warning_categories ;; *none) opt_warning_types=none warning_func=: ;; *error) opt_warning_types=$warning_categories warning_func=func_fatal_error ;; *) func_fatal_error \ "unsupported warning category: '$1'" ;; esac shift ;; --verbose|-v) opt_verbose=: ;; --version) func_version ;; -\?|-h) func_usage ;; --help) func_help ;; # Separate optargs to long options (plugins may need this): --*=*) func_split_equals "$_G_opt" set dummy "$func_split_equals_lhs" \ "$func_split_equals_rhs" ${1+"$@"} shift ;; # Separate optargs to short options: -W*) func_split_short_opt "$_G_opt" set dummy "$func_split_short_opt_name" \ "$func_split_short_opt_arg" ${1+"$@"} shift ;; # Separate non-argument short options: -\?*|-h*|-v*|-x*) func_split_short_opt "$_G_opt" set dummy "$func_split_short_opt_name" \ "-$func_split_short_opt_arg" ${1+"$@"} shift ;; --) _G_rc_parse_options=: ; break ;; -*) func_fatal_help "unrecognised option: '$_G_opt'" ;; *) set dummy "$_G_opt" ${1+"$@"}; shift _G_match_parse_options=false break ;; esac $_G_match_parse_options && _G_rc_parse_options=: done if $_G_rc_parse_options; then # save modified positional parameters for caller func_quote_for_eval ${1+"$@"} func_parse_options_result=$func_quote_for_eval_result fi $_G_rc_parse_options } # func_validate_options [ARG]... # ------------------------------ # Perform any sanity checks on option settings and/or unconsumed # arguments. func_hookable func_validate_options func_validate_options () { $debug_cmd _G_rc_validate_options=false # Display all warnings if -W was not given. test -n "$opt_warning_types" || opt_warning_types=" $warning_categories" if func_run_hooks func_validate_options ${1+"$@"}; then # save modified positional parameters for caller func_validate_options_result=$func_run_hooks_result _G_rc_validate_options=: fi # Bail if the options were screwed! $exit_cmd $EXIT_FAILURE $_G_rc_validate_options } ## ----------------- ## ## Helper functions. ## ## ----------------- ## # This section contains the helper functions used by the rest of the # hookable option parser framework in ascii-betical order. # func_fatal_help ARG... # ---------------------- # Echo program name prefixed message to standard error, followed by # a help hint, and exit. func_fatal_help () { $debug_cmd eval \$ECHO \""Usage: $usage"\" eval \$ECHO \""$fatal_help"\" func_error ${1+"$@"} exit $EXIT_FAILURE } # func_help # --------- # Echo long help message to standard output and exit. func_help () { $debug_cmd func_usage_message $ECHO "$long_help_message" exit 0 } # func_missing_arg ARGNAME # ------------------------ # Echo program name prefixed message to standard error and set global # exit_cmd. func_missing_arg () { $debug_cmd func_error "Missing argument for '$1'." exit_cmd=exit } # func_split_equals STRING # ------------------------ # Set func_split_equals_lhs and func_split_equals_rhs shell variables after # splitting STRING at the '=' sign. test -z "$_G_HAVE_XSI_OPS" \ && (eval 'x=a/b/c; test 5aa/bb/cc = "${#x}${x%%/*}${x%/*}${x#*/}${x##*/}"') 2>/dev/null \ && _G_HAVE_XSI_OPS=yes if test yes = "$_G_HAVE_XSI_OPS" then # This is an XSI compatible shell, allowing a faster implementation... eval 'func_split_equals () { $debug_cmd func_split_equals_lhs=${1%%=*} func_split_equals_rhs=${1#*=} test "x$func_split_equals_lhs" = "x$1" \ && func_split_equals_rhs= }' else # ...otherwise fall back to using expr, which is often a shell builtin. func_split_equals () { $debug_cmd func_split_equals_lhs=`expr "x$1" : 'x\([^=]*\)'` func_split_equals_rhs= test "x$func_split_equals_lhs" = "x$1" \ || func_split_equals_rhs=`expr "x$1" : 'x[^=]*=\(.*\)$'` } fi #func_split_equals # func_split_short_opt SHORTOPT # ----------------------------- # Set func_split_short_opt_name and func_split_short_opt_arg shell # variables after splitting SHORTOPT after the 2nd character. if test yes = "$_G_HAVE_XSI_OPS" then # This is an XSI compatible shell, allowing a faster implementation... eval 'func_split_short_opt () { $debug_cmd func_split_short_opt_arg=${1#??} func_split_short_opt_name=${1%"$func_split_short_opt_arg"} }' else # ...otherwise fall back to using expr, which is often a shell builtin. func_split_short_opt () { $debug_cmd func_split_short_opt_name=`expr "x$1" : 'x-\(.\)'` func_split_short_opt_arg=`expr "x$1" : 'x-.\(.*\)$'` } fi #func_split_short_opt # func_usage # ---------- # Echo short help message to standard output and exit. func_usage () { $debug_cmd func_usage_message $ECHO "Run '$progname --help |${PAGER-more}' for full usage" exit 0 } # func_usage_message # ------------------ # Echo short help message to standard output. func_usage_message () { $debug_cmd eval \$ECHO \""Usage: $usage"\" echo $SED -n 's|^# || /^Written by/{ x;p;x } h /^Written by/q' < "$progpath" echo eval \$ECHO \""$usage_message"\" } # func_version # ------------ # Echo version message to standard output and exit. func_version () { $debug_cmd printf '%s\n' "$progname $scriptversion" $SED -n ' /(C)/!b go :more /\./!{ N s|\n# | | b more } :go /^# Written by /,/# warranty; / { s|^# || s|^# *$|| s|\((C)\)[ 0-9,-]*[ ,-]\([1-9][0-9]* \)|\1 \2| p } /^# Written by / { s|^# || p } /^warranty; /q' < "$progpath" exit $? } # Local variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-pattern: "10/scriptversion=%:y-%02m-%02d.%02H; # UTC" # time-stamp-time-zone: "UTC" # End: # Set a version string. scriptversion='(GNU libtool) 2.4.6' # func_echo ARG... # ---------------- # Libtool also displays the current mode in messages, so override # funclib.sh func_echo with this custom definition. func_echo () { $debug_cmd _G_message=$* func_echo_IFS=$IFS IFS=$nl for _G_line in $_G_message; do IFS=$func_echo_IFS $ECHO "$progname${opt_mode+: $opt_mode}: $_G_line" done IFS=$func_echo_IFS } # func_warning ARG... # ------------------- # Libtool warnings are not categorized, so override funclib.sh # func_warning with this simpler definition. func_warning () { $debug_cmd $warning_func ${1+"$@"} } ## ---------------- ## ## Options parsing. ## ## ---------------- ## # Hook in the functions to make sure our own options are parsed during # the option parsing loop. usage='$progpath [OPTION]... [MODE-ARG]...' # Short help message in response to '-h'. usage_message="Options: --config show all configuration variables --debug enable verbose shell tracing -n, --dry-run display commands without modifying any files --features display basic configuration information and exit --mode=MODE use operation mode MODE --no-warnings equivalent to '-Wnone' --preserve-dup-deps don't remove duplicate dependency libraries --quiet, --silent don't print informational messages --tag=TAG use configuration variables from tag TAG -v, --verbose print more informational messages than default --version print version information -W, --warnings=CATEGORY report the warnings falling in CATEGORY [all] -h, --help, --help-all print short, long, or detailed help message " # Additional text appended to 'usage_message' in response to '--help'. func_help () { $debug_cmd func_usage_message $ECHO "$long_help_message MODE must be one of the following: clean remove files from the build directory compile compile a source file into a libtool object execute automatically set library path, then run a program finish complete the installation of libtool libraries install install libraries or executables link create a library or an executable uninstall remove libraries from an installed directory MODE-ARGS vary depending on the MODE. When passed as first option, '--mode=MODE' may be abbreviated as 'MODE' or a unique abbreviation of that. Try '$progname --help --mode=MODE' for a more detailed description of MODE. When reporting a bug, please describe a test case to reproduce it and include the following information: host-triplet: $host shell: $SHELL compiler: $LTCC compiler flags: $LTCFLAGS linker: $LD (gnu? $with_gnu_ld) version: $progname $scriptversion Debian-2.4.6-15 automake: `($AUTOMAKE --version) 2>/dev/null |$SED 1q` autoconf: `($AUTOCONF --version) 2>/dev/null |$SED 1q` Report bugs to . GNU libtool home page: . General help using GNU software: ." exit 0 } # func_lo2o OBJECT-NAME # --------------------- # Transform OBJECT-NAME from a '.lo' suffix to the platform specific # object suffix. lo2o=s/\\.lo\$/.$objext/ o2lo=s/\\.$objext\$/.lo/ if test yes = "$_G_HAVE_XSI_OPS"; then eval 'func_lo2o () { case $1 in *.lo) func_lo2o_result=${1%.lo}.$objext ;; * ) func_lo2o_result=$1 ;; esac }' # func_xform LIBOBJ-OR-SOURCE # --------------------------- # Transform LIBOBJ-OR-SOURCE from a '.o' or '.c' (or otherwise) # suffix to a '.lo' libtool-object suffix. eval 'func_xform () { func_xform_result=${1%.*}.lo }' else # ...otherwise fall back to using sed. func_lo2o () { func_lo2o_result=`$ECHO "$1" | $SED "$lo2o"` } func_xform () { func_xform_result=`$ECHO "$1" | $SED 's|\.[^.]*$|.lo|'` } fi # func_fatal_configuration ARG... # ------------------------------- # Echo program name prefixed message to standard error, followed by # a configuration failure hint, and exit. func_fatal_configuration () { func__fatal_error ${1+"$@"} \ "See the $PACKAGE documentation for more information." \ "Fatal configuration error." } # func_config # ----------- # Display the configuration for all the tags in this script. func_config () { re_begincf='^# ### BEGIN LIBTOOL' re_endcf='^# ### END LIBTOOL' # Default configuration. $SED "1,/$re_begincf CONFIG/d;/$re_endcf CONFIG/,\$d" < "$progpath" # Now print the configurations for the tags. for tagname in $taglist; do $SED -n "/$re_begincf TAG CONFIG: $tagname\$/,/$re_endcf TAG CONFIG: $tagname\$/p" < "$progpath" done exit $? } # func_features # ------------- # Display the features supported by this script. func_features () { echo "host: $host" if test yes = "$build_libtool_libs"; then echo "enable shared libraries" else echo "disable shared libraries" fi if test yes = "$build_old_libs"; then echo "enable static libraries" else echo "disable static libraries" fi exit $? } # func_enable_tag TAGNAME # ----------------------- # Verify that TAGNAME is valid, and either flag an error and exit, or # enable the TAGNAME tag. We also add TAGNAME to the global $taglist # variable here. func_enable_tag () { # Global variable: tagname=$1 re_begincf="^# ### BEGIN LIBTOOL TAG CONFIG: $tagname\$" re_endcf="^# ### END LIBTOOL TAG CONFIG: $tagname\$" sed_extractcf=/$re_begincf/,/$re_endcf/p # Validate tagname. case $tagname in *[!-_A-Za-z0-9,/]*) func_fatal_error "invalid tag name: $tagname" ;; esac # Don't test for the "default" C tag, as we know it's # there but not specially marked. case $tagname in CC) ;; *) if $GREP "$re_begincf" "$progpath" >/dev/null 2>&1; then taglist="$taglist $tagname" # Evaluate the configuration. Be careful to quote the path # and the sed script, to avoid splitting on whitespace, but # also don't use non-portable quotes within backquotes within # quotes we have to do it in 2 steps: extractedcf=`$SED -n -e "$sed_extractcf" < "$progpath"` eval "$extractedcf" else func_error "ignoring unknown tag $tagname" fi ;; esac } # func_check_version_match # ------------------------ # Ensure that we are using m4 macros, and libtool script from the same # release of libtool. func_check_version_match () { if test "$package_revision" != "$macro_revision"; then if test "$VERSION" != "$macro_version"; then if test -z "$macro_version"; then cat >&2 <<_LT_EOF $progname: Version mismatch error. This is $PACKAGE $VERSION, but the $progname: definition of this LT_INIT comes from an older release. $progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION $progname: and run autoconf again. _LT_EOF else cat >&2 <<_LT_EOF $progname: Version mismatch error. This is $PACKAGE $VERSION, but the $progname: definition of this LT_INIT comes from $PACKAGE $macro_version. $progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION $progname: and run autoconf again. _LT_EOF fi else cat >&2 <<_LT_EOF $progname: Version mismatch error. This is $PACKAGE $VERSION, revision $package_revision, $progname: but the definition of this LT_INIT comes from revision $macro_revision. $progname: You should recreate aclocal.m4 with macros from revision $package_revision $progname: of $PACKAGE $VERSION and run autoconf again. _LT_EOF fi exit $EXIT_MISMATCH fi } # libtool_options_prep [ARG]... # ----------------------------- # Preparation for options parsed by libtool. libtool_options_prep () { $debug_mode # Option defaults: opt_config=false opt_dlopen= opt_dry_run=false opt_help=false opt_mode= opt_preserve_dup_deps=false opt_quiet=false nonopt= preserve_args= _G_rc_lt_options_prep=: # Shorthand for --mode=foo, only valid as the first argument case $1 in clean|clea|cle|cl) shift; set dummy --mode clean ${1+"$@"}; shift ;; compile|compil|compi|comp|com|co|c) shift; set dummy --mode compile ${1+"$@"}; shift ;; execute|execut|execu|exec|exe|ex|e) shift; set dummy --mode execute ${1+"$@"}; shift ;; finish|finis|fini|fin|fi|f) shift; set dummy --mode finish ${1+"$@"}; shift ;; install|instal|insta|inst|ins|in|i) shift; set dummy --mode install ${1+"$@"}; shift ;; link|lin|li|l) shift; set dummy --mode link ${1+"$@"}; shift ;; uninstall|uninstal|uninsta|uninst|unins|unin|uni|un|u) shift; set dummy --mode uninstall ${1+"$@"}; shift ;; *) _G_rc_lt_options_prep=false ;; esac if $_G_rc_lt_options_prep; then # Pass back the list of options. func_quote_for_eval ${1+"$@"} libtool_options_prep_result=$func_quote_for_eval_result fi $_G_rc_lt_options_prep } func_add_hook func_options_prep libtool_options_prep # libtool_parse_options [ARG]... # --------------------------------- # Provide handling for libtool specific options. libtool_parse_options () { $debug_cmd _G_rc_lt_parse_options=false # Perform our own loop to consume as many options as possible in # each iteration. while test $# -gt 0; do _G_match_lt_parse_options=: _G_opt=$1 shift case $_G_opt in --dry-run|--dryrun|-n) opt_dry_run=: ;; --config) func_config ;; --dlopen|-dlopen) opt_dlopen="${opt_dlopen+$opt_dlopen }$1" shift ;; --preserve-dup-deps) opt_preserve_dup_deps=: ;; --features) func_features ;; --finish) set dummy --mode finish ${1+"$@"}; shift ;; --help) opt_help=: ;; --help-all) opt_help=': help-all' ;; --mode) test $# = 0 && func_missing_arg $_G_opt && break opt_mode=$1 case $1 in # Valid mode arguments: clean|compile|execute|finish|install|link|relink|uninstall) ;; # Catch anything else as an error *) func_error "invalid argument for $_G_opt" exit_cmd=exit break ;; esac shift ;; --no-silent|--no-quiet) opt_quiet=false func_append preserve_args " $_G_opt" ;; --no-warnings|--no-warning|--no-warn) opt_warning=false func_append preserve_args " $_G_opt" ;; --no-verbose) opt_verbose=false func_append preserve_args " $_G_opt" ;; --silent|--quiet) opt_quiet=: opt_verbose=false func_append preserve_args " $_G_opt" ;; --tag) test $# = 0 && func_missing_arg $_G_opt && break opt_tag=$1 func_append preserve_args " $_G_opt $1" func_enable_tag "$1" shift ;; --verbose|-v) opt_quiet=false opt_verbose=: func_append preserve_args " $_G_opt" ;; # An option not handled by this hook function: *) set dummy "$_G_opt" ${1+"$@"} ; shift _G_match_lt_parse_options=false break ;; esac $_G_match_lt_parse_options && _G_rc_lt_parse_options=: done if $_G_rc_lt_parse_options; then # save modified positional parameters for caller func_quote_for_eval ${1+"$@"} libtool_parse_options_result=$func_quote_for_eval_result fi $_G_rc_lt_parse_options } func_add_hook func_parse_options libtool_parse_options # libtool_validate_options [ARG]... # --------------------------------- # Perform any sanity checks on option settings and/or unconsumed # arguments. libtool_validate_options () { # save first non-option argument if test 0 -lt $#; then nonopt=$1 shift fi # preserve --debug test : = "$debug_cmd" || func_append preserve_args " --debug" case $host in # Solaris2 added to fix http://debbugs.gnu.org/cgi/bugreport.cgi?bug=16452 # see also: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=59788 *cygwin* | *mingw* | *pw32* | *cegcc* | *solaris2* | *os2*) # don't eliminate duplications in $postdeps and $predeps opt_duplicate_compiler_generated_deps=: ;; *) opt_duplicate_compiler_generated_deps=$opt_preserve_dup_deps ;; esac $opt_help || { # Sanity checks first: func_check_version_match test yes != "$build_libtool_libs" \ && test yes != "$build_old_libs" \ && func_fatal_configuration "not configured to build any kind of library" # Darwin sucks eval std_shrext=\"$shrext_cmds\" # Only execute mode is allowed to have -dlopen flags. if test -n "$opt_dlopen" && test execute != "$opt_mode"; then func_error "unrecognized option '-dlopen'" $ECHO "$help" 1>&2 exit $EXIT_FAILURE fi # Change the help message to a mode-specific one. generic_help=$help help="Try '$progname --help --mode=$opt_mode' for more information." } # Pass back the unparsed argument list func_quote_for_eval ${1+"$@"} libtool_validate_options_result=$func_quote_for_eval_result } func_add_hook func_validate_options libtool_validate_options # Process options as early as possible so that --help and --version # can return quickly. func_options ${1+"$@"} eval set dummy "$func_options_result"; shift ## ----------- ## ## Main. ## ## ----------- ## magic='%%%MAGIC variable%%%' magic_exe='%%%MAGIC EXE variable%%%' # Global variables. extracted_archives= extracted_serial=0 # If this variable is set in any of the actions, the command in it # will be execed at the end. This prevents here-documents from being # left over by shells. exec_cmd= # A function that is used when there is no print builtin or printf. func_fallback_echo () { eval 'cat <<_LTECHO_EOF $1 _LTECHO_EOF' } # func_generated_by_libtool # True iff stdin has been generated by Libtool. This function is only # a basic sanity check; it will hardly flush out determined imposters. func_generated_by_libtool_p () { $GREP "^# Generated by .*$PACKAGE" > /dev/null 2>&1 } # func_lalib_p file # True iff FILE is a libtool '.la' library or '.lo' object file. # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_lalib_p () { test -f "$1" && $SED -e 4q "$1" 2>/dev/null | func_generated_by_libtool_p } # func_lalib_unsafe_p file # True iff FILE is a libtool '.la' library or '.lo' object file. # This function implements the same check as func_lalib_p without # resorting to external programs. To this end, it redirects stdin and # closes it afterwards, without saving the original file descriptor. # As a safety measure, use it only where a negative result would be # fatal anyway. Works if 'file' does not exist. func_lalib_unsafe_p () { lalib_p=no if test -f "$1" && test -r "$1" && exec 5<&0 <"$1"; then for lalib_p_l in 1 2 3 4 do read lalib_p_line case $lalib_p_line in \#\ Generated\ by\ *$PACKAGE* ) lalib_p=yes; break;; esac done exec 0<&5 5<&- fi test yes = "$lalib_p" } # func_ltwrapper_script_p file # True iff FILE is a libtool wrapper script # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_ltwrapper_script_p () { test -f "$1" && $lt_truncate_bin < "$1" 2>/dev/null | func_generated_by_libtool_p } # func_ltwrapper_executable_p file # True iff FILE is a libtool wrapper executable # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_ltwrapper_executable_p () { func_ltwrapper_exec_suffix= case $1 in *.exe) ;; *) func_ltwrapper_exec_suffix=.exe ;; esac $GREP "$magic_exe" "$1$func_ltwrapper_exec_suffix" >/dev/null 2>&1 } # func_ltwrapper_scriptname file # Assumes file is an ltwrapper_executable # uses $file to determine the appropriate filename for a # temporary ltwrapper_script. func_ltwrapper_scriptname () { func_dirname_and_basename "$1" "" "." func_stripname '' '.exe' "$func_basename_result" func_ltwrapper_scriptname_result=$func_dirname_result/$objdir/${func_stripname_result}_ltshwrapper } # func_ltwrapper_p file # True iff FILE is a libtool wrapper script or wrapper executable # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_ltwrapper_p () { func_ltwrapper_script_p "$1" || func_ltwrapper_executable_p "$1" } # func_execute_cmds commands fail_cmd # Execute tilde-delimited COMMANDS. # If FAIL_CMD is given, eval that upon failure. # FAIL_CMD may read-access the current command in variable CMD! func_execute_cmds () { $debug_cmd save_ifs=$IFS; IFS='~' for cmd in $1; do IFS=$sp$nl eval cmd=\"$cmd\" IFS=$save_ifs func_show_eval "$cmd" "${2-:}" done IFS=$save_ifs } # func_source file # Source FILE, adding directory component if necessary. # Note that it is not necessary on cygwin/mingw to append a dot to # FILE even if both FILE and FILE.exe exist: automatic-append-.exe # behavior happens only for exec(3), not for open(2)! Also, sourcing # 'FILE.' does not work on cygwin managed mounts. func_source () { $debug_cmd case $1 in */* | *\\*) . "$1" ;; *) . "./$1" ;; esac } # func_resolve_sysroot PATH # Replace a leading = in PATH with a sysroot. Store the result into # func_resolve_sysroot_result func_resolve_sysroot () { func_resolve_sysroot_result=$1 case $func_resolve_sysroot_result in =*) func_stripname '=' '' "$func_resolve_sysroot_result" func_resolve_sysroot_result=$lt_sysroot$func_stripname_result ;; esac } # func_replace_sysroot PATH # If PATH begins with the sysroot, replace it with = and # store the result into func_replace_sysroot_result. func_replace_sysroot () { case $lt_sysroot:$1 in ?*:"$lt_sysroot"*) func_stripname "$lt_sysroot" '' "$1" func_replace_sysroot_result='='$func_stripname_result ;; *) # Including no sysroot. func_replace_sysroot_result=$1 ;; esac } # func_infer_tag arg # Infer tagged configuration to use if any are available and # if one wasn't chosen via the "--tag" command line option. # Only attempt this if the compiler in the base compile # command doesn't match the default compiler. # arg is usually of the form 'gcc ...' func_infer_tag () { $debug_cmd if test -n "$available_tags" && test -z "$tagname"; then CC_quoted= for arg in $CC; do func_append_quoted CC_quoted "$arg" done CC_expanded=`func_echo_all $CC` CC_quoted_expanded=`func_echo_all $CC_quoted` case $@ in # Blanks in the command may have been stripped by the calling shell, # but not from the CC environment variable when configure was run. " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \ " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) ;; # Blanks at the start of $base_compile will cause this to fail # if we don't check for them as well. *) for z in $available_tags; do if $GREP "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$progpath" > /dev/null; then # Evaluate the configuration. eval "`$SED -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`" CC_quoted= for arg in $CC; do # Double-quote args containing other shell metacharacters. func_append_quoted CC_quoted "$arg" done CC_expanded=`func_echo_all $CC` CC_quoted_expanded=`func_echo_all $CC_quoted` case "$@ " in " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \ " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) # The compiler in the base compile command matches # the one in the tagged configuration. # Assume this is the tagged configuration we want. tagname=$z break ;; esac fi done # If $tagname still isn't set, then no tagged configuration # was found and let the user know that the "--tag" command # line option must be used. if test -z "$tagname"; then func_echo "unable to infer tagged configuration" func_fatal_error "specify a tag with '--tag'" # else # func_verbose "using $tagname tagged configuration" fi ;; esac fi } # func_write_libtool_object output_name pic_name nonpic_name # Create a libtool object file (analogous to a ".la" file), # but don't create it if we're doing a dry run. func_write_libtool_object () { write_libobj=$1 if test yes = "$build_libtool_libs"; then write_lobj=\'$2\' else write_lobj=none fi if test yes = "$build_old_libs"; then write_oldobj=\'$3\' else write_oldobj=none fi $opt_dry_run || { cat >${write_libobj}T </dev/null` if test "$?" -eq 0 && test -n "$func_convert_core_file_wine_to_w32_tmp"; then func_convert_core_file_wine_to_w32_result=`$ECHO "$func_convert_core_file_wine_to_w32_tmp" | $SED -e "$sed_naive_backslashify"` else func_convert_core_file_wine_to_w32_result= fi fi } # end: func_convert_core_file_wine_to_w32 # func_convert_core_path_wine_to_w32 ARG # Helper function used by path conversion functions when $build is *nix, and # $host is mingw, cygwin, or some other w32 environment. Relies on a correctly # configured wine environment available, with the winepath program in $build's # $PATH. Assumes ARG has no leading or trailing path separator characters. # # ARG is path to be converted from $build format to win32. # Result is available in $func_convert_core_path_wine_to_w32_result. # Unconvertible file (directory) names in ARG are skipped; if no directory names # are convertible, then the result may be empty. func_convert_core_path_wine_to_w32 () { $debug_cmd # unfortunately, winepath doesn't convert paths, only file names func_convert_core_path_wine_to_w32_result= if test -n "$1"; then oldIFS=$IFS IFS=: for func_convert_core_path_wine_to_w32_f in $1; do IFS=$oldIFS func_convert_core_file_wine_to_w32 "$func_convert_core_path_wine_to_w32_f" if test -n "$func_convert_core_file_wine_to_w32_result"; then if test -z "$func_convert_core_path_wine_to_w32_result"; then func_convert_core_path_wine_to_w32_result=$func_convert_core_file_wine_to_w32_result else func_append func_convert_core_path_wine_to_w32_result ";$func_convert_core_file_wine_to_w32_result" fi fi done IFS=$oldIFS fi } # end: func_convert_core_path_wine_to_w32 # func_cygpath ARGS... # Wrapper around calling the cygpath program via LT_CYGPATH. This is used when # when (1) $build is *nix and Cygwin is hosted via a wine environment; or (2) # $build is MSYS and $host is Cygwin, or (3) $build is Cygwin. In case (1) or # (2), returns the Cygwin file name or path in func_cygpath_result (input # file name or path is assumed to be in w32 format, as previously converted # from $build's *nix or MSYS format). In case (3), returns the w32 file name # or path in func_cygpath_result (input file name or path is assumed to be in # Cygwin format). Returns an empty string on error. # # ARGS are passed to cygpath, with the last one being the file name or path to # be converted. # # Specify the absolute *nix (or w32) name to cygpath in the LT_CYGPATH # environment variable; do not put it in $PATH. func_cygpath () { $debug_cmd if test -n "$LT_CYGPATH" && test -f "$LT_CYGPATH"; then func_cygpath_result=`$LT_CYGPATH "$@" 2>/dev/null` if test "$?" -ne 0; then # on failure, ensure result is empty func_cygpath_result= fi else func_cygpath_result= func_error "LT_CYGPATH is empty or specifies non-existent file: '$LT_CYGPATH'" fi } #end: func_cygpath # func_convert_core_msys_to_w32 ARG # Convert file name or path ARG from MSYS format to w32 format. Return # result in func_convert_core_msys_to_w32_result. func_convert_core_msys_to_w32 () { $debug_cmd # awkward: cmd appends spaces to result func_convert_core_msys_to_w32_result=`( cmd //c echo "$1" ) 2>/dev/null | $SED -e 's/[ ]*$//' -e "$sed_naive_backslashify"` } #end: func_convert_core_msys_to_w32 # func_convert_file_check ARG1 ARG2 # Verify that ARG1 (a file name in $build format) was converted to $host # format in ARG2. Otherwise, emit an error message, but continue (resetting # func_to_host_file_result to ARG1). func_convert_file_check () { $debug_cmd if test -z "$2" && test -n "$1"; then func_error "Could not determine host file name corresponding to" func_error " '$1'" func_error "Continuing, but uninstalled executables may not work." # Fallback: func_to_host_file_result=$1 fi } # end func_convert_file_check # func_convert_path_check FROM_PATHSEP TO_PATHSEP FROM_PATH TO_PATH # Verify that FROM_PATH (a path in $build format) was converted to $host # format in TO_PATH. Otherwise, emit an error message, but continue, resetting # func_to_host_file_result to a simplistic fallback value (see below). func_convert_path_check () { $debug_cmd if test -z "$4" && test -n "$3"; then func_error "Could not determine the host path corresponding to" func_error " '$3'" func_error "Continuing, but uninstalled executables may not work." # Fallback. This is a deliberately simplistic "conversion" and # should not be "improved". See libtool.info. if test "x$1" != "x$2"; then lt_replace_pathsep_chars="s|$1|$2|g" func_to_host_path_result=`echo "$3" | $SED -e "$lt_replace_pathsep_chars"` else func_to_host_path_result=$3 fi fi } # end func_convert_path_check # func_convert_path_front_back_pathsep FRONTPAT BACKPAT REPL ORIG # Modifies func_to_host_path_result by prepending REPL if ORIG matches FRONTPAT # and appending REPL if ORIG matches BACKPAT. func_convert_path_front_back_pathsep () { $debug_cmd case $4 in $1 ) func_to_host_path_result=$3$func_to_host_path_result ;; esac case $4 in $2 ) func_append func_to_host_path_result "$3" ;; esac } # end func_convert_path_front_back_pathsep ################################################## # $build to $host FILE NAME CONVERSION FUNCTIONS # ################################################## # invoked via '$to_host_file_cmd ARG' # # In each case, ARG is the path to be converted from $build to $host format. # Result will be available in $func_to_host_file_result. # func_to_host_file ARG # Converts the file name ARG from $build format to $host format. Return result # in func_to_host_file_result. func_to_host_file () { $debug_cmd $to_host_file_cmd "$1" } # end func_to_host_file # func_to_tool_file ARG LAZY # converts the file name ARG from $build format to toolchain format. Return # result in func_to_tool_file_result. If the conversion in use is listed # in (the comma separated) LAZY, no conversion takes place. func_to_tool_file () { $debug_cmd case ,$2, in *,"$to_tool_file_cmd",*) func_to_tool_file_result=$1 ;; *) $to_tool_file_cmd "$1" func_to_tool_file_result=$func_to_host_file_result ;; esac } # end func_to_tool_file # func_convert_file_noop ARG # Copy ARG to func_to_host_file_result. func_convert_file_noop () { func_to_host_file_result=$1 } # end func_convert_file_noop # func_convert_file_msys_to_w32 ARG # Convert file name ARG from (mingw) MSYS to (mingw) w32 format; automatic # conversion to w32 is not available inside the cwrapper. Returns result in # func_to_host_file_result. func_convert_file_msys_to_w32 () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then func_convert_core_msys_to_w32 "$1" func_to_host_file_result=$func_convert_core_msys_to_w32_result fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_msys_to_w32 # func_convert_file_cygwin_to_w32 ARG # Convert file name ARG from Cygwin to w32 format. Returns result in # func_to_host_file_result. func_convert_file_cygwin_to_w32 () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then # because $build is cygwin, we call "the" cygpath in $PATH; no need to use # LT_CYGPATH in this case. func_to_host_file_result=`cygpath -m "$1"` fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_cygwin_to_w32 # func_convert_file_nix_to_w32 ARG # Convert file name ARG from *nix to w32 format. Requires a wine environment # and a working winepath. Returns result in func_to_host_file_result. func_convert_file_nix_to_w32 () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then func_convert_core_file_wine_to_w32 "$1" func_to_host_file_result=$func_convert_core_file_wine_to_w32_result fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_nix_to_w32 # func_convert_file_msys_to_cygwin ARG # Convert file name ARG from MSYS to Cygwin format. Requires LT_CYGPATH set. # Returns result in func_to_host_file_result. func_convert_file_msys_to_cygwin () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then func_convert_core_msys_to_w32 "$1" func_cygpath -u "$func_convert_core_msys_to_w32_result" func_to_host_file_result=$func_cygpath_result fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_msys_to_cygwin # func_convert_file_nix_to_cygwin ARG # Convert file name ARG from *nix to Cygwin format. Requires Cygwin installed # in a wine environment, working winepath, and LT_CYGPATH set. Returns result # in func_to_host_file_result. func_convert_file_nix_to_cygwin () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then # convert from *nix to w32, then use cygpath to convert from w32 to cygwin. func_convert_core_file_wine_to_w32 "$1" func_cygpath -u "$func_convert_core_file_wine_to_w32_result" func_to_host_file_result=$func_cygpath_result fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_nix_to_cygwin ############################################# # $build to $host PATH CONVERSION FUNCTIONS # ############################################# # invoked via '$to_host_path_cmd ARG' # # In each case, ARG is the path to be converted from $build to $host format. # The result will be available in $func_to_host_path_result. # # Path separators are also converted from $build format to $host format. If # ARG begins or ends with a path separator character, it is preserved (but # converted to $host format) on output. # # All path conversion functions are named using the following convention: # file name conversion function : func_convert_file_X_to_Y () # path conversion function : func_convert_path_X_to_Y () # where, for any given $build/$host combination the 'X_to_Y' value is the # same. If conversion functions are added for new $build/$host combinations, # the two new functions must follow this pattern, or func_init_to_host_path_cmd # will break. # func_init_to_host_path_cmd # Ensures that function "pointer" variable $to_host_path_cmd is set to the # appropriate value, based on the value of $to_host_file_cmd. to_host_path_cmd= func_init_to_host_path_cmd () { $debug_cmd if test -z "$to_host_path_cmd"; then func_stripname 'func_convert_file_' '' "$to_host_file_cmd" to_host_path_cmd=func_convert_path_$func_stripname_result fi } # func_to_host_path ARG # Converts the path ARG from $build format to $host format. Return result # in func_to_host_path_result. func_to_host_path () { $debug_cmd func_init_to_host_path_cmd $to_host_path_cmd "$1" } # end func_to_host_path # func_convert_path_noop ARG # Copy ARG to func_to_host_path_result. func_convert_path_noop () { func_to_host_path_result=$1 } # end func_convert_path_noop # func_convert_path_msys_to_w32 ARG # Convert path ARG from (mingw) MSYS to (mingw) w32 format; automatic # conversion to w32 is not available inside the cwrapper. Returns result in # func_to_host_path_result. func_convert_path_msys_to_w32 () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # Remove leading and trailing path separator characters from ARG. MSYS # behavior is inconsistent here; cygpath turns them into '.;' and ';.'; # and winepath ignores them completely. func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_msys_to_w32 "$func_to_host_path_tmp1" func_to_host_path_result=$func_convert_core_msys_to_w32_result func_convert_path_check : ";" \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" fi } # end func_convert_path_msys_to_w32 # func_convert_path_cygwin_to_w32 ARG # Convert path ARG from Cygwin to w32 format. Returns result in # func_to_host_file_result. func_convert_path_cygwin_to_w32 () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # See func_convert_path_msys_to_w32: func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_to_host_path_result=`cygpath -m -p "$func_to_host_path_tmp1"` func_convert_path_check : ";" \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" fi } # end func_convert_path_cygwin_to_w32 # func_convert_path_nix_to_w32 ARG # Convert path ARG from *nix to w32 format. Requires a wine environment and # a working winepath. Returns result in func_to_host_file_result. func_convert_path_nix_to_w32 () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # See func_convert_path_msys_to_w32: func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1" func_to_host_path_result=$func_convert_core_path_wine_to_w32_result func_convert_path_check : ";" \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" fi } # end func_convert_path_nix_to_w32 # func_convert_path_msys_to_cygwin ARG # Convert path ARG from MSYS to Cygwin format. Requires LT_CYGPATH set. # Returns result in func_to_host_file_result. func_convert_path_msys_to_cygwin () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # See func_convert_path_msys_to_w32: func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_msys_to_w32 "$func_to_host_path_tmp1" func_cygpath -u -p "$func_convert_core_msys_to_w32_result" func_to_host_path_result=$func_cygpath_result func_convert_path_check : : \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" : "$1" fi } # end func_convert_path_msys_to_cygwin # func_convert_path_nix_to_cygwin ARG # Convert path ARG from *nix to Cygwin format. Requires Cygwin installed in a # a wine environment, working winepath, and LT_CYGPATH set. Returns result in # func_to_host_file_result. func_convert_path_nix_to_cygwin () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # Remove leading and trailing path separator characters from # ARG. msys behavior is inconsistent here, cygpath turns them # into '.;' and ';.', and winepath ignores them completely. func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1" func_cygpath -u -p "$func_convert_core_path_wine_to_w32_result" func_to_host_path_result=$func_cygpath_result func_convert_path_check : : \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" : "$1" fi } # end func_convert_path_nix_to_cygwin # func_dll_def_p FILE # True iff FILE is a Windows DLL '.def' file. # Keep in sync with _LT_DLL_DEF_P in libtool.m4 func_dll_def_p () { $debug_cmd func_dll_def_p_tmp=`$SED -n \ -e 's/^[ ]*//' \ -e '/^\(;.*\)*$/d' \ -e 's/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p' \ -e q \ "$1"` test DEF = "$func_dll_def_p_tmp" } # func_mode_compile arg... func_mode_compile () { $debug_cmd # Get the compilation command and the source file. base_compile= srcfile=$nonopt # always keep a non-empty value in "srcfile" suppress_opt=yes suppress_output= arg_mode=normal libobj= later= pie_flag= for arg do case $arg_mode in arg ) # do not "continue". Instead, add this to base_compile lastarg=$arg arg_mode=normal ;; target ) libobj=$arg arg_mode=normal continue ;; normal ) # Accept any command-line options. case $arg in -o) test -n "$libobj" && \ func_fatal_error "you cannot specify '-o' more than once" arg_mode=target continue ;; -pie | -fpie | -fPIE) func_append pie_flag " $arg" continue ;; -shared | -static | -prefer-pic | -prefer-non-pic) func_append later " $arg" continue ;; -no-suppress) suppress_opt=no continue ;; -Xcompiler) arg_mode=arg # the next one goes into the "base_compile" arg list continue # The current "srcfile" will either be retained or ;; # replaced later. I would guess that would be a bug. -Wc,*) func_stripname '-Wc,' '' "$arg" args=$func_stripname_result lastarg= save_ifs=$IFS; IFS=, for arg in $args; do IFS=$save_ifs func_append_quoted lastarg "$arg" done IFS=$save_ifs func_stripname ' ' '' "$lastarg" lastarg=$func_stripname_result # Add the arguments to base_compile. func_append base_compile " $lastarg" continue ;; *) # Accept the current argument as the source file. # The previous "srcfile" becomes the current argument. # lastarg=$srcfile srcfile=$arg ;; esac # case $arg ;; esac # case $arg_mode # Aesthetically quote the previous argument. func_append_quoted base_compile "$lastarg" done # for arg case $arg_mode in arg) func_fatal_error "you must specify an argument for -Xcompile" ;; target) func_fatal_error "you must specify a target with '-o'" ;; *) # Get the name of the library object. test -z "$libobj" && { func_basename "$srcfile" libobj=$func_basename_result } ;; esac # Recognize several different file suffixes. # If the user specifies -o file.o, it is replaced with file.lo case $libobj in *.[cCFSifmso] | \ *.ada | *.adb | *.ads | *.asm | \ *.c++ | *.cc | *.ii | *.class | *.cpp | *.cxx | \ *.[fF][09]? | *.for | *.java | *.go | *.obj | *.sx | *.cu | *.cup) func_xform "$libobj" libobj=$func_xform_result ;; esac case $libobj in *.lo) func_lo2o "$libobj"; obj=$func_lo2o_result ;; *) func_fatal_error "cannot determine name of library object from '$libobj'" ;; esac func_infer_tag $base_compile for arg in $later; do case $arg in -shared) test yes = "$build_libtool_libs" \ || func_fatal_configuration "cannot build a shared library" build_old_libs=no continue ;; -static) build_libtool_libs=no build_old_libs=yes continue ;; -prefer-pic) pic_mode=yes continue ;; -prefer-non-pic) pic_mode=no continue ;; esac done func_quote_for_eval "$libobj" test "X$libobj" != "X$func_quote_for_eval_result" \ && $ECHO "X$libobj" | $GREP '[]~#^*{};<>?"'"'"' &()|`$[]' \ && func_warning "libobj name '$libobj' may not contain shell special characters." func_dirname_and_basename "$obj" "/" "" objname=$func_basename_result xdir=$func_dirname_result lobj=$xdir$objdir/$objname test -z "$base_compile" && \ func_fatal_help "you must specify a compilation command" # Delete any leftover library objects. if test yes = "$build_old_libs"; then removelist="$obj $lobj $libobj ${libobj}T" else removelist="$lobj $libobj ${libobj}T" fi # On Cygwin there's no "real" PIC flag so we must build both object types case $host_os in cygwin* | mingw* | pw32* | os2* | cegcc*) pic_mode=default ;; esac if test no = "$pic_mode" && test pass_all != "$deplibs_check_method"; then # non-PIC code in shared libraries is not supported pic_mode=default fi # Calculate the filename of the output object if compiler does # not support -o with -c if test no = "$compiler_c_o"; then output_obj=`$ECHO "$srcfile" | $SED 's%^.*/%%; s%\.[^.]*$%%'`.$objext lockfile=$output_obj.lock else output_obj= need_locks=no lockfile= fi # Lock this critical section if it is needed # We use this script file to make the link, it avoids creating a new file if test yes = "$need_locks"; then until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do func_echo "Waiting for $lockfile to be removed" sleep 2 done elif test warn = "$need_locks"; then if test -f "$lockfile"; then $ECHO "\ *** ERROR, $lockfile exists and contains: `cat $lockfile 2>/dev/null` This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because your compiler does not support '-c' and '-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." $opt_dry_run || $RM $removelist exit $EXIT_FAILURE fi func_append removelist " $output_obj" $ECHO "$srcfile" > "$lockfile" fi $opt_dry_run || $RM $removelist func_append removelist " $lockfile" trap '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' 1 2 15 func_to_tool_file "$srcfile" func_convert_file_msys_to_w32 srcfile=$func_to_tool_file_result func_quote_for_eval "$srcfile" qsrcfile=$func_quote_for_eval_result # Only build a PIC object if we are building libtool libraries. if test yes = "$build_libtool_libs"; then # Without this assignment, base_compile gets emptied. fbsd_hideous_sh_bug=$base_compile if test no != "$pic_mode"; then command="$base_compile $qsrcfile $pic_flag" else # Don't build PIC code command="$base_compile $qsrcfile" fi func_mkdir_p "$xdir$objdir" if test -z "$output_obj"; then # Place PIC objects in $objdir func_append command " -o $lobj" fi func_show_eval_locale "$command" \ 'test -n "$output_obj" && $RM $removelist; exit $EXIT_FAILURE' if test warn = "$need_locks" && test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then $ECHO "\ *** ERROR, $lockfile contains: `cat $lockfile 2>/dev/null` but it should contain: $srcfile This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because your compiler does not support '-c' and '-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." $opt_dry_run || $RM $removelist exit $EXIT_FAILURE fi # Just move the object if needed, then go on to compile the next one if test -n "$output_obj" && test "X$output_obj" != "X$lobj"; then func_show_eval '$MV "$output_obj" "$lobj"' \ 'error=$?; $opt_dry_run || $RM $removelist; exit $error' fi # Allow error messages only from the first compilation. if test yes = "$suppress_opt"; then suppress_output=' >/dev/null 2>&1' fi fi # Only build a position-dependent object if we build old libraries. if test yes = "$build_old_libs"; then if test yes != "$pic_mode"; then # Don't build PIC code command="$base_compile $qsrcfile$pie_flag" else command="$base_compile $qsrcfile $pic_flag" fi if test yes = "$compiler_c_o"; then func_append command " -o $obj" fi # Suppress compiler output if we already did a PIC compilation. func_append command "$suppress_output" func_show_eval_locale "$command" \ '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' if test warn = "$need_locks" && test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then $ECHO "\ *** ERROR, $lockfile contains: `cat $lockfile 2>/dev/null` but it should contain: $srcfile This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because your compiler does not support '-c' and '-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." $opt_dry_run || $RM $removelist exit $EXIT_FAILURE fi # Just move the object if needed if test -n "$output_obj" && test "X$output_obj" != "X$obj"; then func_show_eval '$MV "$output_obj" "$obj"' \ 'error=$?; $opt_dry_run || $RM $removelist; exit $error' fi fi $opt_dry_run || { func_write_libtool_object "$libobj" "$objdir/$objname" "$objname" # Unlock the critical section if it was locked if test no != "$need_locks"; then removelist=$lockfile $RM "$lockfile" fi } exit $EXIT_SUCCESS } $opt_help || { test compile = "$opt_mode" && func_mode_compile ${1+"$@"} } func_mode_help () { # We need to display help for each of the modes. case $opt_mode in "") # Generic help is extracted from the usage comments # at the start of this file. func_help ;; clean) $ECHO \ "Usage: $progname [OPTION]... --mode=clean RM [RM-OPTION]... FILE... Remove files from the build directory. RM is the name of the program to use to delete files associated with each FILE (typically '/bin/rm'). RM-OPTIONS are options (such as '-f') to be passed to RM. If FILE is a libtool library, object or program, all the files associated with it are deleted. Otherwise, only FILE itself is deleted using RM." ;; compile) $ECHO \ "Usage: $progname [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE Compile a source file into a libtool library object. This mode accepts the following additional options: -o OUTPUT-FILE set the output file name to OUTPUT-FILE -no-suppress do not suppress compiler output for multiple passes -prefer-pic try to build PIC objects only -prefer-non-pic try to build non-PIC objects only -shared do not build a '.o' file suitable for static linking -static only build a '.o' file suitable for static linking -Wc,FLAG pass FLAG directly to the compiler COMPILE-COMMAND is a command to be used in creating a 'standard' object file from the given SOURCEFILE. The output file name is determined by removing the directory component from SOURCEFILE, then substituting the C source code suffix '.c' with the library object suffix, '.lo'." ;; execute) $ECHO \ "Usage: $progname [OPTION]... --mode=execute COMMAND [ARGS]... Automatically set library path, then run a program. This mode accepts the following additional options: -dlopen FILE add the directory containing FILE to the library path This mode sets the library path environment variable according to '-dlopen' flags. If any of the ARGS are libtool executable wrappers, then they are translated into their corresponding uninstalled binary, and any of their required library directories are added to the library path. Then, COMMAND is executed, with ARGS as arguments." ;; finish) $ECHO \ "Usage: $progname [OPTION]... --mode=finish [LIBDIR]... Complete the installation of libtool libraries. Each LIBDIR is a directory that contains libtool libraries. The commands that this mode executes may require superuser privileges. Use the '--dry-run' option if you just want to see what would be executed." ;; install) $ECHO \ "Usage: $progname [OPTION]... --mode=install INSTALL-COMMAND... Install executables or libraries. INSTALL-COMMAND is the installation command. The first component should be either the 'install' or 'cp' program. The following components of INSTALL-COMMAND are treated specially: -inst-prefix-dir PREFIX-DIR Use PREFIX-DIR as a staging area for installation The rest of the components are interpreted as arguments to that command (only BSD-compatible install options are recognized)." ;; link) $ECHO \ "Usage: $progname [OPTION]... --mode=link LINK-COMMAND... Link object files or libraries together to form another library, or to create an executable program. LINK-COMMAND is a command using the C compiler that you would use to create a program from several object files. The following components of LINK-COMMAND are treated specially: -all-static do not do any dynamic linking at all -avoid-version do not add a version suffix if possible -bindir BINDIR specify path to binaries directory (for systems where libraries must be found in the PATH setting at runtime) -dlopen FILE '-dlpreopen' FILE if it cannot be dlopened at runtime -dlpreopen FILE link in FILE and add its symbols to lt_preloaded_symbols -export-dynamic allow symbols from OUTPUT-FILE to be resolved with dlsym(3) -export-symbols SYMFILE try to export only the symbols listed in SYMFILE -export-symbols-regex REGEX try to export only the symbols matching REGEX -LLIBDIR search LIBDIR for required installed libraries -lNAME OUTPUT-FILE requires the installed library libNAME -module build a library that can dlopened -no-fast-install disable the fast-install mode -no-install link a not-installable executable -no-undefined declare that a library does not refer to external symbols -o OUTPUT-FILE create OUTPUT-FILE from the specified objects -objectlist FILE use a list of object files found in FILE to specify objects -os2dllname NAME force a short DLL name on OS/2 (no effect on other OSes) -precious-files-regex REGEX don't remove output files matching REGEX -release RELEASE specify package release information -rpath LIBDIR the created library will eventually be installed in LIBDIR -R[ ]LIBDIR add LIBDIR to the runtime path of programs and libraries -shared only do dynamic linking of libtool libraries -shrext SUFFIX override the standard shared library file extension -static do not do any dynamic linking of uninstalled libtool libraries -static-libtool-libs do not do any dynamic linking of libtool libraries -version-info CURRENT[:REVISION[:AGE]] specify library version info [each variable defaults to 0] -weak LIBNAME declare that the target provides the LIBNAME interface -Wc,FLAG -Xcompiler FLAG pass linker-specific FLAG directly to the compiler -Wl,FLAG -Xlinker FLAG pass linker-specific FLAG directly to the linker -XCClinker FLAG pass link-specific FLAG to the compiler driver (CC) All other options (arguments beginning with '-') are ignored. Every other argument is treated as a filename. Files ending in '.la' are treated as uninstalled libtool libraries, other files are standard or library object files. If the OUTPUT-FILE ends in '.la', then a libtool library is created, only library objects ('.lo' files) may be specified, and '-rpath' is required, except when creating a convenience library. If OUTPUT-FILE ends in '.a' or '.lib', then a standard library is created using 'ar' and 'ranlib', or on Windows using 'lib'. If OUTPUT-FILE ends in '.lo' or '.$objext', then a reloadable object file is created, otherwise an executable program is created." ;; uninstall) $ECHO \ "Usage: $progname [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE... Remove libraries from an installation directory. RM is the name of the program to use to delete files associated with each FILE (typically '/bin/rm'). RM-OPTIONS are options (such as '-f') to be passed to RM. If FILE is a libtool library, all the files associated with it are deleted. Otherwise, only FILE itself is deleted using RM." ;; *) func_fatal_help "invalid operation mode '$opt_mode'" ;; esac echo $ECHO "Try '$progname --help' for more information about other modes." } # Now that we've collected a possible --mode arg, show help if necessary if $opt_help; then if test : = "$opt_help"; then func_mode_help else { func_help noexit for opt_mode in compile link execute install finish uninstall clean; do func_mode_help done } | $SED -n '1p; 2,$s/^Usage:/ or: /p' { func_help noexit for opt_mode in compile link execute install finish uninstall clean; do echo func_mode_help done } | $SED '1d /^When reporting/,/^Report/{ H d } $x /information about other modes/d /more detailed .*MODE/d s/^Usage:.*--mode=\([^ ]*\) .*/Description of \1 mode:/' fi exit $? fi # func_mode_execute arg... func_mode_execute () { $debug_cmd # The first argument is the command name. cmd=$nonopt test -z "$cmd" && \ func_fatal_help "you must specify a COMMAND" # Handle -dlopen flags immediately. for file in $opt_dlopen; do test -f "$file" \ || func_fatal_help "'$file' is not a file" dir= case $file in *.la) func_resolve_sysroot "$file" file=$func_resolve_sysroot_result # Check to see that this really is a libtool archive. func_lalib_unsafe_p "$file" \ || func_fatal_help "'$lib' is not a valid libtool archive" # Read the libtool library. dlname= library_names= func_source "$file" # Skip this library if it cannot be dlopened. if test -z "$dlname"; then # Warn if it was a shared library. test -n "$library_names" && \ func_warning "'$file' was not linked with '-export-dynamic'" continue fi func_dirname "$file" "" "." dir=$func_dirname_result if test -f "$dir/$objdir/$dlname"; then func_append dir "/$objdir" else if test ! -f "$dir/$dlname"; then func_fatal_error "cannot find '$dlname' in '$dir' or '$dir/$objdir'" fi fi ;; *.lo) # Just add the directory containing the .lo file. func_dirname "$file" "" "." dir=$func_dirname_result ;; *) func_warning "'-dlopen' is ignored for non-libtool libraries and objects" continue ;; esac # Get the absolute pathname. absdir=`cd "$dir" && pwd` test -n "$absdir" && dir=$absdir # Now add the directory to shlibpath_var. if eval "test -z \"\$$shlibpath_var\""; then eval "$shlibpath_var=\"\$dir\"" else eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\"" fi done # This variable tells wrapper scripts just to set shlibpath_var # rather than running their programs. libtool_execute_magic=$magic # Check if any of the arguments is a wrapper script. args= for file do case $file in -* | *.la | *.lo ) ;; *) # Do a test to see if this is really a libtool program. if func_ltwrapper_script_p "$file"; then func_source "$file" # Transform arg to wrapped name. file=$progdir/$program elif func_ltwrapper_executable_p "$file"; then func_ltwrapper_scriptname "$file" func_source "$func_ltwrapper_scriptname_result" # Transform arg to wrapped name. file=$progdir/$program fi ;; esac # Quote arguments (to preserve shell metacharacters). func_append_quoted args "$file" done if $opt_dry_run; then # Display what would be done. if test -n "$shlibpath_var"; then eval "\$ECHO \"\$shlibpath_var=\$$shlibpath_var\"" echo "export $shlibpath_var" fi $ECHO "$cmd$args" exit $EXIT_SUCCESS else if test -n "$shlibpath_var"; then # Export the shlibpath_var. eval "export $shlibpath_var" fi # Restore saved environment variables for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES do eval "if test \"\${save_$lt_var+set}\" = set; then $lt_var=\$save_$lt_var; export $lt_var else $lt_unset $lt_var fi" done # Now prepare to actually exec the command. exec_cmd=\$cmd$args fi } test execute = "$opt_mode" && func_mode_execute ${1+"$@"} # func_mode_finish arg... func_mode_finish () { $debug_cmd libs= libdirs= admincmds= for opt in "$nonopt" ${1+"$@"} do if test -d "$opt"; then func_append libdirs " $opt" elif test -f "$opt"; then if func_lalib_unsafe_p "$opt"; then func_append libs " $opt" else func_warning "'$opt' is not a valid libtool archive" fi else func_fatal_error "invalid argument '$opt'" fi done if test -n "$libs"; then if test -n "$lt_sysroot"; then sysroot_regex=`$ECHO "$lt_sysroot" | $SED "$sed_make_literal_regex"` sysroot_cmd="s/\([ ']\)$sysroot_regex/\1/g;" else sysroot_cmd= fi # Remove sysroot references if $opt_dry_run; then for lib in $libs; do echo "removing references to $lt_sysroot and '=' prefixes from $lib" done else tmpdir=`func_mktempdir` for lib in $libs; do $SED -e "$sysroot_cmd s/\([ ']-[LR]\)=/\1/g; s/\([ ']\)=/\1/g" $lib \ > $tmpdir/tmp-la mv -f $tmpdir/tmp-la $lib done ${RM}r "$tmpdir" fi fi if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then for libdir in $libdirs; do if test -n "$finish_cmds"; then # Do each command in the finish commands. func_execute_cmds "$finish_cmds" 'admincmds="$admincmds '"$cmd"'"' fi if test -n "$finish_eval"; then # Do the single finish_eval. eval cmds=\"$finish_eval\" $opt_dry_run || eval "$cmds" || func_append admincmds " $cmds" fi done fi # Exit here if they wanted silent mode. $opt_quiet && exit $EXIT_SUCCESS if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then echo "----------------------------------------------------------------------" echo "Libraries have been installed in:" for libdir in $libdirs; do $ECHO " $libdir" done echo echo "If you ever happen to want to link against installed libraries" echo "in a given directory, LIBDIR, you must either use libtool, and" echo "specify the full pathname of the library, or use the '-LLIBDIR'" echo "flag during linking and do at least one of the following:" if test -n "$shlibpath_var"; then echo " - add LIBDIR to the '$shlibpath_var' environment variable" echo " during execution" fi if test -n "$runpath_var"; then echo " - add LIBDIR to the '$runpath_var' environment variable" echo " during linking" fi if test -n "$hardcode_libdir_flag_spec"; then libdir=LIBDIR eval flag=\"$hardcode_libdir_flag_spec\" $ECHO " - use the '$flag' linker flag" fi if test -n "$admincmds"; then $ECHO " - have your system administrator run these commands:$admincmds" fi if test -f /etc/ld.so.conf; then echo " - have your system administrator add LIBDIR to '/etc/ld.so.conf'" fi echo echo "See any operating system documentation about shared libraries for" case $host in solaris2.[6789]|solaris2.1[0-9]) echo "more information, such as the ld(1), crle(1) and ld.so(8) manual" echo "pages." ;; *) echo "more information, such as the ld(1) and ld.so(8) manual pages." ;; esac echo "----------------------------------------------------------------------" fi exit $EXIT_SUCCESS } test finish = "$opt_mode" && func_mode_finish ${1+"$@"} # func_mode_install arg... func_mode_install () { $debug_cmd # There may be an optional sh(1) argument at the beginning of # install_prog (especially on Windows NT). if test "$SHELL" = "$nonopt" || test /bin/sh = "$nonopt" || # Allow the use of GNU shtool's install command. case $nonopt in *shtool*) :;; *) false;; esac then # Aesthetically quote it. func_quote_for_eval "$nonopt" install_prog="$func_quote_for_eval_result " arg=$1 shift else install_prog= arg=$nonopt fi # The real first argument should be the name of the installation program. # Aesthetically quote it. func_quote_for_eval "$arg" func_append install_prog "$func_quote_for_eval_result" install_shared_prog=$install_prog case " $install_prog " in *[\\\ /]cp\ *) install_cp=: ;; *) install_cp=false ;; esac # We need to accept at least all the BSD install flags. dest= files= opts= prev= install_type= isdir=false stripme= no_mode=: for arg do arg2= if test -n "$dest"; then func_append files " $dest" dest=$arg continue fi case $arg in -d) isdir=: ;; -f) if $install_cp; then :; else prev=$arg fi ;; -g | -m | -o) prev=$arg ;; -s) stripme=" -s" continue ;; -*) ;; *) # If the previous option needed an argument, then skip it. if test -n "$prev"; then if test X-m = "X$prev" && test -n "$install_override_mode"; then arg2=$install_override_mode no_mode=false fi prev= else dest=$arg continue fi ;; esac # Aesthetically quote the argument. func_quote_for_eval "$arg" func_append install_prog " $func_quote_for_eval_result" if test -n "$arg2"; then func_quote_for_eval "$arg2" fi func_append install_shared_prog " $func_quote_for_eval_result" done test -z "$install_prog" && \ func_fatal_help "you must specify an install program" test -n "$prev" && \ func_fatal_help "the '$prev' option requires an argument" if test -n "$install_override_mode" && $no_mode; then if $install_cp; then :; else func_quote_for_eval "$install_override_mode" func_append install_shared_prog " -m $func_quote_for_eval_result" fi fi if test -z "$files"; then if test -z "$dest"; then func_fatal_help "no file or destination specified" else func_fatal_help "you must specify a destination" fi fi # Strip any trailing slash from the destination. func_stripname '' '/' "$dest" dest=$func_stripname_result # Check to see that the destination is a directory. test -d "$dest" && isdir=: if $isdir; then destdir=$dest destname= else func_dirname_and_basename "$dest" "" "." destdir=$func_dirname_result destname=$func_basename_result # Not a directory, so check to see that there is only one file specified. set dummy $files; shift test "$#" -gt 1 && \ func_fatal_help "'$dest' is not a directory" fi case $destdir in [\\/]* | [A-Za-z]:[\\/]*) ;; *) for file in $files; do case $file in *.lo) ;; *) func_fatal_help "'$destdir' must be an absolute directory name" ;; esac done ;; esac # This variable tells wrapper scripts just to set variables rather # than running their programs. libtool_install_magic=$magic staticlibs= future_libdirs= current_libdirs= for file in $files; do # Do each installation. case $file in *.$libext) # Do the static libraries later. func_append staticlibs " $file" ;; *.la) func_resolve_sysroot "$file" file=$func_resolve_sysroot_result # Check to see that this really is a libtool archive. func_lalib_unsafe_p "$file" \ || func_fatal_help "'$file' is not a valid libtool archive" library_names= old_library= relink_command= func_source "$file" # Add the libdir to current_libdirs if it is the destination. if test "X$destdir" = "X$libdir"; then case "$current_libdirs " in *" $libdir "*) ;; *) func_append current_libdirs " $libdir" ;; esac else # Note the libdir as a future libdir. case "$future_libdirs " in *" $libdir "*) ;; *) func_append future_libdirs " $libdir" ;; esac fi func_dirname "$file" "/" "" dir=$func_dirname_result func_append dir "$objdir" if test -n "$relink_command"; then # Determine the prefix the user has applied to our future dir. inst_prefix_dir=`$ECHO "$destdir" | $SED -e "s%$libdir\$%%"` # Don't allow the user to place us outside of our expected # location b/c this prevents finding dependent libraries that # are installed to the same prefix. # At present, this check doesn't affect windows .dll's that # are installed into $libdir/../bin (currently, that works fine) # but it's something to keep an eye on. test "$inst_prefix_dir" = "$destdir" && \ func_fatal_error "error: cannot install '$file' to a directory not ending in $libdir" if test -n "$inst_prefix_dir"; then # Stick the inst_prefix_dir data into the link command. relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%"` else relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%%"` fi func_warning "relinking '$file'" func_show_eval "$relink_command" \ 'func_fatal_error "error: relink '\''$file'\'' with the above command before installing it"' fi # See the names of the shared library. set dummy $library_names; shift if test -n "$1"; then realname=$1 shift srcname=$realname test -n "$relink_command" && srcname=${realname}T # Install the shared library and build the symlinks. func_show_eval "$install_shared_prog $dir/$srcname $destdir/$realname" \ 'exit $?' tstripme=$stripme case $host_os in cygwin* | mingw* | pw32* | cegcc*) case $realname in *.dll.a) tstripme= ;; esac ;; os2*) case $realname in *_dll.a) tstripme= ;; esac ;; esac if test -n "$tstripme" && test -n "$striplib"; then func_show_eval "$striplib $destdir/$realname" 'exit $?' fi if test "$#" -gt 0; then # Delete the old symlinks, and create new ones. # Try 'ln -sf' first, because the 'ln' binary might depend on # the symlink we replace! Solaris /bin/ln does not understand -f, # so we also need to try rm && ln -s. for linkname do test "$linkname" != "$realname" \ && func_show_eval "(cd $destdir && { $LN_S -f $realname $linkname || { $RM $linkname && $LN_S $realname $linkname; }; })" done fi # Do each command in the postinstall commands. lib=$destdir/$realname func_execute_cmds "$postinstall_cmds" 'exit $?' fi # Install the pseudo-library for information purposes. func_basename "$file" name=$func_basename_result instname=$dir/${name}i func_show_eval "$install_prog $instname $destdir/$name" 'exit $?' # Maybe install the static library, too. test -n "$old_library" && func_append staticlibs " $dir/$old_library" ;; *.lo) # Install (i.e. copy) a libtool object. # Figure out destination file name, if it wasn't already specified. if test -n "$destname"; then destfile=$destdir/$destname else func_basename "$file" destfile=$func_basename_result destfile=$destdir/$destfile fi # Deduce the name of the destination old-style object file. case $destfile in *.lo) func_lo2o "$destfile" staticdest=$func_lo2o_result ;; *.$objext) staticdest=$destfile destfile= ;; *) func_fatal_help "cannot copy a libtool object to '$destfile'" ;; esac # Install the libtool object if requested. test -n "$destfile" && \ func_show_eval "$install_prog $file $destfile" 'exit $?' # Install the old object if enabled. if test yes = "$build_old_libs"; then # Deduce the name of the old-style object file. func_lo2o "$file" staticobj=$func_lo2o_result func_show_eval "$install_prog \$staticobj \$staticdest" 'exit $?' fi exit $EXIT_SUCCESS ;; *) # Figure out destination file name, if it wasn't already specified. if test -n "$destname"; then destfile=$destdir/$destname else func_basename "$file" destfile=$func_basename_result destfile=$destdir/$destfile fi # If the file is missing, and there is a .exe on the end, strip it # because it is most likely a libtool script we actually want to # install stripped_ext= case $file in *.exe) if test ! -f "$file"; then func_stripname '' '.exe' "$file" file=$func_stripname_result stripped_ext=.exe fi ;; esac # Do a test to see if this is really a libtool program. case $host in *cygwin* | *mingw*) if func_ltwrapper_executable_p "$file"; then func_ltwrapper_scriptname "$file" wrapper=$func_ltwrapper_scriptname_result else func_stripname '' '.exe' "$file" wrapper=$func_stripname_result fi ;; *) wrapper=$file ;; esac if func_ltwrapper_script_p "$wrapper"; then notinst_deplibs= relink_command= func_source "$wrapper" # Check the variables that should have been set. test -z "$generated_by_libtool_version" && \ func_fatal_error "invalid libtool wrapper script '$wrapper'" finalize=: for lib in $notinst_deplibs; do # Check to see that each library is installed. libdir= if test -f "$lib"; then func_source "$lib" fi libfile=$libdir/`$ECHO "$lib" | $SED 's%^.*/%%g'` if test -n "$libdir" && test ! -f "$libfile"; then func_warning "'$lib' has not been installed in '$libdir'" finalize=false fi done relink_command= func_source "$wrapper" outputname= if test no = "$fast_install" && test -n "$relink_command"; then $opt_dry_run || { if $finalize; then tmpdir=`func_mktempdir` func_basename "$file$stripped_ext" file=$func_basename_result outputname=$tmpdir/$file # Replace the output file specification. relink_command=`$ECHO "$relink_command" | $SED 's%@OUTPUT@%'"$outputname"'%g'` $opt_quiet || { func_quote_for_expand "$relink_command" eval "func_echo $func_quote_for_expand_result" } if eval "$relink_command"; then : else func_error "error: relink '$file' with the above command before installing it" $opt_dry_run || ${RM}r "$tmpdir" continue fi file=$outputname else func_warning "cannot relink '$file'" fi } else # Install the binary that we compiled earlier. file=`$ECHO "$file$stripped_ext" | $SED "s%\([^/]*\)$%$objdir/\1%"` fi fi # remove .exe since cygwin /usr/bin/install will append another # one anyway case $install_prog,$host in */usr/bin/install*,*cygwin*) case $file:$destfile in *.exe:*.exe) # this is ok ;; *.exe:*) destfile=$destfile.exe ;; *:*.exe) func_stripname '' '.exe' "$destfile" destfile=$func_stripname_result ;; esac ;; esac func_show_eval "$install_prog\$stripme \$file \$destfile" 'exit $?' $opt_dry_run || if test -n "$outputname"; then ${RM}r "$tmpdir" fi ;; esac done for file in $staticlibs; do func_basename "$file" name=$func_basename_result # Set up the ranlib parameters. oldlib=$destdir/$name func_to_tool_file "$oldlib" func_convert_file_msys_to_w32 tool_oldlib=$func_to_tool_file_result func_show_eval "$install_prog \$file \$oldlib" 'exit $?' if test -n "$stripme" && test -n "$old_striplib"; then func_show_eval "$old_striplib $tool_oldlib" 'exit $?' fi # Do each command in the postinstall commands. func_execute_cmds "$old_postinstall_cmds" 'exit $?' done test -n "$future_libdirs" && \ func_warning "remember to run '$progname --finish$future_libdirs'" if test -n "$current_libdirs"; then # Maybe just do a dry run. $opt_dry_run && current_libdirs=" -n$current_libdirs" exec_cmd='$SHELL "$progpath" $preserve_args --finish$current_libdirs' else exit $EXIT_SUCCESS fi } test install = "$opt_mode" && func_mode_install ${1+"$@"} # func_generate_dlsyms outputname originator pic_p # Extract symbols from dlprefiles and create ${outputname}S.o with # a dlpreopen symbol table. func_generate_dlsyms () { $debug_cmd my_outputname=$1 my_originator=$2 my_pic_p=${3-false} my_prefix=`$ECHO "$my_originator" | $SED 's%[^a-zA-Z0-9]%_%g'` my_dlsyms= if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then if test -n "$NM" && test -n "$global_symbol_pipe"; then my_dlsyms=${my_outputname}S.c else func_error "not configured to extract global symbols from dlpreopened files" fi fi if test -n "$my_dlsyms"; then case $my_dlsyms in "") ;; *.c) # Discover the nlist of each of the dlfiles. nlist=$output_objdir/$my_outputname.nm func_show_eval "$RM $nlist ${nlist}S ${nlist}T" # Parse the name list into a source file. func_verbose "creating $output_objdir/$my_dlsyms" $opt_dry_run || $ECHO > "$output_objdir/$my_dlsyms" "\ /* $my_dlsyms - symbol resolution table for '$my_outputname' dlsym emulation. */ /* Generated by $PROGRAM (GNU $PACKAGE) $VERSION */ #ifdef __cplusplus extern \"C\" { #endif #if defined __GNUC__ && (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4)) || (__GNUC__ > 4)) #pragma GCC diagnostic ignored \"-Wstrict-prototypes\" #endif /* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ #if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE /* DATA imports from DLLs on WIN32 can't be const, because runtime relocations are performed -- see ld's documentation on pseudo-relocs. */ # define LT_DLSYM_CONST #elif defined __osf__ /* This system does not cope well with relocations in const data. */ # define LT_DLSYM_CONST #else # define LT_DLSYM_CONST const #endif #define STREQ(s1, s2) (strcmp ((s1), (s2)) == 0) /* External symbol declarations for the compiler. */\ " if test yes = "$dlself"; then func_verbose "generating symbol list for '$output'" $opt_dry_run || echo ': @PROGRAM@ ' > "$nlist" # Add our own program objects to the symbol list. progfiles=`$ECHO "$objs$old_deplibs" | $SP2NL | $SED "$lo2o" | $NL2SP` for progfile in $progfiles; do func_to_tool_file "$progfile" func_convert_file_msys_to_w32 func_verbose "extracting global C symbols from '$func_to_tool_file_result'" $opt_dry_run || eval "$NM $func_to_tool_file_result | $global_symbol_pipe >> '$nlist'" done if test -n "$exclude_expsyms"; then $opt_dry_run || { eval '$EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T' eval '$MV "$nlist"T "$nlist"' } fi if test -n "$export_symbols_regex"; then $opt_dry_run || { eval '$EGREP -e "$export_symbols_regex" "$nlist" > "$nlist"T' eval '$MV "$nlist"T "$nlist"' } fi # Prepare the list of exported symbols if test -z "$export_symbols"; then export_symbols=$output_objdir/$outputname.exp $opt_dry_run || { $RM $export_symbols eval "$SED -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"' case $host in *cygwin* | *mingw* | *cegcc* ) eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' eval 'cat "$export_symbols" >> "$output_objdir/$outputname.def"' ;; esac } else $opt_dry_run || { eval "$SED -e 's/\([].[*^$]\)/\\\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$outputname.exp"' eval '$GREP -f "$output_objdir/$outputname.exp" < "$nlist" > "$nlist"T' eval '$MV "$nlist"T "$nlist"' case $host in *cygwin* | *mingw* | *cegcc* ) eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' eval 'cat "$nlist" >> "$output_objdir/$outputname.def"' ;; esac } fi fi for dlprefile in $dlprefiles; do func_verbose "extracting global C symbols from '$dlprefile'" func_basename "$dlprefile" name=$func_basename_result case $host in *cygwin* | *mingw* | *cegcc* ) # if an import library, we need to obtain dlname if func_win32_import_lib_p "$dlprefile"; then func_tr_sh "$dlprefile" eval "curr_lafile=\$libfile_$func_tr_sh_result" dlprefile_dlbasename= if test -n "$curr_lafile" && func_lalib_p "$curr_lafile"; then # Use subshell, to avoid clobbering current variable values dlprefile_dlname=`source "$curr_lafile" && echo "$dlname"` if test -n "$dlprefile_dlname"; then func_basename "$dlprefile_dlname" dlprefile_dlbasename=$func_basename_result else # no lafile. user explicitly requested -dlpreopen . $sharedlib_from_linklib_cmd "$dlprefile" dlprefile_dlbasename=$sharedlib_from_linklib_result fi fi $opt_dry_run || { if test -n "$dlprefile_dlbasename"; then eval '$ECHO ": $dlprefile_dlbasename" >> "$nlist"' else func_warning "Could not compute DLL name from $name" eval '$ECHO ": $name " >> "$nlist"' fi func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe | $SED -e '/I __imp/d' -e 's/I __nm_/D /;s/_nm__//' >> '$nlist'" } else # not an import lib $opt_dry_run || { eval '$ECHO ": $name " >> "$nlist"' func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'" } fi ;; *) $opt_dry_run || { eval '$ECHO ": $name " >> "$nlist"' func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'" } ;; esac done $opt_dry_run || { # Make sure we have at least an empty file. test -f "$nlist" || : > "$nlist" if test -n "$exclude_expsyms"; then $EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T $MV "$nlist"T "$nlist" fi # Try sorting and uniquifying the output. if $GREP -v "^: " < "$nlist" | if sort -k 3 /dev/null 2>&1; then sort -k 3 else sort +2 fi | uniq > "$nlist"S; then : else $GREP -v "^: " < "$nlist" > "$nlist"S fi if test -f "$nlist"S; then eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$my_dlsyms"' else echo '/* NONE */' >> "$output_objdir/$my_dlsyms" fi func_show_eval '$RM "${nlist}I"' if test -n "$global_symbol_to_import"; then eval "$global_symbol_to_import"' < "$nlist"S > "$nlist"I' fi echo >> "$output_objdir/$my_dlsyms" "\ /* The mapping between symbol names and symbols. */ typedef struct { const char *name; void *address; } lt_dlsymlist; extern LT_DLSYM_CONST lt_dlsymlist lt_${my_prefix}_LTX_preloaded_symbols[];\ " if test -s "$nlist"I; then echo >> "$output_objdir/$my_dlsyms" "\ static void lt_syminit(void) { LT_DLSYM_CONST lt_dlsymlist *symbol = lt_${my_prefix}_LTX_preloaded_symbols; for (; symbol->name; ++symbol) {" $SED 's/.*/ if (STREQ (symbol->name, \"&\")) symbol->address = (void *) \&&;/' < "$nlist"I >> "$output_objdir/$my_dlsyms" echo >> "$output_objdir/$my_dlsyms" "\ } }" fi echo >> "$output_objdir/$my_dlsyms" "\ LT_DLSYM_CONST lt_dlsymlist lt_${my_prefix}_LTX_preloaded_symbols[] = { {\"$my_originator\", (void *) 0}," if test -s "$nlist"I; then echo >> "$output_objdir/$my_dlsyms" "\ {\"@INIT@\", (void *) <_syminit}," fi case $need_lib_prefix in no) eval "$global_symbol_to_c_name_address" < "$nlist" >> "$output_objdir/$my_dlsyms" ;; *) eval "$global_symbol_to_c_name_address_lib_prefix" < "$nlist" >> "$output_objdir/$my_dlsyms" ;; esac echo >> "$output_objdir/$my_dlsyms" "\ {0, (void *) 0} }; /* This works around a problem in FreeBSD linker */ #ifdef FREEBSD_WORKAROUND static const void *lt_preloaded_setup() { return lt_${my_prefix}_LTX_preloaded_symbols; } #endif #ifdef __cplusplus } #endif\ " } # !$opt_dry_run pic_flag_for_symtable= case "$compile_command " in *" -static "*) ;; *) case $host in # compiling the symbol table file with pic_flag works around # a FreeBSD bug that causes programs to crash when -lm is # linked before any other PIC object. But we must not use # pic_flag when linking with -static. The problem exists in # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1. *-*-freebsd2.*|*-*-freebsd3.0*|*-*-freebsdelf3.0*) pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND" ;; *-*-hpux*) pic_flag_for_symtable=" $pic_flag" ;; *) $my_pic_p && pic_flag_for_symtable=" $pic_flag" ;; esac ;; esac symtab_cflags= for arg in $LTCFLAGS; do case $arg in -pie | -fpie | -fPIE) ;; *) func_append symtab_cflags " $arg" ;; esac done # Now compile the dynamic symbol file. func_show_eval '(cd $output_objdir && $LTCC$symtab_cflags -c$no_builtin_flag$pic_flag_for_symtable "$my_dlsyms")' 'exit $?' # Clean up the generated files. func_show_eval '$RM "$output_objdir/$my_dlsyms" "$nlist" "${nlist}S" "${nlist}T" "${nlist}I"' # Transform the symbol file into the correct name. symfileobj=$output_objdir/${my_outputname}S.$objext case $host in *cygwin* | *mingw* | *cegcc* ) if test -f "$output_objdir/$my_outputname.def"; then compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` else compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"` finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"` fi ;; *) compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"` finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"` ;; esac ;; *) func_fatal_error "unknown suffix for '$my_dlsyms'" ;; esac else # We keep going just in case the user didn't refer to # lt_preloaded_symbols. The linker will fail if global_symbol_pipe # really was required. # Nullify the symbol file. compile_command=`$ECHO "$compile_command" | $SED "s% @SYMFILE@%%"` finalize_command=`$ECHO "$finalize_command" | $SED "s% @SYMFILE@%%"` fi } # func_cygming_gnu_implib_p ARG # This predicate returns with zero status (TRUE) if # ARG is a GNU/binutils-style import library. Returns # with nonzero status (FALSE) otherwise. func_cygming_gnu_implib_p () { $debug_cmd func_to_tool_file "$1" func_convert_file_msys_to_w32 func_cygming_gnu_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $EGREP ' (_head_[A-Za-z0-9_]+_[ad]l*|[A-Za-z0-9_]+_[ad]l*_iname)$'` test -n "$func_cygming_gnu_implib_tmp" } # func_cygming_ms_implib_p ARG # This predicate returns with zero status (TRUE) if # ARG is an MS-style import library. Returns # with nonzero status (FALSE) otherwise. func_cygming_ms_implib_p () { $debug_cmd func_to_tool_file "$1" func_convert_file_msys_to_w32 func_cygming_ms_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $GREP '_NULL_IMPORT_DESCRIPTOR'` test -n "$func_cygming_ms_implib_tmp" } # func_win32_libid arg # return the library type of file 'arg' # # Need a lot of goo to handle *both* DLLs and import libs # Has to be a shell function in order to 'eat' the argument # that is supplied when $file_magic_command is called. # Despite the name, also deal with 64 bit binaries. func_win32_libid () { $debug_cmd win32_libid_type=unknown win32_fileres=`file -L $1 2>/dev/null` case $win32_fileres in *ar\ archive\ import\ library*) # definitely import win32_libid_type="x86 archive import" ;; *ar\ archive*) # could be an import, or static # Keep the egrep pattern in sync with the one in _LT_CHECK_MAGIC_METHOD. if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null | $EGREP 'file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' >/dev/null; then case $nm_interface in "MS dumpbin") if func_cygming_ms_implib_p "$1" || func_cygming_gnu_implib_p "$1" then win32_nmres=import else win32_nmres= fi ;; *) func_to_tool_file "$1" func_convert_file_msys_to_w32 win32_nmres=`eval $NM -f posix -A \"$func_to_tool_file_result\" | $SED -n -e ' 1,100{ / I /{ s|.*|import| p q } }'` ;; esac case $win32_nmres in import*) win32_libid_type="x86 archive import";; *) win32_libid_type="x86 archive static";; esac fi ;; *DLL*) win32_libid_type="x86 DLL" ;; *executable*) # but shell scripts are "executable" too... case $win32_fileres in *MS\ Windows\ PE\ Intel*) win32_libid_type="x86 DLL" ;; esac ;; esac $ECHO "$win32_libid_type" } # func_cygming_dll_for_implib ARG # # Platform-specific function to extract the # name of the DLL associated with the specified # import library ARG. # Invoked by eval'ing the libtool variable # $sharedlib_from_linklib_cmd # Result is available in the variable # $sharedlib_from_linklib_result func_cygming_dll_for_implib () { $debug_cmd sharedlib_from_linklib_result=`$DLLTOOL --identify-strict --identify "$1"` } # func_cygming_dll_for_implib_fallback_core SECTION_NAME LIBNAMEs # # The is the core of a fallback implementation of a # platform-specific function to extract the name of the # DLL associated with the specified import library LIBNAME. # # SECTION_NAME is either .idata$6 or .idata$7, depending # on the platform and compiler that created the implib. # # Echos the name of the DLL associated with the # specified import library. func_cygming_dll_for_implib_fallback_core () { $debug_cmd match_literal=`$ECHO "$1" | $SED "$sed_make_literal_regex"` $OBJDUMP -s --section "$1" "$2" 2>/dev/null | $SED '/^Contents of section '"$match_literal"':/{ # Place marker at beginning of archive member dllname section s/.*/====MARK====/ p d } # These lines can sometimes be longer than 43 characters, but # are always uninteresting /:[ ]*file format pe[i]\{,1\}-/d /^In archive [^:]*:/d # Ensure marker is printed /^====MARK====/p # Remove all lines with less than 43 characters /^.\{43\}/!d # From remaining lines, remove first 43 characters s/^.\{43\}//' | $SED -n ' # Join marker and all lines until next marker into a single line /^====MARK====/ b para H $ b para b :para x s/\n//g # Remove the marker s/^====MARK====// # Remove trailing dots and whitespace s/[\. \t]*$// # Print /./p' | # we now have a list, one entry per line, of the stringified # contents of the appropriate section of all members of the # archive that possess that section. Heuristic: eliminate # all those that have a first or second character that is # a '.' (that is, objdump's representation of an unprintable # character.) This should work for all archives with less than # 0x302f exports -- but will fail for DLLs whose name actually # begins with a literal '.' or a single character followed by # a '.'. # # Of those that remain, print the first one. $SED -e '/^\./d;/^.\./d;q' } # func_cygming_dll_for_implib_fallback ARG # Platform-specific function to extract the # name of the DLL associated with the specified # import library ARG. # # This fallback implementation is for use when $DLLTOOL # does not support the --identify-strict option. # Invoked by eval'ing the libtool variable # $sharedlib_from_linklib_cmd # Result is available in the variable # $sharedlib_from_linklib_result func_cygming_dll_for_implib_fallback () { $debug_cmd if func_cygming_gnu_implib_p "$1"; then # binutils import library sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$7' "$1"` elif func_cygming_ms_implib_p "$1"; then # ms-generated import library sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$6' "$1"` else # unknown sharedlib_from_linklib_result= fi } # func_extract_an_archive dir oldlib func_extract_an_archive () { $debug_cmd f_ex_an_ar_dir=$1; shift f_ex_an_ar_oldlib=$1 if test yes = "$lock_old_archive_extraction"; then lockfile=$f_ex_an_ar_oldlib.lock until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do func_echo "Waiting for $lockfile to be removed" sleep 2 done fi func_show_eval "(cd \$f_ex_an_ar_dir && $AR x \"\$f_ex_an_ar_oldlib\")" \ 'stat=$?; rm -f "$lockfile"; exit $stat' if test yes = "$lock_old_archive_extraction"; then $opt_dry_run || rm -f "$lockfile" fi if ($AR t "$f_ex_an_ar_oldlib" | sort | sort -uc >/dev/null 2>&1); then : else func_fatal_error "object name conflicts in archive: $f_ex_an_ar_dir/$f_ex_an_ar_oldlib" fi } # func_extract_archives gentop oldlib ... func_extract_archives () { $debug_cmd my_gentop=$1; shift my_oldlibs=${1+"$@"} my_oldobjs= my_xlib= my_xabs= my_xdir= for my_xlib in $my_oldlibs; do # Extract the objects. case $my_xlib in [\\/]* | [A-Za-z]:[\\/]*) my_xabs=$my_xlib ;; *) my_xabs=`pwd`"/$my_xlib" ;; esac func_basename "$my_xlib" my_xlib=$func_basename_result my_xlib_u=$my_xlib while :; do case " $extracted_archives " in *" $my_xlib_u "*) func_arith $extracted_serial + 1 extracted_serial=$func_arith_result my_xlib_u=lt$extracted_serial-$my_xlib ;; *) break ;; esac done extracted_archives="$extracted_archives $my_xlib_u" my_xdir=$my_gentop/$my_xlib_u func_mkdir_p "$my_xdir" case $host in *-darwin*) func_verbose "Extracting $my_xabs" # Do not bother doing anything if just a dry run $opt_dry_run || { darwin_orig_dir=`pwd` cd $my_xdir || exit $? darwin_archive=$my_xabs darwin_curdir=`pwd` func_basename "$darwin_archive" darwin_base_archive=$func_basename_result darwin_arches=`$LIPO -info "$darwin_archive" 2>/dev/null | $GREP Architectures 2>/dev/null || true` if test -n "$darwin_arches"; then darwin_arches=`$ECHO "$darwin_arches" | $SED -e 's/.*are://'` darwin_arch= func_verbose "$darwin_base_archive has multiple architectures $darwin_arches" for darwin_arch in $darwin_arches; do func_mkdir_p "unfat-$$/$darwin_base_archive-$darwin_arch" $LIPO -thin $darwin_arch -output "unfat-$$/$darwin_base_archive-$darwin_arch/$darwin_base_archive" "$darwin_archive" cd "unfat-$$/$darwin_base_archive-$darwin_arch" func_extract_an_archive "`pwd`" "$darwin_base_archive" cd "$darwin_curdir" $RM "unfat-$$/$darwin_base_archive-$darwin_arch/$darwin_base_archive" done # $darwin_arches ## Okay now we've a bunch of thin objects, gotta fatten them up :) darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print | $SED -e "$sed_basename" | sort -u` darwin_file= darwin_files= for darwin_file in $darwin_filelist; do darwin_files=`find unfat-$$ -name $darwin_file -print | sort | $NL2SP` $LIPO -create -output "$darwin_file" $darwin_files done # $darwin_filelist $RM -rf unfat-$$ cd "$darwin_orig_dir" else cd $darwin_orig_dir func_extract_an_archive "$my_xdir" "$my_xabs" fi # $darwin_arches } # !$opt_dry_run ;; *) func_extract_an_archive "$my_xdir" "$my_xabs" ;; esac my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | sort | $NL2SP` done func_extract_archives_result=$my_oldobjs } # func_emit_wrapper [arg=no] # # Emit a libtool wrapper script on stdout. # Don't directly open a file because we may want to # incorporate the script contents within a cygwin/mingw # wrapper executable. Must ONLY be called from within # func_mode_link because it depends on a number of variables # set therein. # # ARG is the value that the WRAPPER_SCRIPT_BELONGS_IN_OBJDIR # variable will take. If 'yes', then the emitted script # will assume that the directory where it is stored is # the $objdir directory. This is a cygwin/mingw-specific # behavior. func_emit_wrapper () { func_emit_wrapper_arg1=${1-no} $ECHO "\ #! $SHELL # $output - temporary wrapper script for $objdir/$outputname # Generated by $PROGRAM (GNU $PACKAGE) $VERSION # # The $output program cannot be directly executed until all the libtool # libraries that it depends on are installed. # # This wrapper script should never be moved out of the build directory. # If it is, it will not operate correctly. # Sed substitution that helps us do robust quoting. It backslashifies # metacharacters that are still active within double-quoted strings. sed_quote_subst='$sed_quote_subst' # Be Bourne compatible if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: # Zsh 3.x and 4.x performs word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case \`(set -o) 2>/dev/null\` in *posix*) set -o posix;; esac fi BIN_SH=xpg4; export BIN_SH # for Tru64 DUALCASE=1; export DUALCASE # for MKS sh # The HP-UX ksh and POSIX shell print the target directory to stdout # if CDPATH is set. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH relink_command=\"$relink_command\" # This environment variable determines our operation mode. if test \"\$libtool_install_magic\" = \"$magic\"; then # install mode needs the following variables: generated_by_libtool_version='$macro_version' notinst_deplibs='$notinst_deplibs' else # When we are sourced in execute mode, \$file and \$ECHO are already set. if test \"\$libtool_execute_magic\" != \"$magic\"; then file=\"\$0\"" qECHO=`$ECHO "$ECHO" | $SED "$sed_quote_subst"` $ECHO "\ # A function that is used when there is no print builtin or printf. func_fallback_echo () { eval 'cat <<_LTECHO_EOF \$1 _LTECHO_EOF' } ECHO=\"$qECHO\" fi # Very basic option parsing. These options are (a) specific to # the libtool wrapper, (b) are identical between the wrapper # /script/ and the wrapper /executable/ that is used only on # windows platforms, and (c) all begin with the string "--lt-" # (application programs are unlikely to have options that match # this pattern). # # There are only two supported options: --lt-debug and # --lt-dump-script. There is, deliberately, no --lt-help. # # The first argument to this parsing function should be the # script's $0 value, followed by "$@". lt_option_debug= func_parse_lt_options () { lt_script_arg0=\$0 shift for lt_opt do case \"\$lt_opt\" in --lt-debug) lt_option_debug=1 ;; --lt-dump-script) lt_dump_D=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%/[^/]*$%%'\` test \"X\$lt_dump_D\" = \"X\$lt_script_arg0\" && lt_dump_D=. lt_dump_F=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%^.*/%%'\` cat \"\$lt_dump_D/\$lt_dump_F\" exit 0 ;; --lt-*) \$ECHO \"Unrecognized --lt- option: '\$lt_opt'\" 1>&2 exit 1 ;; esac done # Print the debug banner immediately: if test -n \"\$lt_option_debug\"; then echo \"$outputname:$output:\$LINENO: libtool wrapper (GNU $PACKAGE) $VERSION\" 1>&2 fi } # Used when --lt-debug. Prints its arguments to stdout # (redirection is the responsibility of the caller) func_lt_dump_args () { lt_dump_args_N=1; for lt_arg do \$ECHO \"$outputname:$output:\$LINENO: newargv[\$lt_dump_args_N]: \$lt_arg\" lt_dump_args_N=\`expr \$lt_dump_args_N + 1\` done } # Core function for launching the target application func_exec_program_core () { " case $host in # Backslashes separate directories on plain windows *-*-mingw | *-*-os2* | *-cegcc*) $ECHO "\ if test -n \"\$lt_option_debug\"; then \$ECHO \"$outputname:$output:\$LINENO: newargv[0]: \$progdir\\\\\$program\" 1>&2 func_lt_dump_args \${1+\"\$@\"} 1>&2 fi exec \"\$progdir\\\\\$program\" \${1+\"\$@\"} " ;; *) $ECHO "\ if test -n \"\$lt_option_debug\"; then \$ECHO \"$outputname:$output:\$LINENO: newargv[0]: \$progdir/\$program\" 1>&2 func_lt_dump_args \${1+\"\$@\"} 1>&2 fi exec \"\$progdir/\$program\" \${1+\"\$@\"} " ;; esac $ECHO "\ \$ECHO \"\$0: cannot exec \$program \$*\" 1>&2 exit 1 } # A function to encapsulate launching the target application # Strips options in the --lt-* namespace from \$@ and # launches target application with the remaining arguments. func_exec_program () { case \" \$* \" in *\\ --lt-*) for lt_wr_arg do case \$lt_wr_arg in --lt-*) ;; *) set x \"\$@\" \"\$lt_wr_arg\"; shift;; esac shift done ;; esac func_exec_program_core \${1+\"\$@\"} } # Parse options func_parse_lt_options \"\$0\" \${1+\"\$@\"} # Find the directory that this script lives in. thisdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*$%%'\` test \"x\$thisdir\" = \"x\$file\" && thisdir=. # Follow symbolic links until we get to the real thisdir. file=\`ls -ld \"\$file\" | $SED -n 's/.*-> //p'\` while test -n \"\$file\"; do destdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*\$%%'\` # If there was a directory component, then change thisdir. if test \"x\$destdir\" != \"x\$file\"; then case \"\$destdir\" in [\\\\/]* | [A-Za-z]:[\\\\/]*) thisdir=\"\$destdir\" ;; *) thisdir=\"\$thisdir/\$destdir\" ;; esac fi file=\`\$ECHO \"\$file\" | $SED 's%^.*/%%'\` file=\`ls -ld \"\$thisdir/\$file\" | $SED -n 's/.*-> //p'\` done # Usually 'no', except on cygwin/mingw when embedded into # the cwrapper. WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=$func_emit_wrapper_arg1 if test \"\$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR\" = \"yes\"; then # special case for '.' if test \"\$thisdir\" = \".\"; then thisdir=\`pwd\` fi # remove .libs from thisdir case \"\$thisdir\" in *[\\\\/]$objdir ) thisdir=\`\$ECHO \"\$thisdir\" | $SED 's%[\\\\/][^\\\\/]*$%%'\` ;; $objdir ) thisdir=. ;; esac fi # Try to get the absolute directory name. absdir=\`cd \"\$thisdir\" && pwd\` test -n \"\$absdir\" && thisdir=\"\$absdir\" " if test yes = "$fast_install"; then $ECHO "\ program=lt-'$outputname'$exeext progdir=\"\$thisdir/$objdir\" if test ! -f \"\$progdir/\$program\" || { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | $SED 1q\`; \\ test \"X\$file\" != \"X\$progdir/\$program\"; }; then file=\"\$\$-\$program\" if test ! -d \"\$progdir\"; then $MKDIR \"\$progdir\" else $RM \"\$progdir/\$file\" fi" $ECHO "\ # relink executable if necessary if test -n \"\$relink_command\"; then if relink_command_output=\`eval \$relink_command 2>&1\`; then : else \$ECHO \"\$relink_command_output\" >&2 $RM \"\$progdir/\$file\" exit 1 fi fi $MV \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null || { $RM \"\$progdir/\$program\"; $MV \"\$progdir/\$file\" \"\$progdir/\$program\"; } $RM \"\$progdir/\$file\" fi" else $ECHO "\ program='$outputname' progdir=\"\$thisdir/$objdir\" " fi $ECHO "\ if test -f \"\$progdir/\$program\"; then" # fixup the dll searchpath if we need to. # # Fix the DLL searchpath if we need to. Do this before prepending # to shlibpath, because on Windows, both are PATH and uninstalled # libraries must come first. if test -n "$dllsearchpath"; then $ECHO "\ # Add the dll search path components to the executable PATH PATH=$dllsearchpath:\$PATH " fi # Export our shlibpath_var if we have one. if test yes = "$shlibpath_overrides_runpath" && test -n "$shlibpath_var" && test -n "$temp_rpath"; then $ECHO "\ # Add our own library path to $shlibpath_var $shlibpath_var=\"$temp_rpath\$$shlibpath_var\" # Some systems cannot cope with colon-terminated $shlibpath_var # The second colon is a workaround for a bug in BeOS R4 sed $shlibpath_var=\`\$ECHO \"\$$shlibpath_var\" | $SED 's/::*\$//'\` export $shlibpath_var " fi $ECHO "\ if test \"\$libtool_execute_magic\" != \"$magic\"; then # Run the actual program with our arguments. func_exec_program \${1+\"\$@\"} fi else # The program doesn't exist. \$ECHO \"\$0: error: '\$progdir/\$program' does not exist\" 1>&2 \$ECHO \"This script is just a wrapper for \$program.\" 1>&2 \$ECHO \"See the $PACKAGE documentation for more information.\" 1>&2 exit 1 fi fi\ " } # func_emit_cwrapperexe_src # emit the source code for a wrapper executable on stdout # Must ONLY be called from within func_mode_link because # it depends on a number of variable set therein. func_emit_cwrapperexe_src () { cat < #include #ifdef _MSC_VER # include # include # include #else # include # include # ifdef __CYGWIN__ # include # endif #endif #include #include #include #include #include #include #include #include #define STREQ(s1, s2) (strcmp ((s1), (s2)) == 0) /* declarations of non-ANSI functions */ #if defined __MINGW32__ # ifdef __STRICT_ANSI__ int _putenv (const char *); # endif #elif defined __CYGWIN__ # ifdef __STRICT_ANSI__ char *realpath (const char *, char *); int putenv (char *); int setenv (const char *, const char *, int); # endif /* #elif defined other_platform || defined ... */ #endif /* portability defines, excluding path handling macros */ #if defined _MSC_VER # define setmode _setmode # define stat _stat # define chmod _chmod # define getcwd _getcwd # define putenv _putenv # define S_IXUSR _S_IEXEC #elif defined __MINGW32__ # define setmode _setmode # define stat _stat # define chmod _chmod # define getcwd _getcwd # define putenv _putenv #elif defined __CYGWIN__ # define HAVE_SETENV # define FOPEN_WB "wb" /* #elif defined other platforms ... */ #endif #if defined PATH_MAX # define LT_PATHMAX PATH_MAX #elif defined MAXPATHLEN # define LT_PATHMAX MAXPATHLEN #else # define LT_PATHMAX 1024 #endif #ifndef S_IXOTH # define S_IXOTH 0 #endif #ifndef S_IXGRP # define S_IXGRP 0 #endif /* path handling portability macros */ #ifndef DIR_SEPARATOR # define DIR_SEPARATOR '/' # define PATH_SEPARATOR ':' #endif #if defined _WIN32 || defined __MSDOS__ || defined __DJGPP__ || \ defined __OS2__ # define HAVE_DOS_BASED_FILE_SYSTEM # define FOPEN_WB "wb" # ifndef DIR_SEPARATOR_2 # define DIR_SEPARATOR_2 '\\' # endif # ifndef PATH_SEPARATOR_2 # define PATH_SEPARATOR_2 ';' # endif #endif #ifndef DIR_SEPARATOR_2 # define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR) #else /* DIR_SEPARATOR_2 */ # define IS_DIR_SEPARATOR(ch) \ (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2)) #endif /* DIR_SEPARATOR_2 */ #ifndef PATH_SEPARATOR_2 # define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR) #else /* PATH_SEPARATOR_2 */ # define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR_2) #endif /* PATH_SEPARATOR_2 */ #ifndef FOPEN_WB # define FOPEN_WB "w" #endif #ifndef _O_BINARY # define _O_BINARY 0 #endif #define XMALLOC(type, num) ((type *) xmalloc ((num) * sizeof(type))) #define XFREE(stale) do { \ if (stale) { free (stale); stale = 0; } \ } while (0) #if defined LT_DEBUGWRAPPER static int lt_debug = 1; #else static int lt_debug = 0; #endif const char *program_name = "libtool-wrapper"; /* in case xstrdup fails */ void *xmalloc (size_t num); char *xstrdup (const char *string); const char *base_name (const char *name); char *find_executable (const char *wrapper); char *chase_symlinks (const char *pathspec); int make_executable (const char *path); int check_executable (const char *path); char *strendzap (char *str, const char *pat); void lt_debugprintf (const char *file, int line, const char *fmt, ...); void lt_fatal (const char *file, int line, const char *message, ...); static const char *nonnull (const char *s); static const char *nonempty (const char *s); void lt_setenv (const char *name, const char *value); char *lt_extend_str (const char *orig_value, const char *add, int to_end); void lt_update_exe_path (const char *name, const char *value); void lt_update_lib_path (const char *name, const char *value); char **prepare_spawn (char **argv); void lt_dump_script (FILE *f); EOF cat <= 0) && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) return 1; else return 0; } int make_executable (const char *path) { int rval = 0; struct stat st; lt_debugprintf (__FILE__, __LINE__, "(make_executable): %s\n", nonempty (path)); if ((!path) || (!*path)) return 0; if (stat (path, &st) >= 0) { rval = chmod (path, st.st_mode | S_IXOTH | S_IXGRP | S_IXUSR); } return rval; } /* Searches for the full path of the wrapper. Returns newly allocated full path name if found, NULL otherwise Does not chase symlinks, even on platforms that support them. */ char * find_executable (const char *wrapper) { int has_slash = 0; const char *p; const char *p_next; /* static buffer for getcwd */ char tmp[LT_PATHMAX + 1]; size_t tmp_len; char *concat_name; lt_debugprintf (__FILE__, __LINE__, "(find_executable): %s\n", nonempty (wrapper)); if ((wrapper == NULL) || (*wrapper == '\0')) return NULL; /* Absolute path? */ #if defined HAVE_DOS_BASED_FILE_SYSTEM if (isalpha ((unsigned char) wrapper[0]) && wrapper[1] == ':') { concat_name = xstrdup (wrapper); if (check_executable (concat_name)) return concat_name; XFREE (concat_name); } else { #endif if (IS_DIR_SEPARATOR (wrapper[0])) { concat_name = xstrdup (wrapper); if (check_executable (concat_name)) return concat_name; XFREE (concat_name); } #if defined HAVE_DOS_BASED_FILE_SYSTEM } #endif for (p = wrapper; *p; p++) if (*p == '/') { has_slash = 1; break; } if (!has_slash) { /* no slashes; search PATH */ const char *path = getenv ("PATH"); if (path != NULL) { for (p = path; *p; p = p_next) { const char *q; size_t p_len; for (q = p; *q; q++) if (IS_PATH_SEPARATOR (*q)) break; p_len = (size_t) (q - p); p_next = (*q == '\0' ? q : q + 1); if (p_len == 0) { /* empty path: current directory */ if (getcwd (tmp, LT_PATHMAX) == NULL) lt_fatal (__FILE__, __LINE__, "getcwd failed: %s", nonnull (strerror (errno))); tmp_len = strlen (tmp); concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); memcpy (concat_name, tmp, tmp_len); concat_name[tmp_len] = '/'; strcpy (concat_name + tmp_len + 1, wrapper); } else { concat_name = XMALLOC (char, p_len + 1 + strlen (wrapper) + 1); memcpy (concat_name, p, p_len); concat_name[p_len] = '/'; strcpy (concat_name + p_len + 1, wrapper); } if (check_executable (concat_name)) return concat_name; XFREE (concat_name); } } /* not found in PATH; assume curdir */ } /* Relative path | not found in path: prepend cwd */ if (getcwd (tmp, LT_PATHMAX) == NULL) lt_fatal (__FILE__, __LINE__, "getcwd failed: %s", nonnull (strerror (errno))); tmp_len = strlen (tmp); concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); memcpy (concat_name, tmp, tmp_len); concat_name[tmp_len] = '/'; strcpy (concat_name + tmp_len + 1, wrapper); if (check_executable (concat_name)) return concat_name; XFREE (concat_name); return NULL; } char * chase_symlinks (const char *pathspec) { #ifndef S_ISLNK return xstrdup (pathspec); #else char buf[LT_PATHMAX]; struct stat s; char *tmp_pathspec = xstrdup (pathspec); char *p; int has_symlinks = 0; while (strlen (tmp_pathspec) && !has_symlinks) { lt_debugprintf (__FILE__, __LINE__, "checking path component for symlinks: %s\n", tmp_pathspec); if (lstat (tmp_pathspec, &s) == 0) { if (S_ISLNK (s.st_mode) != 0) { has_symlinks = 1; break; } /* search backwards for last DIR_SEPARATOR */ p = tmp_pathspec + strlen (tmp_pathspec) - 1; while ((p > tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) p--; if ((p == tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) { /* no more DIR_SEPARATORS left */ break; } *p = '\0'; } else { lt_fatal (__FILE__, __LINE__, "error accessing file \"%s\": %s", tmp_pathspec, nonnull (strerror (errno))); } } XFREE (tmp_pathspec); if (!has_symlinks) { return xstrdup (pathspec); } tmp_pathspec = realpath (pathspec, buf); if (tmp_pathspec == 0) { lt_fatal (__FILE__, __LINE__, "could not follow symlinks for %s", pathspec); } return xstrdup (tmp_pathspec); #endif } char * strendzap (char *str, const char *pat) { size_t len, patlen; assert (str != NULL); assert (pat != NULL); len = strlen (str); patlen = strlen (pat); if (patlen <= len) { str += len - patlen; if (STREQ (str, pat)) *str = '\0'; } return str; } void lt_debugprintf (const char *file, int line, const char *fmt, ...) { va_list args; if (lt_debug) { (void) fprintf (stderr, "%s:%s:%d: ", program_name, file, line); va_start (args, fmt); (void) vfprintf (stderr, fmt, args); va_end (args); } } static void lt_error_core (int exit_status, const char *file, int line, const char *mode, const char *message, va_list ap) { fprintf (stderr, "%s:%s:%d: %s: ", program_name, file, line, mode); vfprintf (stderr, message, ap); fprintf (stderr, ".\n"); if (exit_status >= 0) exit (exit_status); } void lt_fatal (const char *file, int line, const char *message, ...) { va_list ap; va_start (ap, message); lt_error_core (EXIT_FAILURE, file, line, "FATAL", message, ap); va_end (ap); } static const char * nonnull (const char *s) { return s ? s : "(null)"; } static const char * nonempty (const char *s) { return (s && !*s) ? "(empty)" : nonnull (s); } void lt_setenv (const char *name, const char *value) { lt_debugprintf (__FILE__, __LINE__, "(lt_setenv) setting '%s' to '%s'\n", nonnull (name), nonnull (value)); { #ifdef HAVE_SETENV /* always make a copy, for consistency with !HAVE_SETENV */ char *str = xstrdup (value); setenv (name, str, 1); #else size_t len = strlen (name) + 1 + strlen (value) + 1; char *str = XMALLOC (char, len); sprintf (str, "%s=%s", name, value); if (putenv (str) != EXIT_SUCCESS) { XFREE (str); } #endif } } char * lt_extend_str (const char *orig_value, const char *add, int to_end) { char *new_value; if (orig_value && *orig_value) { size_t orig_value_len = strlen (orig_value); size_t add_len = strlen (add); new_value = XMALLOC (char, add_len + orig_value_len + 1); if (to_end) { strcpy (new_value, orig_value); strcpy (new_value + orig_value_len, add); } else { strcpy (new_value, add); strcpy (new_value + add_len, orig_value); } } else { new_value = xstrdup (add); } return new_value; } void lt_update_exe_path (const char *name, const char *value) { lt_debugprintf (__FILE__, __LINE__, "(lt_update_exe_path) modifying '%s' by prepending '%s'\n", nonnull (name), nonnull (value)); if (name && *name && value && *value) { char *new_value = lt_extend_str (getenv (name), value, 0); /* some systems can't cope with a ':'-terminated path #' */ size_t len = strlen (new_value); while ((len > 0) && IS_PATH_SEPARATOR (new_value[len-1])) { new_value[--len] = '\0'; } lt_setenv (name, new_value); XFREE (new_value); } } void lt_update_lib_path (const char *name, const char *value) { lt_debugprintf (__FILE__, __LINE__, "(lt_update_lib_path) modifying '%s' by prepending '%s'\n", nonnull (name), nonnull (value)); if (name && *name && value && *value) { char *new_value = lt_extend_str (getenv (name), value, 0); lt_setenv (name, new_value); XFREE (new_value); } } EOF case $host_os in mingw*) cat <<"EOF" /* Prepares an argument vector before calling spawn(). Note that spawn() does not by itself call the command interpreter (getenv ("COMSPEC") != NULL ? getenv ("COMSPEC") : ({ OSVERSIONINFO v; v.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&v); v.dwPlatformId == VER_PLATFORM_WIN32_NT; }) ? "cmd.exe" : "command.com"). Instead it simply concatenates the arguments, separated by ' ', and calls CreateProcess(). We must quote the arguments since Win32 CreateProcess() interprets characters like ' ', '\t', '\\', '"' (but not '<' and '>') in a special way: - Space and tab are interpreted as delimiters. They are not treated as delimiters if they are surrounded by double quotes: "...". - Unescaped double quotes are removed from the input. Their only effect is that within double quotes, space and tab are treated like normal characters. - Backslashes not followed by double quotes are not special. - But 2*n+1 backslashes followed by a double quote become n backslashes followed by a double quote (n >= 0): \" -> " \\\" -> \" \\\\\" -> \\" */ #define SHELL_SPECIAL_CHARS "\"\\ \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" #define SHELL_SPACE_CHARS " \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" char ** prepare_spawn (char **argv) { size_t argc; char **new_argv; size_t i; /* Count number of arguments. */ for (argc = 0; argv[argc] != NULL; argc++) ; /* Allocate new argument vector. */ new_argv = XMALLOC (char *, argc + 1); /* Put quoted arguments into the new argument vector. */ for (i = 0; i < argc; i++) { const char *string = argv[i]; if (string[0] == '\0') new_argv[i] = xstrdup ("\"\""); else if (strpbrk (string, SHELL_SPECIAL_CHARS) != NULL) { int quote_around = (strpbrk (string, SHELL_SPACE_CHARS) != NULL); size_t length; unsigned int backslashes; const char *s; char *quoted_string; char *p; length = 0; backslashes = 0; if (quote_around) length++; for (s = string; *s != '\0'; s++) { char c = *s; if (c == '"') length += backslashes + 1; length++; if (c == '\\') backslashes++; else backslashes = 0; } if (quote_around) length += backslashes + 1; quoted_string = XMALLOC (char, length + 1); p = quoted_string; backslashes = 0; if (quote_around) *p++ = '"'; for (s = string; *s != '\0'; s++) { char c = *s; if (c == '"') { unsigned int j; for (j = backslashes + 1; j > 0; j--) *p++ = '\\'; } *p++ = c; if (c == '\\') backslashes++; else backslashes = 0; } if (quote_around) { unsigned int j; for (j = backslashes; j > 0; j--) *p++ = '\\'; *p++ = '"'; } *p = '\0'; new_argv[i] = quoted_string; } else new_argv[i] = (char *) string; } new_argv[argc] = NULL; return new_argv; } EOF ;; esac cat <<"EOF" void lt_dump_script (FILE* f) { EOF func_emit_wrapper yes | $SED -n -e ' s/^\(.\{79\}\)\(..*\)/\1\ \2/ h s/\([\\"]\)/\\\1/g s/$/\\n/ s/\([^\n]*\).*/ fputs ("\1", f);/p g D' cat <<"EOF" } EOF } # end: func_emit_cwrapperexe_src # func_win32_import_lib_p ARG # True if ARG is an import lib, as indicated by $file_magic_cmd func_win32_import_lib_p () { $debug_cmd case `eval $file_magic_cmd \"\$1\" 2>/dev/null | $SED -e 10q` in *import*) : ;; *) false ;; esac } # func_suncc_cstd_abi # !!ONLY CALL THIS FOR SUN CC AFTER $compile_command IS FULLY EXPANDED!! # Several compiler flags select an ABI that is incompatible with the # Cstd library. Avoid specifying it if any are in CXXFLAGS. func_suncc_cstd_abi () { $debug_cmd case " $compile_command " in *" -compat=g "*|*\ -std=c++[0-9][0-9]\ *|*" -library=stdcxx4 "*|*" -library=stlport4 "*) suncc_use_cstd_abi=no ;; *) suncc_use_cstd_abi=yes ;; esac } # func_mode_link arg... func_mode_link () { $debug_cmd case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) # It is impossible to link a dll without this setting, and # we shouldn't force the makefile maintainer to figure out # what system we are compiling for in order to pass an extra # flag for every libtool invocation. # allow_undefined=no # FIXME: Unfortunately, there are problems with the above when trying # to make a dll that has undefined symbols, in which case not # even a static library is built. For now, we need to specify # -no-undefined on the libtool link line when we can be certain # that all symbols are satisfied, otherwise we get a static library. allow_undefined=yes ;; *) allow_undefined=yes ;; esac libtool_args=$nonopt base_compile="$nonopt $@" compile_command=$nonopt finalize_command=$nonopt compile_rpath= finalize_rpath= compile_shlibpath= finalize_shlibpath= convenience= old_convenience= deplibs= old_deplibs= compiler_flags= linker_flags= dllsearchpath= lib_search_path=`pwd` inst_prefix_dir= new_inherited_linker_flags= avoid_version=no bindir= dlfiles= dlprefiles= dlself=no export_dynamic=no export_symbols= export_symbols_regex= generated= libobjs= ltlibs= module=no no_install=no objs= os2dllname= non_pic_objects= precious_files_regex= prefer_static_libs=no preload=false prev= prevarg= release= rpath= xrpath= perm_rpath= temp_rpath= thread_safe=no vinfo= vinfo_number=no weak_libs= single_module=$wl-single_module func_infer_tag $base_compile # We need to know -static, to get the right output filenames. for arg do case $arg in -shared) test yes != "$build_libtool_libs" \ && func_fatal_configuration "cannot build a shared library" build_old_libs=no break ;; -all-static | -static | -static-libtool-libs) case $arg in -all-static) if test yes = "$build_libtool_libs" && test -z "$link_static_flag"; then func_warning "complete static linking is impossible in this configuration" fi if test -n "$link_static_flag"; then dlopen_self=$dlopen_self_static fi prefer_static_libs=yes ;; -static) if test -z "$pic_flag" && test -n "$link_static_flag"; then dlopen_self=$dlopen_self_static fi prefer_static_libs=built ;; -static-libtool-libs) if test -z "$pic_flag" && test -n "$link_static_flag"; then dlopen_self=$dlopen_self_static fi prefer_static_libs=yes ;; esac build_libtool_libs=no build_old_libs=yes break ;; esac done # See if our shared archives depend on static archives. test -n "$old_archive_from_new_cmds" && build_old_libs=yes # Go through the arguments, transforming them on the way. while test "$#" -gt 0; do arg=$1 shift func_quote_for_eval "$arg" qarg=$func_quote_for_eval_unquoted_result func_append libtool_args " $func_quote_for_eval_result" # If the previous option needs an argument, assign it. if test -n "$prev"; then case $prev in output) func_append compile_command " @OUTPUT@" func_append finalize_command " @OUTPUT@" ;; esac case $prev in bindir) bindir=$arg prev= continue ;; dlfiles|dlprefiles) $preload || { # Add the symbol object into the linking commands. func_append compile_command " @SYMFILE@" func_append finalize_command " @SYMFILE@" preload=: } case $arg in *.la | *.lo) ;; # We handle these cases below. force) if test no = "$dlself"; then dlself=needless export_dynamic=yes fi prev= continue ;; self) if test dlprefiles = "$prev"; then dlself=yes elif test dlfiles = "$prev" && test yes != "$dlopen_self"; then dlself=yes else dlself=needless export_dynamic=yes fi prev= continue ;; *) if test dlfiles = "$prev"; then func_append dlfiles " $arg" else func_append dlprefiles " $arg" fi prev= continue ;; esac ;; expsyms) export_symbols=$arg test -f "$arg" \ || func_fatal_error "symbol file '$arg' does not exist" prev= continue ;; expsyms_regex) export_symbols_regex=$arg prev= continue ;; framework) case $host in *-*-darwin*) case "$deplibs " in *" $qarg.ltframework "*) ;; *) func_append deplibs " $qarg.ltframework" # this is fixed later ;; esac ;; esac prev= continue ;; inst_prefix) inst_prefix_dir=$arg prev= continue ;; mllvm) # Clang does not use LLVM to link, so we can simply discard any # '-mllvm $arg' options when doing the link step. prev= continue ;; objectlist) if test -f "$arg"; then save_arg=$arg moreargs= for fil in `cat "$save_arg"` do # func_append moreargs " $fil" arg=$fil # A libtool-controlled object. # Check to see that this really is a libtool object. if func_lalib_unsafe_p "$arg"; then pic_object= non_pic_object= # Read the .lo file func_source "$arg" if test -z "$pic_object" || test -z "$non_pic_object" || test none = "$pic_object" && test none = "$non_pic_object"; then func_fatal_error "cannot find name of object for '$arg'" fi # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir=$func_dirname_result if test none != "$pic_object"; then # Prepend the subdirectory the object is found in. pic_object=$xdir$pic_object if test dlfiles = "$prev"; then if test yes = "$build_libtool_libs" && test yes = "$dlopen_support"; then func_append dlfiles " $pic_object" prev= continue else # If libtool objects are unsupported, then we need to preload. prev=dlprefiles fi fi # CHECK ME: I think I busted this. -Ossama if test dlprefiles = "$prev"; then # Preload the old-style object. func_append dlprefiles " $pic_object" prev= fi # A PIC object. func_append libobjs " $pic_object" arg=$pic_object fi # Non-PIC object. if test none != "$non_pic_object"; then # Prepend the subdirectory the object is found in. non_pic_object=$xdir$non_pic_object # A standard non-PIC object func_append non_pic_objects " $non_pic_object" if test -z "$pic_object" || test none = "$pic_object"; then arg=$non_pic_object fi else # If the PIC object exists, use it instead. # $xdir was prepended to $pic_object above. non_pic_object=$pic_object func_append non_pic_objects " $non_pic_object" fi else # Only an error if not doing a dry-run. if $opt_dry_run; then # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir=$func_dirname_result func_lo2o "$arg" pic_object=$xdir$objdir/$func_lo2o_result non_pic_object=$xdir$func_lo2o_result func_append libobjs " $pic_object" func_append non_pic_objects " $non_pic_object" else func_fatal_error "'$arg' is not a valid libtool object" fi fi done else func_fatal_error "link input file '$arg' does not exist" fi arg=$save_arg prev= continue ;; os2dllname) os2dllname=$arg prev= continue ;; precious_regex) precious_files_regex=$arg prev= continue ;; release) release=-$arg prev= continue ;; rpath | xrpath) # We need an absolute path. case $arg in [\\/]* | [A-Za-z]:[\\/]*) ;; *) func_fatal_error "only absolute run-paths are allowed" ;; esac if test rpath = "$prev"; then case "$rpath " in *" $arg "*) ;; *) func_append rpath " $arg" ;; esac else case "$xrpath " in *" $arg "*) ;; *) func_append xrpath " $arg" ;; esac fi prev= continue ;; shrext) shrext_cmds=$arg prev= continue ;; weak) func_append weak_libs " $arg" prev= continue ;; xcclinker) func_append linker_flags " $qarg" func_append compiler_flags " $qarg" prev= func_append compile_command " $qarg" func_append finalize_command " $qarg" continue ;; xcompiler) func_append compiler_flags " $qarg" prev= func_append compile_command " $qarg" func_append finalize_command " $qarg" continue ;; xlinker) func_append linker_flags " $qarg" func_append compiler_flags " $wl$qarg" prev= func_append compile_command " $wl$qarg" func_append finalize_command " $wl$qarg" continue ;; *) eval "$prev=\"\$arg\"" prev= continue ;; esac fi # test -n "$prev" prevarg=$arg case $arg in -all-static) if test -n "$link_static_flag"; then # See comment for -static flag below, for more details. func_append compile_command " $link_static_flag" func_append finalize_command " $link_static_flag" fi continue ;; -allow-undefined) # FIXME: remove this flag sometime in the future. func_fatal_error "'-allow-undefined' must not be used because it is the default" ;; -avoid-version) avoid_version=yes continue ;; -bindir) prev=bindir continue ;; -dlopen) prev=dlfiles continue ;; -dlpreopen) prev=dlprefiles continue ;; -export-dynamic) export_dynamic=yes continue ;; -export-symbols | -export-symbols-regex) if test -n "$export_symbols" || test -n "$export_symbols_regex"; then func_fatal_error "more than one -exported-symbols argument is not allowed" fi if test X-export-symbols = "X$arg"; then prev=expsyms else prev=expsyms_regex fi continue ;; -framework) prev=framework continue ;; -inst-prefix-dir) prev=inst_prefix continue ;; # The native IRIX linker understands -LANG:*, -LIST:* and -LNO:* # so, if we see these flags be careful not to treat them like -L -L[A-Z][A-Z]*:*) case $with_gcc/$host in no/*-*-irix* | /*-*-irix*) func_append compile_command " $arg" func_append finalize_command " $arg" ;; esac continue ;; -L*) func_stripname "-L" '' "$arg" if test -z "$func_stripname_result"; then if test "$#" -gt 0; then func_fatal_error "require no space between '-L' and '$1'" else func_fatal_error "need path for '-L' option" fi fi func_resolve_sysroot "$func_stripname_result" dir=$func_resolve_sysroot_result # We need an absolute path. case $dir in [\\/]* | [A-Za-z]:[\\/]*) ;; *) absdir=`cd "$dir" && pwd` test -z "$absdir" && \ func_fatal_error "cannot determine absolute directory name of '$dir'" dir=$absdir ;; esac case "$deplibs " in *" -L$dir "* | *" $arg "*) # Will only happen for absolute or sysroot arguments ;; *) # Preserve sysroot, but never include relative directories case $dir in [\\/]* | [A-Za-z]:[\\/]* | =*) func_append deplibs " $arg" ;; *) func_append deplibs " -L$dir" ;; esac func_append lib_search_path " $dir" ;; esac case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) testbindir=`$ECHO "$dir" | $SED 's*/lib$*/bin*'` case :$dllsearchpath: in *":$dir:"*) ;; ::) dllsearchpath=$dir;; *) func_append dllsearchpath ":$dir";; esac case :$dllsearchpath: in *":$testbindir:"*) ;; ::) dllsearchpath=$testbindir;; *) func_append dllsearchpath ":$testbindir";; esac ;; esac continue ;; -l*) if test X-lc = "X$arg" || test X-lm = "X$arg"; then case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-beos* | *-cegcc* | *-*-haiku*) # These systems don't actually have a C or math library (as such) continue ;; *-*-os2*) # These systems don't actually have a C library (as such) test X-lc = "X$arg" && continue ;; *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-bitrig*) # Do not include libc due to us having libc/libc_r. test X-lc = "X$arg" && continue ;; *-*-rhapsody* | *-*-darwin1.[012]) # Rhapsody C and math libraries are in the System framework func_append deplibs " System.ltframework" continue ;; *-*-sco3.2v5* | *-*-sco5v6*) # Causes problems with __ctype test X-lc = "X$arg" && continue ;; *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) # Compiler inserts libc in the correct place for threads to work test X-lc = "X$arg" && continue ;; esac elif test X-lc_r = "X$arg"; then case $host in *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-bitrig*) # Do not include libc_r directly, use -pthread flag. continue ;; esac fi func_append deplibs " $arg" continue ;; -mllvm) prev=mllvm continue ;; -module) module=yes continue ;; # Tru64 UNIX uses -model [arg] to determine the layout of C++ # classes, name mangling, and exception handling. # Darwin uses the -arch flag to determine output architecture. -model|-arch|-isysroot|--sysroot) func_append compiler_flags " $arg" func_append compile_command " $arg" func_append finalize_command " $arg" prev=xcompiler continue ;; -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \ |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*) func_append compiler_flags " $arg" func_append compile_command " $arg" func_append finalize_command " $arg" case "$new_inherited_linker_flags " in *" $arg "*) ;; * ) func_append new_inherited_linker_flags " $arg" ;; esac continue ;; -multi_module) single_module=$wl-multi_module continue ;; -no-fast-install) fast_install=no continue ;; -no-install) case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-darwin* | *-cegcc*) # The PATH hackery in wrapper scripts is required on Windows # and Darwin in order for the loader to find any dlls it needs. func_warning "'-no-install' is ignored for $host" func_warning "assuming '-no-fast-install' instead" fast_install=no ;; *) no_install=yes ;; esac continue ;; -no-undefined) allow_undefined=no continue ;; -objectlist) prev=objectlist continue ;; -os2dllname) prev=os2dllname continue ;; -o) prev=output ;; -precious-files-regex) prev=precious_regex continue ;; -release) prev=release continue ;; -rpath) prev=rpath continue ;; -R) prev=xrpath continue ;; -R*) func_stripname '-R' '' "$arg" dir=$func_stripname_result # We need an absolute path. case $dir in [\\/]* | [A-Za-z]:[\\/]*) ;; =*) func_stripname '=' '' "$dir" dir=$lt_sysroot$func_stripname_result ;; *) func_fatal_error "only absolute run-paths are allowed" ;; esac case "$xrpath " in *" $dir "*) ;; *) func_append xrpath " $dir" ;; esac continue ;; -shared) # The effects of -shared are defined in a previous loop. continue ;; -shrext) prev=shrext continue ;; -static | -static-libtool-libs) # The effects of -static are defined in a previous loop. # We used to do the same as -all-static on platforms that # didn't have a PIC flag, but the assumption that the effects # would be equivalent was wrong. It would break on at least # Digital Unix and AIX. continue ;; -thread-safe) thread_safe=yes continue ;; -version-info) prev=vinfo continue ;; -version-number) prev=vinfo vinfo_number=yes continue ;; -weak) prev=weak continue ;; -Wc,*) func_stripname '-Wc,' '' "$arg" args=$func_stripname_result arg= save_ifs=$IFS; IFS=, for flag in $args; do IFS=$save_ifs func_quote_for_eval "$flag" func_append arg " $func_quote_for_eval_result" func_append compiler_flags " $func_quote_for_eval_result" done IFS=$save_ifs func_stripname ' ' '' "$arg" arg=$func_stripname_result ;; -Wl,*) func_stripname '-Wl,' '' "$arg" args=$func_stripname_result arg= save_ifs=$IFS; IFS=, for flag in $args; do IFS=$save_ifs func_quote_for_eval "$flag" func_append arg " $wl$func_quote_for_eval_result" func_append compiler_flags " $wl$func_quote_for_eval_result" func_append linker_flags " $func_quote_for_eval_result" done IFS=$save_ifs func_stripname ' ' '' "$arg" arg=$func_stripname_result ;; -Xcompiler) prev=xcompiler continue ;; -Xlinker) prev=xlinker continue ;; -XCClinker) prev=xcclinker continue ;; # -msg_* for osf cc -msg_*) func_quote_for_eval "$arg" arg=$func_quote_for_eval_result ;; # Flags to be passed through unchanged, with rationale: # -64, -mips[0-9] enable 64-bit mode for the SGI compiler # -r[0-9][0-9]* specify processor for the SGI compiler # -xarch=*, -xtarget=* enable 64-bit mode for the Sun compiler # +DA*, +DD* enable 64-bit mode for the HP compiler # -q* compiler args for the IBM compiler # -m*, -t[45]*, -txscale* architecture-specific flags for GCC # -F/path path to uninstalled frameworks, gcc on darwin # -p, -pg, --coverage, -fprofile-* profiling flags for GCC # -fstack-protector* stack protector flags for GCC # @file GCC response files # -tp=* Portland pgcc target processor selection # --sysroot=* for sysroot support # -O*, -g*, -flto*, -fwhopr*, -fuse-linker-plugin GCC link-time optimization # -specs=* GCC specs files # -stdlib=* select c++ std lib with clang # -fsanitize=* Clang/GCC memory and address sanitizer # -fuse-ld=* Linker select flags for GCC # -static-* direct GCC to link specific libraries statically # -fcilkplus Cilk Plus language extension features for C/C++ -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \ -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*|-tp=*|--sysroot=*| \ -O*|-g*|-flto*|-fwhopr*|-fuse-linker-plugin|-fstack-protector*|-stdlib=*| \ -specs=*|-fsanitize=*|-fuse-ld=*|-static-*|-fcilkplus) func_quote_for_eval "$arg" arg=$func_quote_for_eval_result func_append compile_command " $arg" func_append finalize_command " $arg" func_append compiler_flags " $arg" continue ;; -Z*) if test os2 = "`expr $host : '.*\(os2\)'`"; then # OS/2 uses -Zxxx to specify OS/2-specific options compiler_flags="$compiler_flags $arg" func_append compile_command " $arg" func_append finalize_command " $arg" case $arg in -Zlinker | -Zstack) prev=xcompiler ;; esac continue else # Otherwise treat like 'Some other compiler flag' below func_quote_for_eval "$arg" arg=$func_quote_for_eval_result fi ;; # Some other compiler flag. -* | +*) func_quote_for_eval "$arg" arg=$func_quote_for_eval_result ;; *.$objext) # A standard object. func_append objs " $arg" ;; *.lo) # A libtool-controlled object. # Check to see that this really is a libtool object. if func_lalib_unsafe_p "$arg"; then pic_object= non_pic_object= # Read the .lo file func_source "$arg" if test -z "$pic_object" || test -z "$non_pic_object" || test none = "$pic_object" && test none = "$non_pic_object"; then func_fatal_error "cannot find name of object for '$arg'" fi # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir=$func_dirname_result test none = "$pic_object" || { # Prepend the subdirectory the object is found in. pic_object=$xdir$pic_object if test dlfiles = "$prev"; then if test yes = "$build_libtool_libs" && test yes = "$dlopen_support"; then func_append dlfiles " $pic_object" prev= continue else # If libtool objects are unsupported, then we need to preload. prev=dlprefiles fi fi # CHECK ME: I think I busted this. -Ossama if test dlprefiles = "$prev"; then # Preload the old-style object. func_append dlprefiles " $pic_object" prev= fi # A PIC object. func_append libobjs " $pic_object" arg=$pic_object } # Non-PIC object. if test none != "$non_pic_object"; then # Prepend the subdirectory the object is found in. non_pic_object=$xdir$non_pic_object # A standard non-PIC object func_append non_pic_objects " $non_pic_object" if test -z "$pic_object" || test none = "$pic_object"; then arg=$non_pic_object fi else # If the PIC object exists, use it instead. # $xdir was prepended to $pic_object above. non_pic_object=$pic_object func_append non_pic_objects " $non_pic_object" fi else # Only an error if not doing a dry-run. if $opt_dry_run; then # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir=$func_dirname_result func_lo2o "$arg" pic_object=$xdir$objdir/$func_lo2o_result non_pic_object=$xdir$func_lo2o_result func_append libobjs " $pic_object" func_append non_pic_objects " $non_pic_object" else func_fatal_error "'$arg' is not a valid libtool object" fi fi ;; *.$libext) # An archive. func_append deplibs " $arg" func_append old_deplibs " $arg" continue ;; *.la) # A libtool-controlled library. func_resolve_sysroot "$arg" if test dlfiles = "$prev"; then # This library was specified with -dlopen. func_append dlfiles " $func_resolve_sysroot_result" prev= elif test dlprefiles = "$prev"; then # The library was specified with -dlpreopen. func_append dlprefiles " $func_resolve_sysroot_result" prev= else func_append deplibs " $func_resolve_sysroot_result" fi continue ;; # Some other compiler argument. *) # Unknown arguments in both finalize_command and compile_command need # to be aesthetically quoted because they are evaled later. func_quote_for_eval "$arg" arg=$func_quote_for_eval_result ;; esac # arg # Now actually substitute the argument into the commands. if test -n "$arg"; then func_append compile_command " $arg" func_append finalize_command " $arg" fi done # argument parsing loop test -n "$prev" && \ func_fatal_help "the '$prevarg' option requires an argument" if test yes = "$export_dynamic" && test -n "$export_dynamic_flag_spec"; then eval arg=\"$export_dynamic_flag_spec\" func_append compile_command " $arg" func_append finalize_command " $arg" fi oldlibs= # calculate the name of the file, without its directory func_basename "$output" outputname=$func_basename_result libobjs_save=$libobjs if test -n "$shlibpath_var"; then # get the directories listed in $shlibpath_var eval shlib_search_path=\`\$ECHO \"\$$shlibpath_var\" \| \$SED \'s/:/ /g\'\` else shlib_search_path= fi eval sys_lib_search_path=\"$sys_lib_search_path_spec\" eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\" # Definition is injected by LT_CONFIG during libtool generation. func_munge_path_list sys_lib_dlsearch_path "$LT_SYS_LIBRARY_PATH" func_dirname "$output" "/" "" output_objdir=$func_dirname_result$objdir func_to_tool_file "$output_objdir/" tool_output_objdir=$func_to_tool_file_result # Create the object directory. func_mkdir_p "$output_objdir" # Determine the type of output case $output in "") func_fatal_help "you must specify an output file" ;; *.$libext) linkmode=oldlib ;; *.lo | *.$objext) linkmode=obj ;; *.la) linkmode=lib ;; *) linkmode=prog ;; # Anything else should be a program. esac specialdeplibs= libs= # Find all interdependent deplibs by searching for libraries # that are linked more than once (e.g. -la -lb -la) for deplib in $deplibs; do if $opt_preserve_dup_deps; then case "$libs " in *" $deplib "*) func_append specialdeplibs " $deplib" ;; esac fi func_append libs " $deplib" done if test lib = "$linkmode"; then libs="$predeps $libs $compiler_lib_search_path $postdeps" # Compute libraries that are listed more than once in $predeps # $postdeps and mark them as special (i.e., whose duplicates are # not to be eliminated). pre_post_deps= if $opt_duplicate_compiler_generated_deps; then for pre_post_dep in $predeps $postdeps; do case "$pre_post_deps " in *" $pre_post_dep "*) func_append specialdeplibs " $pre_post_deps" ;; esac func_append pre_post_deps " $pre_post_dep" done fi pre_post_deps= fi deplibs= newdependency_libs= newlib_search_path= need_relink=no # whether we're linking any uninstalled libtool libraries notinst_deplibs= # not-installed libtool libraries notinst_path= # paths that contain not-installed libtool libraries case $linkmode in lib) passes="conv dlpreopen link" for file in $dlfiles $dlprefiles; do case $file in *.la) ;; *) func_fatal_help "libraries can '-dlopen' only libtool libraries: $file" ;; esac done ;; prog) compile_deplibs= finalize_deplibs= alldeplibs=false newdlfiles= newdlprefiles= passes="conv scan dlopen dlpreopen link" ;; *) passes="conv" ;; esac for pass in $passes; do # The preopen pass in lib mode reverses $deplibs; put it back here # so that -L comes before libs that need it for instance... if test lib,link = "$linkmode,$pass"; then ## FIXME: Find the place where the list is rebuilt in the wrong ## order, and fix it there properly tmp_deplibs= for deplib in $deplibs; do tmp_deplibs="$deplib $tmp_deplibs" done deplibs=$tmp_deplibs fi if test lib,link = "$linkmode,$pass" || test prog,scan = "$linkmode,$pass"; then libs=$deplibs deplibs= fi if test prog = "$linkmode"; then case $pass in dlopen) libs=$dlfiles ;; dlpreopen) libs=$dlprefiles ;; link) libs="$deplibs %DEPLIBS%" test "X$link_all_deplibs" != Xno && libs="$libs $dependency_libs" ;; esac fi if test lib,dlpreopen = "$linkmode,$pass"; then # Collect and forward deplibs of preopened libtool libs for lib in $dlprefiles; do # Ignore non-libtool-libs dependency_libs= func_resolve_sysroot "$lib" case $lib in *.la) func_source "$func_resolve_sysroot_result" ;; esac # Collect preopened libtool deplibs, except any this library # has declared as weak libs for deplib in $dependency_libs; do func_basename "$deplib" deplib_base=$func_basename_result case " $weak_libs " in *" $deplib_base "*) ;; *) func_append deplibs " $deplib" ;; esac done done libs=$dlprefiles fi if test dlopen = "$pass"; then # Collect dlpreopened libraries save_deplibs=$deplibs deplibs= fi for deplib in $libs; do lib= found=false case $deplib in -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \ |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*) if test prog,link = "$linkmode,$pass"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else func_append compiler_flags " $deplib" if test lib = "$linkmode"; then case "$new_inherited_linker_flags " in *" $deplib "*) ;; * ) func_append new_inherited_linker_flags " $deplib" ;; esac fi fi continue ;; -l*) if test lib != "$linkmode" && test prog != "$linkmode"; then func_warning "'-l' is ignored for archives/objects" continue fi func_stripname '-l' '' "$deplib" name=$func_stripname_result if test lib = "$linkmode"; then searchdirs="$newlib_search_path $lib_search_path $compiler_lib_search_dirs $sys_lib_search_path $shlib_search_path" else searchdirs="$newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path" fi for searchdir in $searchdirs; do for search_ext in .la $std_shrext .so .a; do # Search the libtool library lib=$searchdir/lib$name$search_ext if test -f "$lib"; then if test .la = "$search_ext"; then found=: else found=false fi break 2 fi done done if $found; then # deplib is a libtool library # If $allow_libtool_libs_with_static_runtimes && $deplib is a stdlib, # We need to do some special things here, and not later. if test yes = "$allow_libtool_libs_with_static_runtimes"; then case " $predeps $postdeps " in *" $deplib "*) if func_lalib_p "$lib"; then library_names= old_library= func_source "$lib" for l in $old_library $library_names; do ll=$l done if test "X$ll" = "X$old_library"; then # only static version available found=false func_dirname "$lib" "" "." ladir=$func_dirname_result lib=$ladir/$old_library if test prog,link = "$linkmode,$pass"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else deplibs="$deplib $deplibs" test lib = "$linkmode" && newdependency_libs="$deplib $newdependency_libs" fi continue fi fi ;; *) ;; esac fi else # deplib doesn't seem to be a libtool library if test prog,link = "$linkmode,$pass"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else deplibs="$deplib $deplibs" test lib = "$linkmode" && newdependency_libs="$deplib $newdependency_libs" fi continue fi ;; # -l *.ltframework) if test prog,link = "$linkmode,$pass"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else deplibs="$deplib $deplibs" if test lib = "$linkmode"; then case "$new_inherited_linker_flags " in *" $deplib "*) ;; * ) func_append new_inherited_linker_flags " $deplib" ;; esac fi fi continue ;; -L*) case $linkmode in lib) deplibs="$deplib $deplibs" test conv = "$pass" && continue newdependency_libs="$deplib $newdependency_libs" func_stripname '-L' '' "$deplib" func_resolve_sysroot "$func_stripname_result" func_append newlib_search_path " $func_resolve_sysroot_result" ;; prog) if test conv = "$pass"; then deplibs="$deplib $deplibs" continue fi if test scan = "$pass"; then deplibs="$deplib $deplibs" else compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" fi func_stripname '-L' '' "$deplib" func_resolve_sysroot "$func_stripname_result" func_append newlib_search_path " $func_resolve_sysroot_result" ;; *) func_warning "'-L' is ignored for archives/objects" ;; esac # linkmode continue ;; # -L -R*) if test link = "$pass"; then func_stripname '-R' '' "$deplib" func_resolve_sysroot "$func_stripname_result" dir=$func_resolve_sysroot_result # Make sure the xrpath contains only unique directories. case "$xrpath " in *" $dir "*) ;; *) func_append xrpath " $dir" ;; esac fi deplibs="$deplib $deplibs" continue ;; *.la) func_resolve_sysroot "$deplib" lib=$func_resolve_sysroot_result ;; *.$libext) if test conv = "$pass"; then deplibs="$deplib $deplibs" continue fi case $linkmode in lib) # Linking convenience modules into shared libraries is allowed, # but linking other static libraries is non-portable. case " $dlpreconveniencelibs " in *" $deplib "*) ;; *) valid_a_lib=false case $deplibs_check_method in match_pattern*) set dummy $deplibs_check_method; shift match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` if eval "\$ECHO \"$deplib\"" 2>/dev/null | $SED 10q \ | $EGREP "$match_pattern_regex" > /dev/null; then valid_a_lib=: fi ;; pass_all) valid_a_lib=: ;; esac if $valid_a_lib; then echo $ECHO "*** Warning: Linking the shared library $output against the" $ECHO "*** static library $deplib is not portable!" deplibs="$deplib $deplibs" else echo $ECHO "*** Warning: Trying to link with static lib archive $deplib." echo "*** I have the capability to make that library automatically link in when" echo "*** you link to this library. But I can only do this if you have a" echo "*** shared version of the library, which you do not appear to have" echo "*** because the file extensions .$libext of this argument makes me believe" echo "*** that it is just a static archive that I should not use here." fi ;; esac continue ;; prog) if test link != "$pass"; then deplibs="$deplib $deplibs" else compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" fi continue ;; esac # linkmode ;; # *.$libext *.lo | *.$objext) if test conv = "$pass"; then deplibs="$deplib $deplibs" elif test prog = "$linkmode"; then if test dlpreopen = "$pass" || test yes != "$dlopen_support" || test no = "$build_libtool_libs"; then # If there is no dlopen support or we're linking statically, # we need to preload. func_append newdlprefiles " $deplib" compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else func_append newdlfiles " $deplib" fi fi continue ;; %DEPLIBS%) alldeplibs=: continue ;; esac # case $deplib $found || test -f "$lib" \ || func_fatal_error "cannot find the library '$lib' or unhandled argument '$deplib'" # Check to see that this really is a libtool archive. func_lalib_unsafe_p "$lib" \ || func_fatal_error "'$lib' is not a valid libtool archive" func_dirname "$lib" "" "." ladir=$func_dirname_result dlname= dlopen= dlpreopen= libdir= library_names= old_library= inherited_linker_flags= # If the library was installed with an old release of libtool, # it will not redefine variables installed, or shouldnotlink installed=yes shouldnotlink=no avoidtemprpath= # Read the .la file func_source "$lib" # Convert "-framework foo" to "foo.ltframework" if test -n "$inherited_linker_flags"; then tmp_inherited_linker_flags=`$ECHO "$inherited_linker_flags" | $SED 's/-framework \([^ $]*\)/\1.ltframework/g'` for tmp_inherited_linker_flag in $tmp_inherited_linker_flags; do case " $new_inherited_linker_flags " in *" $tmp_inherited_linker_flag "*) ;; *) func_append new_inherited_linker_flags " $tmp_inherited_linker_flag";; esac done fi dependency_libs=`$ECHO " $dependency_libs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` if test lib,link = "$linkmode,$pass" || test prog,scan = "$linkmode,$pass" || { test prog != "$linkmode" && test lib != "$linkmode"; }; then test -n "$dlopen" && func_append dlfiles " $dlopen" test -n "$dlpreopen" && func_append dlprefiles " $dlpreopen" fi if test conv = "$pass"; then # Only check for convenience libraries deplibs="$lib $deplibs" if test -z "$libdir"; then if test -z "$old_library"; then func_fatal_error "cannot find name of link library for '$lib'" fi # It is a libtool convenience library, so add in its objects. func_append convenience " $ladir/$objdir/$old_library" func_append old_convenience " $ladir/$objdir/$old_library" tmp_libs= for deplib in $dependency_libs; do deplibs="$deplib $deplibs" if $opt_preserve_dup_deps; then case "$tmp_libs " in *" $deplib "*) func_append specialdeplibs " $deplib" ;; esac fi func_append tmp_libs " $deplib" done elif test prog != "$linkmode" && test lib != "$linkmode"; then func_fatal_error "'$lib' is not a convenience library" fi continue fi # $pass = conv # Get the name of the library we link against. linklib= if test -n "$old_library" && { test yes = "$prefer_static_libs" || test built,no = "$prefer_static_libs,$installed"; }; then linklib=$old_library else for l in $old_library $library_names; do linklib=$l done fi if test -z "$linklib"; then func_fatal_error "cannot find name of link library for '$lib'" fi # This library was specified with -dlopen. if test dlopen = "$pass"; then test -z "$libdir" \ && func_fatal_error "cannot -dlopen a convenience library: '$lib'" if test -z "$dlname" || test yes != "$dlopen_support" || test no = "$build_libtool_libs" then # If there is no dlname, no dlopen support or we're linking # statically, we need to preload. We also need to preload any # dependent libraries so libltdl's deplib preloader doesn't # bomb out in the load deplibs phase. func_append dlprefiles " $lib $dependency_libs" else func_append newdlfiles " $lib" fi continue fi # $pass = dlopen # We need an absolute path. case $ladir in [\\/]* | [A-Za-z]:[\\/]*) abs_ladir=$ladir ;; *) abs_ladir=`cd "$ladir" && pwd` if test -z "$abs_ladir"; then func_warning "cannot determine absolute directory name of '$ladir'" func_warning "passing it literally to the linker, although it might fail" abs_ladir=$ladir fi ;; esac func_basename "$lib" laname=$func_basename_result # Find the relevant object directory and library name. if test yes = "$installed"; then if test ! -f "$lt_sysroot$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then func_warning "library '$lib' was moved." dir=$ladir absdir=$abs_ladir libdir=$abs_ladir else dir=$lt_sysroot$libdir absdir=$lt_sysroot$libdir fi test yes = "$hardcode_automatic" && avoidtemprpath=yes else if test ! -f "$ladir/$objdir/$linklib" && test -f "$abs_ladir/$linklib"; then dir=$ladir absdir=$abs_ladir # Remove this search path later func_append notinst_path " $abs_ladir" else dir=$ladir/$objdir absdir=$abs_ladir/$objdir # Remove this search path later func_append notinst_path " $abs_ladir" fi fi # $installed = yes func_stripname 'lib' '.la' "$laname" name=$func_stripname_result # This library was specified with -dlpreopen. if test dlpreopen = "$pass"; then if test -z "$libdir" && test prog = "$linkmode"; then func_fatal_error "only libraries may -dlpreopen a convenience library: '$lib'" fi case $host in # special handling for platforms with PE-DLLs. *cygwin* | *mingw* | *cegcc* ) # Linker will automatically link against shared library if both # static and shared are present. Therefore, ensure we extract # symbols from the import library if a shared library is present # (otherwise, the dlopen module name will be incorrect). We do # this by putting the import library name into $newdlprefiles. # We recover the dlopen module name by 'saving' the la file # name in a special purpose variable, and (later) extracting the # dlname from the la file. if test -n "$dlname"; then func_tr_sh "$dir/$linklib" eval "libfile_$func_tr_sh_result=\$abs_ladir/\$laname" func_append newdlprefiles " $dir/$linklib" else func_append newdlprefiles " $dir/$old_library" # Keep a list of preopened convenience libraries to check # that they are being used correctly in the link pass. test -z "$libdir" && \ func_append dlpreconveniencelibs " $dir/$old_library" fi ;; * ) # Prefer using a static library (so that no silly _DYNAMIC symbols # are required to link). if test -n "$old_library"; then func_append newdlprefiles " $dir/$old_library" # Keep a list of preopened convenience libraries to check # that they are being used correctly in the link pass. test -z "$libdir" && \ func_append dlpreconveniencelibs " $dir/$old_library" # Otherwise, use the dlname, so that lt_dlopen finds it. elif test -n "$dlname"; then func_append newdlprefiles " $dir/$dlname" else func_append newdlprefiles " $dir/$linklib" fi ;; esac fi # $pass = dlpreopen if test -z "$libdir"; then # Link the convenience library if test lib = "$linkmode"; then deplibs="$dir/$old_library $deplibs" elif test prog,link = "$linkmode,$pass"; then compile_deplibs="$dir/$old_library $compile_deplibs" finalize_deplibs="$dir/$old_library $finalize_deplibs" else deplibs="$lib $deplibs" # used for prog,scan pass fi continue fi if test prog = "$linkmode" && test link != "$pass"; then func_append newlib_search_path " $ladir" deplibs="$lib $deplibs" linkalldeplibs=false if test no != "$link_all_deplibs" || test -z "$library_names" || test no = "$build_libtool_libs"; then linkalldeplibs=: fi tmp_libs= for deplib in $dependency_libs; do case $deplib in -L*) func_stripname '-L' '' "$deplib" func_resolve_sysroot "$func_stripname_result" func_append newlib_search_path " $func_resolve_sysroot_result" ;; esac # Need to link against all dependency_libs? if $linkalldeplibs; then deplibs="$deplib $deplibs" else # Need to hardcode shared library paths # or/and link against static libraries newdependency_libs="$deplib $newdependency_libs" fi if $opt_preserve_dup_deps; then case "$tmp_libs " in *" $deplib "*) func_append specialdeplibs " $deplib" ;; esac fi func_append tmp_libs " $deplib" done # for deplib continue fi # $linkmode = prog... if test prog,link = "$linkmode,$pass"; then if test -n "$library_names" && { { test no = "$prefer_static_libs" || test built,yes = "$prefer_static_libs,$installed"; } || test -z "$old_library"; }; then # We need to hardcode the library path if test -n "$shlibpath_var" && test -z "$avoidtemprpath"; then # Make sure the rpath contains only unique directories. case $temp_rpath: in *"$absdir:"*) ;; *) func_append temp_rpath "$absdir:" ;; esac fi # Hardcode the library path. # Skip directories that are in the system default run-time # search path. case " $sys_lib_dlsearch_path " in *" $absdir "*) ;; *) case "$compile_rpath " in *" $absdir "*) ;; *) func_append compile_rpath " $absdir" ;; esac ;; esac case " $sys_lib_dlsearch_path " in *" $libdir "*) ;; *) case "$finalize_rpath " in *" $libdir "*) ;; *) func_append finalize_rpath " $libdir" ;; esac ;; esac fi # $linkmode,$pass = prog,link... if $alldeplibs && { test pass_all = "$deplibs_check_method" || { test yes = "$build_libtool_libs" && test -n "$library_names"; }; }; then # We only need to search for static libraries continue fi fi link_static=no # Whether the deplib will be linked statically use_static_libs=$prefer_static_libs if test built = "$use_static_libs" && test yes = "$installed"; then use_static_libs=no fi if test -n "$library_names" && { test no = "$use_static_libs" || test -z "$old_library"; }; then case $host in *cygwin* | *mingw* | *cegcc* | *os2*) # No point in relinking DLLs because paths are not encoded func_append notinst_deplibs " $lib" need_relink=no ;; *) if test no = "$installed"; then func_append notinst_deplibs " $lib" need_relink=yes fi ;; esac # This is a shared library # Warn about portability, can't link against -module's on some # systems (darwin). Don't bleat about dlopened modules though! dlopenmodule= for dlpremoduletest in $dlprefiles; do if test "X$dlpremoduletest" = "X$lib"; then dlopenmodule=$dlpremoduletest break fi done if test -z "$dlopenmodule" && test yes = "$shouldnotlink" && test link = "$pass"; then echo if test prog = "$linkmode"; then $ECHO "*** Warning: Linking the executable $output against the loadable module" else $ECHO "*** Warning: Linking the shared library $output against the loadable module" fi $ECHO "*** $linklib is not portable!" fi if test lib = "$linkmode" && test yes = "$hardcode_into_libs"; then # Hardcode the library path. # Skip directories that are in the system default run-time # search path. case " $sys_lib_dlsearch_path " in *" $absdir "*) ;; *) case "$compile_rpath " in *" $absdir "*) ;; *) func_append compile_rpath " $absdir" ;; esac ;; esac case " $sys_lib_dlsearch_path " in *" $libdir "*) ;; *) case "$finalize_rpath " in *" $libdir "*) ;; *) func_append finalize_rpath " $libdir" ;; esac ;; esac fi if test -n "$old_archive_from_expsyms_cmds"; then # figure out the soname set dummy $library_names shift realname=$1 shift libname=`eval "\\$ECHO \"$libname_spec\""` # use dlname if we got it. it's perfectly good, no? if test -n "$dlname"; then soname=$dlname elif test -n "$soname_spec"; then # bleh windows case $host in *cygwin* | mingw* | *cegcc* | *os2*) func_arith $current - $age major=$func_arith_result versuffix=-$major ;; esac eval soname=\"$soname_spec\" else soname=$realname fi # Make a new name for the extract_expsyms_cmds to use soroot=$soname func_basename "$soroot" soname=$func_basename_result func_stripname 'lib' '.dll' "$soname" newlib=libimp-$func_stripname_result.a # If the library has no export list, then create one now if test -f "$output_objdir/$soname-def"; then : else func_verbose "extracting exported symbol list from '$soname'" func_execute_cmds "$extract_expsyms_cmds" 'exit $?' fi # Create $newlib if test -f "$output_objdir/$newlib"; then :; else func_verbose "generating import library for '$soname'" func_execute_cmds "$old_archive_from_expsyms_cmds" 'exit $?' fi # make sure the library variables are pointing to the new library dir=$output_objdir linklib=$newlib fi # test -n "$old_archive_from_expsyms_cmds" if test prog = "$linkmode" || test relink != "$opt_mode"; then add_shlibpath= add_dir= add= lib_linked=yes case $hardcode_action in immediate | unsupported) if test no = "$hardcode_direct"; then add=$dir/$linklib case $host in *-*-sco3.2v5.0.[024]*) add_dir=-L$dir ;; *-*-sysv4*uw2*) add_dir=-L$dir ;; *-*-sysv5OpenUNIX* | *-*-sysv5UnixWare7.[01].[10]* | \ *-*-unixware7*) add_dir=-L$dir ;; *-*-darwin* ) # if the lib is a (non-dlopened) module then we cannot # link against it, someone is ignoring the earlier warnings if /usr/bin/file -L $add 2> /dev/null | $GREP ": [^:]* bundle" >/dev/null; then if test "X$dlopenmodule" != "X$lib"; then $ECHO "*** Warning: lib $linklib is a module, not a shared library" if test -z "$old_library"; then echo echo "*** And there doesn't seem to be a static archive available" echo "*** The link will probably fail, sorry" else add=$dir/$old_library fi elif test -n "$old_library"; then add=$dir/$old_library fi fi esac elif test no = "$hardcode_minus_L"; then case $host in *-*-sunos*) add_shlibpath=$dir ;; esac add_dir=-L$dir add=-l$name elif test no = "$hardcode_shlibpath_var"; then add_shlibpath=$dir add=-l$name else lib_linked=no fi ;; relink) if test yes = "$hardcode_direct" && test no = "$hardcode_direct_absolute"; then add=$dir/$linklib elif test yes = "$hardcode_minus_L"; then add_dir=-L$absdir # Try looking first in the location we're being installed to. if test -n "$inst_prefix_dir"; then case $libdir in [\\/]*) func_append add_dir " -L$inst_prefix_dir$libdir" ;; esac fi add=-l$name elif test yes = "$hardcode_shlibpath_var"; then add_shlibpath=$dir add=-l$name else lib_linked=no fi ;; *) lib_linked=no ;; esac if test yes != "$lib_linked"; then func_fatal_configuration "unsupported hardcode properties" fi if test -n "$add_shlibpath"; then case :$compile_shlibpath: in *":$add_shlibpath:"*) ;; *) func_append compile_shlibpath "$add_shlibpath:" ;; esac fi if test prog = "$linkmode"; then test -n "$add_dir" && compile_deplibs="$add_dir $compile_deplibs" test -n "$add" && compile_deplibs="$add $compile_deplibs" else test -n "$add_dir" && deplibs="$add_dir $deplibs" test -n "$add" && deplibs="$add $deplibs" if test yes != "$hardcode_direct" && test yes != "$hardcode_minus_L" && test yes = "$hardcode_shlibpath_var"; then case :$finalize_shlibpath: in *":$libdir:"*) ;; *) func_append finalize_shlibpath "$libdir:" ;; esac fi fi fi if test prog = "$linkmode" || test relink = "$opt_mode"; then add_shlibpath= add_dir= add= # Finalize command for both is simple: just hardcode it. if test yes = "$hardcode_direct" && test no = "$hardcode_direct_absolute"; then add=$libdir/$linklib elif test yes = "$hardcode_minus_L"; then add_dir=-L$libdir add=-l$name elif test yes = "$hardcode_shlibpath_var"; then case :$finalize_shlibpath: in *":$libdir:"*) ;; *) func_append finalize_shlibpath "$libdir:" ;; esac add=-l$name elif test yes = "$hardcode_automatic"; then if test -n "$inst_prefix_dir" && test -f "$inst_prefix_dir$libdir/$linklib"; then add=$inst_prefix_dir$libdir/$linklib else add=$libdir/$linklib fi else # We cannot seem to hardcode it, guess we'll fake it. add_dir=-L$libdir # Try looking first in the location we're being installed to. if test -n "$inst_prefix_dir"; then case $libdir in [\\/]*) func_append add_dir " -L$inst_prefix_dir$libdir" ;; esac fi add=-l$name fi if test prog = "$linkmode"; then test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs" test -n "$add" && finalize_deplibs="$add $finalize_deplibs" else test -n "$add_dir" && deplibs="$add_dir $deplibs" test -n "$add" && deplibs="$add $deplibs" fi fi elif test prog = "$linkmode"; then # Here we assume that one of hardcode_direct or hardcode_minus_L # is not unsupported. This is valid on all known static and # shared platforms. if test unsupported != "$hardcode_direct"; then test -n "$old_library" && linklib=$old_library compile_deplibs="$dir/$linklib $compile_deplibs" finalize_deplibs="$dir/$linklib $finalize_deplibs" else compile_deplibs="-l$name -L$dir $compile_deplibs" finalize_deplibs="-l$name -L$dir $finalize_deplibs" fi elif test yes = "$build_libtool_libs"; then # Not a shared library if test pass_all != "$deplibs_check_method"; then # We're trying link a shared library against a static one # but the system doesn't support it. # Just print a warning and add the library to dependency_libs so # that the program can be linked against the static library. echo $ECHO "*** Warning: This system cannot link to static lib archive $lib." echo "*** I have the capability to make that library automatically link in when" echo "*** you link to this library. But I can only do this if you have a" echo "*** shared version of the library, which you do not appear to have." if test yes = "$module"; then echo "*** But as you try to build a module library, libtool will still create " echo "*** a static module, that should work as long as the dlopening application" echo "*** is linked with the -dlopen flag to resolve symbols at runtime." if test -z "$global_symbol_pipe"; then echo echo "*** However, this would only work if libtool was able to extract symbol" echo "*** lists from a program, using 'nm' or equivalent, but libtool could" echo "*** not find such a program. So, this module is probably useless." echo "*** 'nm' from GNU binutils and a full rebuild may help." fi if test no = "$build_old_libs"; then build_libtool_libs=module build_old_libs=yes else build_libtool_libs=no fi fi else deplibs="$dir/$old_library $deplibs" link_static=yes fi fi # link shared/static library? if test lib = "$linkmode"; then if test -n "$dependency_libs" && { test yes != "$hardcode_into_libs" || test yes = "$build_old_libs" || test yes = "$link_static"; }; then # Extract -R from dependency_libs temp_deplibs= for libdir in $dependency_libs; do case $libdir in -R*) func_stripname '-R' '' "$libdir" temp_xrpath=$func_stripname_result case " $xrpath " in *" $temp_xrpath "*) ;; *) func_append xrpath " $temp_xrpath";; esac;; *) func_append temp_deplibs " $libdir";; esac done dependency_libs=$temp_deplibs fi func_append newlib_search_path " $absdir" # Link against this library test no = "$link_static" && newdependency_libs="$abs_ladir/$laname $newdependency_libs" # ... and its dependency_libs tmp_libs= for deplib in $dependency_libs; do newdependency_libs="$deplib $newdependency_libs" case $deplib in -L*) func_stripname '-L' '' "$deplib" func_resolve_sysroot "$func_stripname_result";; *) func_resolve_sysroot "$deplib" ;; esac if $opt_preserve_dup_deps; then case "$tmp_libs " in *" $func_resolve_sysroot_result "*) func_append specialdeplibs " $func_resolve_sysroot_result" ;; esac fi func_append tmp_libs " $func_resolve_sysroot_result" done if test no != "$link_all_deplibs"; then # Add the search paths of all dependency libraries for deplib in $dependency_libs; do path= case $deplib in -L*) path=$deplib ;; *.la) func_resolve_sysroot "$deplib" deplib=$func_resolve_sysroot_result func_dirname "$deplib" "" "." dir=$func_dirname_result # We need an absolute path. case $dir in [\\/]* | [A-Za-z]:[\\/]*) absdir=$dir ;; *) absdir=`cd "$dir" && pwd` if test -z "$absdir"; then func_warning "cannot determine absolute directory name of '$dir'" absdir=$dir fi ;; esac if $GREP "^installed=no" $deplib > /dev/null; then case $host in *-*-darwin*) depdepl= eval deplibrary_names=`$SED -n -e 's/^library_names=\(.*\)$/\1/p' $deplib` if test -n "$deplibrary_names"; then for tmp in $deplibrary_names; do depdepl=$tmp done if test -f "$absdir/$objdir/$depdepl"; then depdepl=$absdir/$objdir/$depdepl darwin_install_name=`$OTOOL -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` if test -z "$darwin_install_name"; then darwin_install_name=`$OTOOL64 -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` fi func_append compiler_flags " $wl-dylib_file $wl$darwin_install_name:$depdepl" func_append linker_flags " -dylib_file $darwin_install_name:$depdepl" path= fi fi ;; *) path=-L$absdir/$objdir ;; esac else eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` test -z "$libdir" && \ func_fatal_error "'$deplib' is not a valid libtool archive" test "$absdir" != "$libdir" && \ func_warning "'$deplib' seems to be moved" path=-L$absdir fi ;; esac case " $deplibs " in *" $path "*) ;; *) deplibs="$path $deplibs" ;; esac done fi # link_all_deplibs != no fi # linkmode = lib done # for deplib in $libs if test link = "$pass"; then if test prog = "$linkmode"; then compile_deplibs="$new_inherited_linker_flags $compile_deplibs" finalize_deplibs="$new_inherited_linker_flags $finalize_deplibs" else compiler_flags="$compiler_flags "`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` fi fi dependency_libs=$newdependency_libs if test dlpreopen = "$pass"; then # Link the dlpreopened libraries before other libraries for deplib in $save_deplibs; do deplibs="$deplib $deplibs" done fi if test dlopen != "$pass"; then test conv = "$pass" || { # Make sure lib_search_path contains only unique directories. lib_search_path= for dir in $newlib_search_path; do case "$lib_search_path " in *" $dir "*) ;; *) func_append lib_search_path " $dir" ;; esac done newlib_search_path= } if test prog,link = "$linkmode,$pass"; then vars="compile_deplibs finalize_deplibs" else vars=deplibs fi for var in $vars dependency_libs; do # Add libraries to $var in reverse order eval tmp_libs=\"\$$var\" new_libs= for deplib in $tmp_libs; do # FIXME: Pedantically, this is the right thing to do, so # that some nasty dependency loop isn't accidentally # broken: #new_libs="$deplib $new_libs" # Pragmatically, this seems to cause very few problems in # practice: case $deplib in -L*) new_libs="$deplib $new_libs" ;; -R*) ;; *) # And here is the reason: when a library appears more # than once as an explicit dependence of a library, or # is implicitly linked in more than once by the # compiler, it is considered special, and multiple # occurrences thereof are not removed. Compare this # with having the same library being listed as a # dependency of multiple other libraries: in this case, # we know (pedantically, we assume) the library does not # need to be listed more than once, so we keep only the # last copy. This is not always right, but it is rare # enough that we require users that really mean to play # such unportable linking tricks to link the library # using -Wl,-lname, so that libtool does not consider it # for duplicate removal. case " $specialdeplibs " in *" $deplib "*) new_libs="$deplib $new_libs" ;; *) case " $new_libs " in *" $deplib "*) ;; *) new_libs="$deplib $new_libs" ;; esac ;; esac ;; esac done tmp_libs= for deplib in $new_libs; do case $deplib in -L*) case " $tmp_libs " in *" $deplib "*) ;; *) func_append tmp_libs " $deplib" ;; esac ;; *) func_append tmp_libs " $deplib" ;; esac done eval $var=\"$tmp_libs\" done # for var fi # Add Sun CC postdeps if required: test CXX = "$tagname" && { case $host_os in linux*) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 func_suncc_cstd_abi if test no != "$suncc_use_cstd_abi"; then func_append postdeps ' -library=Cstd -library=Crun' fi ;; esac ;; solaris*) func_cc_basename "$CC" case $func_cc_basename_result in CC* | sunCC*) func_suncc_cstd_abi if test no != "$suncc_use_cstd_abi"; then func_append postdeps ' -library=Cstd -library=Crun' fi ;; esac ;; esac } # Last step: remove runtime libs from dependency_libs # (they stay in deplibs) tmp_libs= for i in $dependency_libs; do case " $predeps $postdeps $compiler_lib_search_path " in *" $i "*) i= ;; esac if test -n "$i"; then func_append tmp_libs " $i" fi done dependency_libs=$tmp_libs done # for pass if test prog = "$linkmode"; then dlfiles=$newdlfiles fi if test prog = "$linkmode" || test lib = "$linkmode"; then dlprefiles=$newdlprefiles fi case $linkmode in oldlib) if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then func_warning "'-dlopen' is ignored for archives" fi case " $deplibs" in *\ -l* | *\ -L*) func_warning "'-l' and '-L' are ignored for archives" ;; esac test -n "$rpath" && \ func_warning "'-rpath' is ignored for archives" test -n "$xrpath" && \ func_warning "'-R' is ignored for archives" test -n "$vinfo" && \ func_warning "'-version-info/-version-number' is ignored for archives" test -n "$release" && \ func_warning "'-release' is ignored for archives" test -n "$export_symbols$export_symbols_regex" && \ func_warning "'-export-symbols' is ignored for archives" # Now set the variables for building old libraries. build_libtool_libs=no oldlibs=$output func_append objs "$old_deplibs" ;; lib) # Make sure we only generate libraries of the form 'libNAME.la'. case $outputname in lib*) func_stripname 'lib' '.la' "$outputname" name=$func_stripname_result eval shared_ext=\"$shrext_cmds\" eval libname=\"$libname_spec\" ;; *) test no = "$module" \ && func_fatal_help "libtool library '$output' must begin with 'lib'" if test no != "$need_lib_prefix"; then # Add the "lib" prefix for modules if required func_stripname '' '.la' "$outputname" name=$func_stripname_result eval shared_ext=\"$shrext_cmds\" eval libname=\"$libname_spec\" else func_stripname '' '.la' "$outputname" libname=$func_stripname_result fi ;; esac if test -n "$objs"; then if test pass_all != "$deplibs_check_method"; then func_fatal_error "cannot build libtool library '$output' from non-libtool objects on this host:$objs" else echo $ECHO "*** Warning: Linking the shared library $output against the non-libtool" $ECHO "*** objects $objs is not portable!" func_append libobjs " $objs" fi fi test no = "$dlself" \ || func_warning "'-dlopen self' is ignored for libtool libraries" set dummy $rpath shift test 1 -lt "$#" \ && func_warning "ignoring multiple '-rpath's for a libtool library" install_libdir=$1 oldlibs= if test -z "$rpath"; then if test yes = "$build_libtool_libs"; then # Building a libtool convenience library. # Some compilers have problems with a '.al' extension so # convenience libraries should have the same extension an # archive normally would. oldlibs="$output_objdir/$libname.$libext $oldlibs" build_libtool_libs=convenience build_old_libs=yes fi test -n "$vinfo" && \ func_warning "'-version-info/-version-number' is ignored for convenience libraries" test -n "$release" && \ func_warning "'-release' is ignored for convenience libraries" else # Parse the version information argument. save_ifs=$IFS; IFS=: set dummy $vinfo 0 0 0 shift IFS=$save_ifs test -n "$7" && \ func_fatal_help "too many parameters to '-version-info'" # convert absolute version numbers to libtool ages # this retains compatibility with .la files and attempts # to make the code below a bit more comprehensible case $vinfo_number in yes) number_major=$1 number_minor=$2 number_revision=$3 # # There are really only two kinds -- those that # use the current revision as the major version # and those that subtract age and use age as # a minor version. But, then there is irix # that has an extra 1 added just for fun # case $version_type in # correct linux to gnu/linux during the next big refactor darwin|freebsd-elf|linux|osf|windows|none) func_arith $number_major + $number_minor current=$func_arith_result age=$number_minor revision=$number_revision ;; freebsd-aout|qnx|sunos) current=$number_major revision=$number_minor age=0 ;; irix|nonstopux) func_arith $number_major + $number_minor current=$func_arith_result age=$number_minor revision=$number_minor lt_irix_increment=no ;; *) func_fatal_configuration "$modename: unknown library version type '$version_type'" ;; esac ;; no) current=$1 revision=$2 age=$3 ;; esac # Check that each of the things are valid numbers. case $current in 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; *) func_error "CURRENT '$current' must be a nonnegative integer" func_fatal_error "'$vinfo' is not valid version information" ;; esac case $revision in 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; *) func_error "REVISION '$revision' must be a nonnegative integer" func_fatal_error "'$vinfo' is not valid version information" ;; esac case $age in 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; *) func_error "AGE '$age' must be a nonnegative integer" func_fatal_error "'$vinfo' is not valid version information" ;; esac if test "$age" -gt "$current"; then func_error "AGE '$age' is greater than the current interface number '$current'" func_fatal_error "'$vinfo' is not valid version information" fi # Calculate the version variables. major= versuffix= verstring= case $version_type in none) ;; darwin) # Like Linux, but with the current version available in # verstring for coding it into the library header func_arith $current - $age major=.$func_arith_result versuffix=$major.$age.$revision # Darwin ld doesn't like 0 for these options... func_arith $current + 1 minor_current=$func_arith_result xlcverstring="$wl-compatibility_version $wl$minor_current $wl-current_version $wl$minor_current.$revision" verstring="-compatibility_version $minor_current -current_version $minor_current.$revision" # On Darwin other compilers case $CC in nagfor*) verstring="$wl-compatibility_version $wl$minor_current $wl-current_version $wl$minor_current.$revision" ;; *) verstring="-compatibility_version $minor_current -current_version $minor_current.$revision" ;; esac ;; freebsd-aout) major=.$current versuffix=.$current.$revision ;; freebsd-elf) func_arith $current - $age major=.$func_arith_result versuffix=$major.$age.$revision ;; irix | nonstopux) if test no = "$lt_irix_increment"; then func_arith $current - $age else func_arith $current - $age + 1 fi major=$func_arith_result case $version_type in nonstopux) verstring_prefix=nonstopux ;; *) verstring_prefix=sgi ;; esac verstring=$verstring_prefix$major.$revision # Add in all the interfaces that we are compatible with. loop=$revision while test 0 -ne "$loop"; do func_arith $revision - $loop iface=$func_arith_result func_arith $loop - 1 loop=$func_arith_result verstring=$verstring_prefix$major.$iface:$verstring done # Before this point, $major must not contain '.'. major=.$major versuffix=$major.$revision ;; linux) # correct to gnu/linux during the next big refactor func_arith $current - $age major=.$func_arith_result versuffix=$major.$age.$revision ;; osf) func_arith $current - $age major=.$func_arith_result versuffix=.$current.$age.$revision verstring=$current.$age.$revision # Add in all the interfaces that we are compatible with. loop=$age while test 0 -ne "$loop"; do func_arith $current - $loop iface=$func_arith_result func_arith $loop - 1 loop=$func_arith_result verstring=$verstring:$iface.0 done # Make executables depend on our current version. func_append verstring ":$current.0" ;; qnx) major=.$current versuffix=.$current ;; sco) major=.$current versuffix=.$current ;; sunos) major=.$current versuffix=.$current.$revision ;; windows) # Use '-' rather than '.', since we only want one # extension on DOS 8.3 file systems. func_arith $current - $age major=$func_arith_result versuffix=-$major ;; *) func_fatal_configuration "unknown library version type '$version_type'" ;; esac # Clear the version info if we defaulted, and they specified a release. if test -z "$vinfo" && test -n "$release"; then major= case $version_type in darwin) # we can't check for "0.0" in archive_cmds due to quoting # problems, so we reset it completely verstring= ;; *) verstring=0.0 ;; esac if test no = "$need_version"; then versuffix= else versuffix=.0.0 fi fi # Remove version info from name if versioning should be avoided if test yes,no = "$avoid_version,$need_version"; then major= versuffix= verstring= fi # Check to see if the archive will have undefined symbols. if test yes = "$allow_undefined"; then if test unsupported = "$allow_undefined_flag"; then if test yes = "$build_old_libs"; then func_warning "undefined symbols not allowed in $host shared libraries; building static only" build_libtool_libs=no else func_fatal_error "can't build $host shared library unless -no-undefined is specified" fi fi else # Don't allow undefined symbols. allow_undefined_flag=$no_undefined_flag fi fi func_generate_dlsyms "$libname" "$libname" : func_append libobjs " $symfileobj" test " " = "$libobjs" && libobjs= if test relink != "$opt_mode"; then # Remove our outputs, but don't remove object files since they # may have been created when compiling PIC objects. removelist= tempremovelist=`$ECHO "$output_objdir/*"` for p in $tempremovelist; do case $p in *.$objext | *.gcno) ;; $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/$libname$release.*) if test -n "$precious_files_regex"; then if $ECHO "$p" | $EGREP -e "$precious_files_regex" >/dev/null 2>&1 then continue fi fi func_append removelist " $p" ;; *) ;; esac done test -n "$removelist" && \ func_show_eval "${RM}r \$removelist" fi # Now set the variables for building old libraries. if test yes = "$build_old_libs" && test convenience != "$build_libtool_libs"; then func_append oldlibs " $output_objdir/$libname.$libext" # Transform .lo files to .o files. oldobjs="$objs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.$libext$/d; $lo2o" | $NL2SP` fi # Eliminate all temporary directories. #for path in $notinst_path; do # lib_search_path=`$ECHO "$lib_search_path " | $SED "s% $path % %g"` # deplibs=`$ECHO "$deplibs " | $SED "s% -L$path % %g"` # dependency_libs=`$ECHO "$dependency_libs " | $SED "s% -L$path % %g"` #done if test -n "$xrpath"; then # If the user specified any rpath flags, then add them. temp_xrpath= for libdir in $xrpath; do func_replace_sysroot "$libdir" func_append temp_xrpath " -R$func_replace_sysroot_result" case "$finalize_rpath " in *" $libdir "*) ;; *) func_append finalize_rpath " $libdir" ;; esac done if test yes != "$hardcode_into_libs" || test yes = "$build_old_libs"; then dependency_libs="$temp_xrpath $dependency_libs" fi fi # Make sure dlfiles contains only unique files that won't be dlpreopened old_dlfiles=$dlfiles dlfiles= for lib in $old_dlfiles; do case " $dlprefiles $dlfiles " in *" $lib "*) ;; *) func_append dlfiles " $lib" ;; esac done # Make sure dlprefiles contains only unique files old_dlprefiles=$dlprefiles dlprefiles= for lib in $old_dlprefiles; do case "$dlprefiles " in *" $lib "*) ;; *) func_append dlprefiles " $lib" ;; esac done if test yes = "$build_libtool_libs"; then if test -n "$rpath"; then case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos* | *-cegcc* | *-*-haiku*) # these systems don't actually have a c library (as such)! ;; *-*-rhapsody* | *-*-darwin1.[012]) # Rhapsody C library is in the System framework func_append deplibs " System.ltframework" ;; *-*-netbsd*) # Don't link with libc until the a.out ld.so is fixed. ;; *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) # Do not include libc due to us having libc/libc_r. ;; *-*-sco3.2v5* | *-*-sco5v6*) # Causes problems with __ctype ;; *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) # Compiler inserts libc in the correct place for threads to work ;; *) # Add libc to deplibs on all other systems if necessary. if test yes = "$build_libtool_need_lc"; then func_append deplibs " -lc" fi ;; esac fi # Transform deplibs into only deplibs that can be linked in shared. name_save=$name libname_save=$libname release_save=$release versuffix_save=$versuffix major_save=$major # I'm not sure if I'm treating the release correctly. I think # release should show up in the -l (ie -lgmp5) so we don't want to # add it in twice. Is that correct? release= versuffix= major= newdeplibs= droppeddeps=no case $deplibs_check_method in pass_all) # Don't check for shared/static. Everything works. # This might be a little naive. We might want to check # whether the library exists or not. But this is on # osf3 & osf4 and I'm not really sure... Just # implementing what was already the behavior. newdeplibs=$deplibs ;; test_compile) # This code stresses the "libraries are programs" paradigm to its # limits. Maybe even breaks it. We compile a program, linking it # against the deplibs as a proxy for the library. Then we can check # whether they linked in statically or dynamically with ldd. $opt_dry_run || $RM conftest.c cat > conftest.c </dev/null` $nocaseglob else potential_libs=`ls $i/$libnameglob[.-]* 2>/dev/null` fi for potent_lib in $potential_libs; do # Follow soft links. if ls -lLd "$potent_lib" 2>/dev/null | $GREP " -> " >/dev/null; then continue fi # The statement above tries to avoid entering an # endless loop below, in case of cyclic links. # We might still enter an endless loop, since a link # loop can be closed while we follow links, # but so what? potlib=$potent_lib while test -h "$potlib" 2>/dev/null; do potliblink=`ls -ld $potlib | $SED 's/.* -> //'` case $potliblink in [\\/]* | [A-Za-z]:[\\/]*) potlib=$potliblink;; *) potlib=`$ECHO "$potlib" | $SED 's|[^/]*$||'`"$potliblink";; esac done if eval $file_magic_cmd \"\$potlib\" 2>/dev/null | $SED -e 10q | $EGREP "$file_magic_regex" > /dev/null; then func_append newdeplibs " $a_deplib" a_deplib= break 2 fi done done fi if test -n "$a_deplib"; then droppeddeps=yes echo $ECHO "*** Warning: linker path does not have real file for library $a_deplib." echo "*** I have the capability to make that library automatically link in when" echo "*** you link to this library. But I can only do this if you have a" echo "*** shared version of the library, which you do not appear to have" echo "*** because I did check the linker path looking for a file starting" if test -z "$potlib"; then $ECHO "*** with $libname but no candidates were found. (...for file magic test)" else $ECHO "*** with $libname and none of the candidates passed a file format test" $ECHO "*** using a file magic. Last file checked: $potlib" fi fi ;; *) # Add a -L argument. func_append newdeplibs " $a_deplib" ;; esac done # Gone through all deplibs. ;; match_pattern*) set dummy $deplibs_check_method; shift match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` for a_deplib in $deplibs; do case $a_deplib in -l*) func_stripname -l '' "$a_deplib" name=$func_stripname_result if test yes = "$allow_libtool_libs_with_static_runtimes"; then case " $predeps $postdeps " in *" $a_deplib "*) func_append newdeplibs " $a_deplib" a_deplib= ;; esac fi if test -n "$a_deplib"; then libname=`eval "\\$ECHO \"$libname_spec\""` for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do potential_libs=`ls $i/$libname[.-]* 2>/dev/null` for potent_lib in $potential_libs; do potlib=$potent_lib # see symlink-check above in file_magic test if eval "\$ECHO \"$potent_lib\"" 2>/dev/null | $SED 10q | \ $EGREP "$match_pattern_regex" > /dev/null; then func_append newdeplibs " $a_deplib" a_deplib= break 2 fi done done fi if test -n "$a_deplib"; then droppeddeps=yes echo $ECHO "*** Warning: linker path does not have real file for library $a_deplib." echo "*** I have the capability to make that library automatically link in when" echo "*** you link to this library. But I can only do this if you have a" echo "*** shared version of the library, which you do not appear to have" echo "*** because I did check the linker path looking for a file starting" if test -z "$potlib"; then $ECHO "*** with $libname but no candidates were found. (...for regex pattern test)" else $ECHO "*** with $libname and none of the candidates passed a file format test" $ECHO "*** using a regex pattern. Last file checked: $potlib" fi fi ;; *) # Add a -L argument. func_append newdeplibs " $a_deplib" ;; esac done # Gone through all deplibs. ;; none | unknown | *) newdeplibs= tmp_deplibs=`$ECHO " $deplibs" | $SED 's/ -lc$//; s/ -[LR][^ ]*//g'` if test yes = "$allow_libtool_libs_with_static_runtimes"; then for i in $predeps $postdeps; do # can't use Xsed below, because $i might contain '/' tmp_deplibs=`$ECHO " $tmp_deplibs" | $SED "s|$i||"` done fi case $tmp_deplibs in *[!\ \ ]*) echo if test none = "$deplibs_check_method"; then echo "*** Warning: inter-library dependencies are not supported in this platform." else echo "*** Warning: inter-library dependencies are not known to be supported." fi echo "*** All declared inter-library dependencies are being dropped." droppeddeps=yes ;; esac ;; esac versuffix=$versuffix_save major=$major_save release=$release_save libname=$libname_save name=$name_save case $host in *-*-rhapsody* | *-*-darwin1.[012]) # On Rhapsody replace the C library with the System framework newdeplibs=`$ECHO " $newdeplibs" | $SED 's/ -lc / System.ltframework /'` ;; esac if test yes = "$droppeddeps"; then if test yes = "$module"; then echo echo "*** Warning: libtool could not satisfy all declared inter-library" $ECHO "*** dependencies of module $libname. Therefore, libtool will create" echo "*** a static module, that should work as long as the dlopening" echo "*** application is linked with the -dlopen flag." if test -z "$global_symbol_pipe"; then echo echo "*** However, this would only work if libtool was able to extract symbol" echo "*** lists from a program, using 'nm' or equivalent, but libtool could" echo "*** not find such a program. So, this module is probably useless." echo "*** 'nm' from GNU binutils and a full rebuild may help." fi if test no = "$build_old_libs"; then oldlibs=$output_objdir/$libname.$libext build_libtool_libs=module build_old_libs=yes else build_libtool_libs=no fi else echo "*** The inter-library dependencies that have been dropped here will be" echo "*** automatically added whenever a program is linked with this library" echo "*** or is declared to -dlopen it." if test no = "$allow_undefined"; then echo echo "*** Since this library must not contain undefined symbols," echo "*** because either the platform does not support them or" echo "*** it was explicitly requested with -no-undefined," echo "*** libtool will only create a static version of it." if test no = "$build_old_libs"; then oldlibs=$output_objdir/$libname.$libext build_libtool_libs=module build_old_libs=yes else build_libtool_libs=no fi fi fi fi # Done checking deplibs! deplibs=$newdeplibs fi # Time to change all our "foo.ltframework" stuff back to "-framework foo" case $host in *-*-darwin*) newdeplibs=`$ECHO " $newdeplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` new_inherited_linker_flags=`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` deplibs=`$ECHO " $deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` ;; esac # move library search paths that coincide with paths to not yet # installed libraries to the beginning of the library search list new_libs= for path in $notinst_path; do case " $new_libs " in *" -L$path/$objdir "*) ;; *) case " $deplibs " in *" -L$path/$objdir "*) func_append new_libs " -L$path/$objdir" ;; esac ;; esac done for deplib in $deplibs; do case $deplib in -L*) case " $new_libs " in *" $deplib "*) ;; *) func_append new_libs " $deplib" ;; esac ;; *) func_append new_libs " $deplib" ;; esac done deplibs=$new_libs # All the library-specific variables (install_libdir is set above). library_names= old_library= dlname= # Test again, we may have decided not to build it any more if test yes = "$build_libtool_libs"; then # Remove $wl instances when linking with ld. # FIXME: should test the right _cmds variable. case $archive_cmds in *\$LD\ *) wl= ;; esac if test yes = "$hardcode_into_libs"; then # Hardcode the library paths hardcode_libdirs= dep_rpath= rpath=$finalize_rpath test relink = "$opt_mode" || rpath=$compile_rpath$rpath for libdir in $rpath; do if test -n "$hardcode_libdir_flag_spec"; then if test -n "$hardcode_libdir_separator"; then func_replace_sysroot "$libdir" libdir=$func_replace_sysroot_result if test -z "$hardcode_libdirs"; then hardcode_libdirs=$libdir else # Just accumulate the unique libdirs. case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) ;; *) func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" ;; esac fi else eval flag=\"$hardcode_libdir_flag_spec\" func_append dep_rpath " $flag" fi elif test -n "$runpath_var"; then case "$perm_rpath " in *" $libdir "*) ;; *) func_append perm_rpath " $libdir" ;; esac fi done # Substitute the hardcoded libdirs into the rpath. if test -n "$hardcode_libdir_separator" && test -n "$hardcode_libdirs"; then libdir=$hardcode_libdirs eval "dep_rpath=\"$hardcode_libdir_flag_spec\"" fi if test -n "$runpath_var" && test -n "$perm_rpath"; then # We should set the runpath_var. rpath= for dir in $perm_rpath; do func_append rpath "$dir:" done eval "$runpath_var='$rpath\$$runpath_var'; export $runpath_var" fi test -n "$dep_rpath" && deplibs="$dep_rpath $deplibs" fi shlibpath=$finalize_shlibpath test relink = "$opt_mode" || shlibpath=$compile_shlibpath$shlibpath if test -n "$shlibpath"; then eval "$shlibpath_var='$shlibpath\$$shlibpath_var'; export $shlibpath_var" fi # Get the real and link names of the library. eval shared_ext=\"$shrext_cmds\" eval library_names=\"$library_names_spec\" set dummy $library_names shift realname=$1 shift if test -n "$soname_spec"; then eval soname=\"$soname_spec\" else soname=$realname fi if test -z "$dlname"; then dlname=$soname fi lib=$output_objdir/$realname linknames= for link do func_append linknames " $link" done # Use standard objects if they are pic test -z "$pic_flag" && libobjs=`$ECHO "$libobjs" | $SP2NL | $SED "$lo2o" | $NL2SP` test "X$libobjs" = "X " && libobjs= delfiles= if test -n "$export_symbols" && test -n "$include_expsyms"; then $opt_dry_run || cp "$export_symbols" "$output_objdir/$libname.uexp" export_symbols=$output_objdir/$libname.uexp func_append delfiles " $export_symbols" fi orig_export_symbols= case $host_os in cygwin* | mingw* | cegcc*) if test -n "$export_symbols" && test -z "$export_symbols_regex"; then # exporting using user supplied symfile func_dll_def_p "$export_symbols" || { # and it's NOT already a .def file. Must figure out # which of the given symbols are data symbols and tag # them as such. So, trigger use of export_symbols_cmds. # export_symbols gets reassigned inside the "prepare # the list of exported symbols" if statement, so the # include_expsyms logic still works. orig_export_symbols=$export_symbols export_symbols= always_export_symbols=yes } fi ;; esac # Prepare the list of exported symbols if test -z "$export_symbols"; then if test yes = "$always_export_symbols" || test -n "$export_symbols_regex"; then func_verbose "generating symbol list for '$libname.la'" export_symbols=$output_objdir/$libname.exp $opt_dry_run || $RM $export_symbols cmds=$export_symbols_cmds save_ifs=$IFS; IFS='~' for cmd1 in $cmds; do IFS=$save_ifs # Take the normal branch if the nm_file_list_spec branch # doesn't work or if tool conversion is not needed. case $nm_file_list_spec~$to_tool_file_cmd in *~func_convert_file_noop | *~func_convert_file_msys_to_w32 | ~*) try_normal_branch=yes eval cmd=\"$cmd1\" func_len " $cmd" len=$func_len_result ;; *) try_normal_branch=no ;; esac if test yes = "$try_normal_branch" \ && { test "$len" -lt "$max_cmd_len" \ || test "$max_cmd_len" -le -1; } then func_show_eval "$cmd" 'exit $?' skipped_export=false elif test -n "$nm_file_list_spec"; then func_basename "$output" output_la=$func_basename_result save_libobjs=$libobjs save_output=$output output=$output_objdir/$output_la.nm func_to_tool_file "$output" libobjs=$nm_file_list_spec$func_to_tool_file_result func_append delfiles " $output" func_verbose "creating $NM input file list: $output" for obj in $save_libobjs; do func_to_tool_file "$obj" $ECHO "$func_to_tool_file_result" done > "$output" eval cmd=\"$cmd1\" func_show_eval "$cmd" 'exit $?' output=$save_output libobjs=$save_libobjs skipped_export=false else # The command line is too long to execute in one step. func_verbose "using reloadable object file for export list..." skipped_export=: # Break out early, otherwise skipped_export may be # set to false by a later but shorter cmd. break fi done IFS=$save_ifs if test -n "$export_symbols_regex" && test : != "$skipped_export"; then func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' func_show_eval '$MV "${export_symbols}T" "$export_symbols"' fi fi fi if test -n "$export_symbols" && test -n "$include_expsyms"; then tmp_export_symbols=$export_symbols test -n "$orig_export_symbols" && tmp_export_symbols=$orig_export_symbols $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"' fi if test : != "$skipped_export" && test -n "$orig_export_symbols"; then # The given exports_symbols file has to be filtered, so filter it. func_verbose "filter symbol list for '$libname.la' to tag DATA exports" # FIXME: $output_objdir/$libname.filter potentially contains lots of # 's' commands, which not all seds can handle. GNU sed should be fine # though. Also, the filter scales superlinearly with the number of # global variables. join(1) would be nice here, but unfortunately # isn't a blessed tool. $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter func_append delfiles " $export_symbols $output_objdir/$libname.filter" export_symbols=$output_objdir/$libname.def $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols fi tmp_deplibs= for test_deplib in $deplibs; do case " $convenience " in *" $test_deplib "*) ;; *) func_append tmp_deplibs " $test_deplib" ;; esac done deplibs=$tmp_deplibs if test -n "$convenience"; then if test -n "$whole_archive_flag_spec" && test yes = "$compiler_needs_object" && test -z "$libobjs"; then # extract the archives, so we have objects to list. # TODO: could optimize this to just extract one archive. whole_archive_flag_spec= fi if test -n "$whole_archive_flag_spec"; then save_libobjs=$libobjs eval libobjs=\"\$libobjs $whole_archive_flag_spec\" test "X$libobjs" = "X " && libobjs= else gentop=$output_objdir/${outputname}x func_append generated " $gentop" func_extract_archives $gentop $convenience func_append libobjs " $func_extract_archives_result" test "X$libobjs" = "X " && libobjs= fi fi if test yes = "$thread_safe" && test -n "$thread_safe_flag_spec"; then eval flag=\"$thread_safe_flag_spec\" func_append linker_flags " $flag" fi # Make a backup of the uninstalled library when relinking if test relink = "$opt_mode"; then $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}U && $MV $realname ${realname}U)' || exit $? fi # Do each of the archive commands. if test yes = "$module" && test -n "$module_cmds"; then if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then eval test_cmds=\"$module_expsym_cmds\" cmds=$module_expsym_cmds else eval test_cmds=\"$module_cmds\" cmds=$module_cmds fi else if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then eval test_cmds=\"$archive_expsym_cmds\" cmds=$archive_expsym_cmds else eval test_cmds=\"$archive_cmds\" cmds=$archive_cmds fi fi if test : != "$skipped_export" && func_len " $test_cmds" && len=$func_len_result && test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then : else # The command line is too long to link in one step, link piecewise # or, if using GNU ld and skipped_export is not :, use a linker # script. # Save the value of $output and $libobjs because we want to # use them later. If we have whole_archive_flag_spec, we # want to use save_libobjs as it was before # whole_archive_flag_spec was expanded, because we can't # assume the linker understands whole_archive_flag_spec. # This may have to be revisited, in case too many # convenience libraries get linked in and end up exceeding # the spec. if test -z "$convenience" || test -z "$whole_archive_flag_spec"; then save_libobjs=$libobjs fi save_output=$output func_basename "$output" output_la=$func_basename_result # Clear the reloadable object creation command queue and # initialize k to one. test_cmds= concat_cmds= objlist= last_robj= k=1 if test -n "$save_libobjs" && test : != "$skipped_export" && test yes = "$with_gnu_ld"; then output=$output_objdir/$output_la.lnkscript func_verbose "creating GNU ld script: $output" echo 'INPUT (' > $output for obj in $save_libobjs do func_to_tool_file "$obj" $ECHO "$func_to_tool_file_result" >> $output done echo ')' >> $output func_append delfiles " $output" func_to_tool_file "$output" output=$func_to_tool_file_result elif test -n "$save_libobjs" && test : != "$skipped_export" && test -n "$file_list_spec"; then output=$output_objdir/$output_la.lnk func_verbose "creating linker input file list: $output" : > $output set x $save_libobjs shift firstobj= if test yes = "$compiler_needs_object"; then firstobj="$1 " shift fi for obj do func_to_tool_file "$obj" $ECHO "$func_to_tool_file_result" >> $output done func_append delfiles " $output" func_to_tool_file "$output" output=$firstobj\"$file_list_spec$func_to_tool_file_result\" else if test -n "$save_libobjs"; then func_verbose "creating reloadable object files..." output=$output_objdir/$output_la-$k.$objext eval test_cmds=\"$reload_cmds\" func_len " $test_cmds" len0=$func_len_result len=$len0 # Loop over the list of objects to be linked. for obj in $save_libobjs do func_len " $obj" func_arith $len + $func_len_result len=$func_arith_result if test -z "$objlist" || test "$len" -lt "$max_cmd_len"; then func_append objlist " $obj" else # The command $test_cmds is almost too long, add a # command to the queue. if test 1 -eq "$k"; then # The first file doesn't have a previous command to add. reload_objs=$objlist eval concat_cmds=\"$reload_cmds\" else # All subsequent reloadable object files will link in # the last one created. reload_objs="$objlist $last_robj" eval concat_cmds=\"\$concat_cmds~$reload_cmds~\$RM $last_robj\" fi last_robj=$output_objdir/$output_la-$k.$objext func_arith $k + 1 k=$func_arith_result output=$output_objdir/$output_la-$k.$objext objlist=" $obj" func_len " $last_robj" func_arith $len0 + $func_len_result len=$func_arith_result fi done # Handle the remaining objects by creating one last # reloadable object file. All subsequent reloadable object # files will link in the last one created. test -z "$concat_cmds" || concat_cmds=$concat_cmds~ reload_objs="$objlist $last_robj" eval concat_cmds=\"\$concat_cmds$reload_cmds\" if test -n "$last_robj"; then eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\" fi func_append delfiles " $output" else output= fi ${skipped_export-false} && { func_verbose "generating symbol list for '$libname.la'" export_symbols=$output_objdir/$libname.exp $opt_dry_run || $RM $export_symbols libobjs=$output # Append the command to create the export file. test -z "$concat_cmds" || concat_cmds=$concat_cmds~ eval concat_cmds=\"\$concat_cmds$export_symbols_cmds\" if test -n "$last_robj"; then eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\" fi } test -n "$save_libobjs" && func_verbose "creating a temporary reloadable object file: $output" # Loop through the commands generated above and execute them. save_ifs=$IFS; IFS='~' for cmd in $concat_cmds; do IFS=$save_ifs $opt_quiet || { func_quote_for_expand "$cmd" eval "func_echo $func_quote_for_expand_result" } $opt_dry_run || eval "$cmd" || { lt_exit=$? # Restore the uninstalled library and exit if test relink = "$opt_mode"; then ( cd "$output_objdir" && \ $RM "${realname}T" && \ $MV "${realname}U" "$realname" ) fi exit $lt_exit } done IFS=$save_ifs if test -n "$export_symbols_regex" && ${skipped_export-false}; then func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' func_show_eval '$MV "${export_symbols}T" "$export_symbols"' fi fi ${skipped_export-false} && { if test -n "$export_symbols" && test -n "$include_expsyms"; then tmp_export_symbols=$export_symbols test -n "$orig_export_symbols" && tmp_export_symbols=$orig_export_symbols $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"' fi if test -n "$orig_export_symbols"; then # The given exports_symbols file has to be filtered, so filter it. func_verbose "filter symbol list for '$libname.la' to tag DATA exports" # FIXME: $output_objdir/$libname.filter potentially contains lots of # 's' commands, which not all seds can handle. GNU sed should be fine # though. Also, the filter scales superlinearly with the number of # global variables. join(1) would be nice here, but unfortunately # isn't a blessed tool. $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter func_append delfiles " $export_symbols $output_objdir/$libname.filter" export_symbols=$output_objdir/$libname.def $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols fi } libobjs=$output # Restore the value of output. output=$save_output if test -n "$convenience" && test -n "$whole_archive_flag_spec"; then eval libobjs=\"\$libobjs $whole_archive_flag_spec\" test "X$libobjs" = "X " && libobjs= fi # Expand the library linking commands again to reset the # value of $libobjs for piecewise linking. # Do each of the archive commands. if test yes = "$module" && test -n "$module_cmds"; then if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then cmds=$module_expsym_cmds else cmds=$module_cmds fi else if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then cmds=$archive_expsym_cmds else cmds=$archive_cmds fi fi fi if test -n "$delfiles"; then # Append the command to remove temporary files to $cmds. eval cmds=\"\$cmds~\$RM $delfiles\" fi # Add any objects from preloaded convenience libraries if test -n "$dlprefiles"; then gentop=$output_objdir/${outputname}x func_append generated " $gentop" func_extract_archives $gentop $dlprefiles func_append libobjs " $func_extract_archives_result" test "X$libobjs" = "X " && libobjs= fi save_ifs=$IFS; IFS='~' for cmd in $cmds; do IFS=$sp$nl eval cmd=\"$cmd\" IFS=$save_ifs $opt_quiet || { func_quote_for_expand "$cmd" eval "func_echo $func_quote_for_expand_result" } $opt_dry_run || eval "$cmd" || { lt_exit=$? # Restore the uninstalled library and exit if test relink = "$opt_mode"; then ( cd "$output_objdir" && \ $RM "${realname}T" && \ $MV "${realname}U" "$realname" ) fi exit $lt_exit } done IFS=$save_ifs # Restore the uninstalled library and exit if test relink = "$opt_mode"; then $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}T && $MV $realname ${realname}T && $MV ${realname}U $realname)' || exit $? if test -n "$convenience"; then if test -z "$whole_archive_flag_spec"; then func_show_eval '${RM}r "$gentop"' fi fi exit $EXIT_SUCCESS fi # Create links to the real library. for linkname in $linknames; do if test "$realname" != "$linkname"; then func_show_eval '(cd "$output_objdir" && $RM "$linkname" && $LN_S "$realname" "$linkname")' 'exit $?' fi done # If -module or -export-dynamic was specified, set the dlname. if test yes = "$module" || test yes = "$export_dynamic"; then # On all known operating systems, these are identical. dlname=$soname fi fi ;; obj) if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then func_warning "'-dlopen' is ignored for objects" fi case " $deplibs" in *\ -l* | *\ -L*) func_warning "'-l' and '-L' are ignored for objects" ;; esac test -n "$rpath" && \ func_warning "'-rpath' is ignored for objects" test -n "$xrpath" && \ func_warning "'-R' is ignored for objects" test -n "$vinfo" && \ func_warning "'-version-info' is ignored for objects" test -n "$release" && \ func_warning "'-release' is ignored for objects" case $output in *.lo) test -n "$objs$old_deplibs" && \ func_fatal_error "cannot build library object '$output' from non-libtool objects" libobj=$output func_lo2o "$libobj" obj=$func_lo2o_result ;; *) libobj= obj=$output ;; esac # Delete the old objects. $opt_dry_run || $RM $obj $libobj # Objects from convenience libraries. This assumes # single-version convenience libraries. Whenever we create # different ones for PIC/non-PIC, this we'll have to duplicate # the extraction. reload_conv_objs= gentop= # if reload_cmds runs $LD directly, get rid of -Wl from # whole_archive_flag_spec and hope we can get by with turning comma # into space. case $reload_cmds in *\$LD[\ \$]*) wl= ;; esac if test -n "$convenience"; then if test -n "$whole_archive_flag_spec"; then eval tmp_whole_archive_flags=\"$whole_archive_flag_spec\" test -n "$wl" || tmp_whole_archive_flags=`$ECHO "$tmp_whole_archive_flags" | $SED 's|,| |g'` reload_conv_objs=$reload_objs\ $tmp_whole_archive_flags else gentop=$output_objdir/${obj}x func_append generated " $gentop" func_extract_archives $gentop $convenience reload_conv_objs="$reload_objs $func_extract_archives_result" fi fi # If we're not building shared, we need to use non_pic_objs test yes = "$build_libtool_libs" || libobjs=$non_pic_objects # Create the old-style object. reload_objs=$objs$old_deplibs' '`$ECHO "$libobjs" | $SP2NL | $SED "/\.$libext$/d; /\.lib$/d; $lo2o" | $NL2SP`' '$reload_conv_objs output=$obj func_execute_cmds "$reload_cmds" 'exit $?' # Exit if we aren't doing a library object file. if test -z "$libobj"; then if test -n "$gentop"; then func_show_eval '${RM}r "$gentop"' fi exit $EXIT_SUCCESS fi test yes = "$build_libtool_libs" || { if test -n "$gentop"; then func_show_eval '${RM}r "$gentop"' fi # Create an invalid libtool object if no PIC, so that we don't # accidentally link it into a program. # $show "echo timestamp > $libobj" # $opt_dry_run || eval "echo timestamp > $libobj" || exit $? exit $EXIT_SUCCESS } if test -n "$pic_flag" || test default != "$pic_mode"; then # Only do commands if we really have different PIC objects. reload_objs="$libobjs $reload_conv_objs" output=$libobj func_execute_cmds "$reload_cmds" 'exit $?' fi if test -n "$gentop"; then func_show_eval '${RM}r "$gentop"' fi exit $EXIT_SUCCESS ;; prog) case $host in *cygwin*) func_stripname '' '.exe' "$output" output=$func_stripname_result.exe;; esac test -n "$vinfo" && \ func_warning "'-version-info' is ignored for programs" test -n "$release" && \ func_warning "'-release' is ignored for programs" $preload \ && test unknown,unknown,unknown = "$dlopen_support,$dlopen_self,$dlopen_self_static" \ && func_warning "'LT_INIT([dlopen])' not used. Assuming no dlopen support." case $host in *-*-rhapsody* | *-*-darwin1.[012]) # On Rhapsody replace the C library is the System framework compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's/ -lc / System.ltframework /'` finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's/ -lc / System.ltframework /'` ;; esac case $host in *-*-darwin*) # Don't allow lazy linking, it breaks C++ global constructors # But is supposedly fixed on 10.4 or later (yay!). if test CXX = "$tagname"; then case ${MACOSX_DEPLOYMENT_TARGET-10.0} in 10.[0123]) func_append compile_command " $wl-bind_at_load" func_append finalize_command " $wl-bind_at_load" ;; esac fi # Time to change all our "foo.ltframework" stuff back to "-framework foo" compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` ;; esac # move library search paths that coincide with paths to not yet # installed libraries to the beginning of the library search list new_libs= for path in $notinst_path; do case " $new_libs " in *" -L$path/$objdir "*) ;; *) case " $compile_deplibs " in *" -L$path/$objdir "*) func_append new_libs " -L$path/$objdir" ;; esac ;; esac done for deplib in $compile_deplibs; do case $deplib in -L*) case " $new_libs " in *" $deplib "*) ;; *) func_append new_libs " $deplib" ;; esac ;; *) func_append new_libs " $deplib" ;; esac done compile_deplibs=$new_libs func_append compile_command " $compile_deplibs" func_append finalize_command " $finalize_deplibs" if test -n "$rpath$xrpath"; then # If the user specified any rpath flags, then add them. for libdir in $rpath $xrpath; do # This is the magic to use -rpath. case "$finalize_rpath " in *" $libdir "*) ;; *) func_append finalize_rpath " $libdir" ;; esac done fi # Now hardcode the library paths rpath= hardcode_libdirs= for libdir in $compile_rpath $finalize_rpath; do if test -n "$hardcode_libdir_flag_spec"; then if test -n "$hardcode_libdir_separator"; then if test -z "$hardcode_libdirs"; then hardcode_libdirs=$libdir else # Just accumulate the unique libdirs. case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) ;; *) func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" ;; esac fi else eval flag=\"$hardcode_libdir_flag_spec\" func_append rpath " $flag" fi elif test -n "$runpath_var"; then case "$perm_rpath " in *" $libdir "*) ;; *) func_append perm_rpath " $libdir" ;; esac fi case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) testbindir=`$ECHO "$libdir" | $SED -e 's*/lib$*/bin*'` case :$dllsearchpath: in *":$libdir:"*) ;; ::) dllsearchpath=$libdir;; *) func_append dllsearchpath ":$libdir";; esac case :$dllsearchpath: in *":$testbindir:"*) ;; ::) dllsearchpath=$testbindir;; *) func_append dllsearchpath ":$testbindir";; esac ;; esac done # Substitute the hardcoded libdirs into the rpath. if test -n "$hardcode_libdir_separator" && test -n "$hardcode_libdirs"; then libdir=$hardcode_libdirs eval rpath=\" $hardcode_libdir_flag_spec\" fi compile_rpath=$rpath rpath= hardcode_libdirs= for libdir in $finalize_rpath; do if test -n "$hardcode_libdir_flag_spec"; then if test -n "$hardcode_libdir_separator"; then if test -z "$hardcode_libdirs"; then hardcode_libdirs=$libdir else # Just accumulate the unique libdirs. case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) ;; *) func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" ;; esac fi else eval flag=\"$hardcode_libdir_flag_spec\" func_append rpath " $flag" fi elif test -n "$runpath_var"; then case "$finalize_perm_rpath " in *" $libdir "*) ;; *) func_append finalize_perm_rpath " $libdir" ;; esac fi done # Substitute the hardcoded libdirs into the rpath. if test -n "$hardcode_libdir_separator" && test -n "$hardcode_libdirs"; then libdir=$hardcode_libdirs eval rpath=\" $hardcode_libdir_flag_spec\" fi finalize_rpath=$rpath if test -n "$libobjs" && test yes = "$build_old_libs"; then # Transform all the library objects into standard objects. compile_command=`$ECHO "$compile_command" | $SP2NL | $SED "$lo2o" | $NL2SP` finalize_command=`$ECHO "$finalize_command" | $SP2NL | $SED "$lo2o" | $NL2SP` fi func_generate_dlsyms "$outputname" "@PROGRAM@" false # template prelinking step if test -n "$prelink_cmds"; then func_execute_cmds "$prelink_cmds" 'exit $?' fi wrappers_required=: case $host in *cegcc* | *mingw32ce*) # Disable wrappers for cegcc and mingw32ce hosts, we are cross compiling anyway. wrappers_required=false ;; *cygwin* | *mingw* ) test yes = "$build_libtool_libs" || wrappers_required=false ;; *) if test no = "$need_relink" || test yes != "$build_libtool_libs"; then wrappers_required=false fi ;; esac $wrappers_required || { # Replace the output file specification. compile_command=`$ECHO "$compile_command" | $SED 's%@OUTPUT@%'"$output"'%g'` link_command=$compile_command$compile_rpath # We have no uninstalled library dependencies, so finalize right now. exit_status=0 func_show_eval "$link_command" 'exit_status=$?' if test -n "$postlink_cmds"; then func_to_tool_file "$output" postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` func_execute_cmds "$postlink_cmds" 'exit $?' fi # Delete the generated files. if test -f "$output_objdir/${outputname}S.$objext"; then func_show_eval '$RM "$output_objdir/${outputname}S.$objext"' fi exit $exit_status } if test -n "$compile_shlibpath$finalize_shlibpath"; then compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command" fi if test -n "$finalize_shlibpath"; then finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command" fi compile_var= finalize_var= if test -n "$runpath_var"; then if test -n "$perm_rpath"; then # We should set the runpath_var. rpath= for dir in $perm_rpath; do func_append rpath "$dir:" done compile_var="$runpath_var=\"$rpath\$$runpath_var\" " fi if test -n "$finalize_perm_rpath"; then # We should set the runpath_var. rpath= for dir in $finalize_perm_rpath; do func_append rpath "$dir:" done finalize_var="$runpath_var=\"$rpath\$$runpath_var\" " fi fi if test yes = "$no_install"; then # We don't need to create a wrapper script. link_command=$compile_var$compile_command$compile_rpath # Replace the output file specification. link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output"'%g'` # Delete the old output file. $opt_dry_run || $RM $output # Link the executable and exit func_show_eval "$link_command" 'exit $?' if test -n "$postlink_cmds"; then func_to_tool_file "$output" postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` func_execute_cmds "$postlink_cmds" 'exit $?' fi exit $EXIT_SUCCESS fi case $hardcode_action,$fast_install in relink,*) # Fast installation is not supported link_command=$compile_var$compile_command$compile_rpath relink_command=$finalize_var$finalize_command$finalize_rpath func_warning "this platform does not like uninstalled shared libraries" func_warning "'$output' will be relinked during installation" ;; *,yes) link_command=$finalize_var$compile_command$finalize_rpath relink_command=`$ECHO "$compile_var$compile_command$compile_rpath" | $SED 's%@OUTPUT@%\$progdir/\$file%g'` ;; *,no) link_command=$compile_var$compile_command$compile_rpath relink_command=$finalize_var$finalize_command$finalize_rpath ;; *,needless) link_command=$finalize_var$compile_command$finalize_rpath relink_command= ;; esac # Replace the output file specification. link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'` # Delete the old output files. $opt_dry_run || $RM $output $output_objdir/$outputname $output_objdir/lt-$outputname func_show_eval "$link_command" 'exit $?' if test -n "$postlink_cmds"; then func_to_tool_file "$output_objdir/$outputname" postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` func_execute_cmds "$postlink_cmds" 'exit $?' fi # Now create the wrapper script. func_verbose "creating $output" # Quote the relink command for shipping. if test -n "$relink_command"; then # Preserve any variables that may affect compiler behavior for var in $variables_saved_for_relink; do if eval test -z \"\${$var+set}\"; then relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command" elif eval var_value=\$$var; test -z "$var_value"; then relink_command="$var=; export $var; $relink_command" else func_quote_for_eval "$var_value" relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command" fi done relink_command="(cd `pwd`; $relink_command)" relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"` fi # Only actually do things if not in dry run mode. $opt_dry_run || { # win32 will think the script is a binary if it has # a .exe suffix, so we strip it off here. case $output in *.exe) func_stripname '' '.exe' "$output" output=$func_stripname_result ;; esac # test for cygwin because mv fails w/o .exe extensions case $host in *cygwin*) exeext=.exe func_stripname '' '.exe' "$outputname" outputname=$func_stripname_result ;; *) exeext= ;; esac case $host in *cygwin* | *mingw* ) func_dirname_and_basename "$output" "" "." output_name=$func_basename_result output_path=$func_dirname_result cwrappersource=$output_path/$objdir/lt-$output_name.c cwrapper=$output_path/$output_name.exe $RM $cwrappersource $cwrapper trap "$RM $cwrappersource $cwrapper; exit $EXIT_FAILURE" 1 2 15 func_emit_cwrapperexe_src > $cwrappersource # The wrapper executable is built using the $host compiler, # because it contains $host paths and files. If cross- # compiling, it, like the target executable, must be # executed on the $host or under an emulation environment. $opt_dry_run || { $LTCC $LTCFLAGS -o $cwrapper $cwrappersource $STRIP $cwrapper } # Now, create the wrapper script for func_source use: func_ltwrapper_scriptname $cwrapper $RM $func_ltwrapper_scriptname_result trap "$RM $func_ltwrapper_scriptname_result; exit $EXIT_FAILURE" 1 2 15 $opt_dry_run || { # note: this script will not be executed, so do not chmod. if test "x$build" = "x$host"; then $cwrapper --lt-dump-script > $func_ltwrapper_scriptname_result else func_emit_wrapper no > $func_ltwrapper_scriptname_result fi } ;; * ) $RM $output trap "$RM $output; exit $EXIT_FAILURE" 1 2 15 func_emit_wrapper no > $output chmod +x $output ;; esac } exit $EXIT_SUCCESS ;; esac # See if we need to build an old-fashioned archive. for oldlib in $oldlibs; do case $build_libtool_libs in convenience) oldobjs="$libobjs_save $symfileobj" addlibs=$convenience build_libtool_libs=no ;; module) oldobjs=$libobjs_save addlibs=$old_convenience build_libtool_libs=no ;; *) oldobjs="$old_deplibs $non_pic_objects" $preload && test -f "$symfileobj" \ && func_append oldobjs " $symfileobj" addlibs=$old_convenience ;; esac if test -n "$addlibs"; then gentop=$output_objdir/${outputname}x func_append generated " $gentop" func_extract_archives $gentop $addlibs func_append oldobjs " $func_extract_archives_result" fi # Do each command in the archive commands. if test -n "$old_archive_from_new_cmds" && test yes = "$build_libtool_libs"; then cmds=$old_archive_from_new_cmds else # Add any objects from preloaded convenience libraries if test -n "$dlprefiles"; then gentop=$output_objdir/${outputname}x func_append generated " $gentop" func_extract_archives $gentop $dlprefiles func_append oldobjs " $func_extract_archives_result" fi # POSIX demands no paths to be encoded in archives. We have # to avoid creating archives with duplicate basenames if we # might have to extract them afterwards, e.g., when creating a # static archive out of a convenience library, or when linking # the entirety of a libtool archive into another (currently # not supported by libtool). if (for obj in $oldobjs do func_basename "$obj" $ECHO "$func_basename_result" done | sort | sort -uc >/dev/null 2>&1); then : else echo "copying selected object files to avoid basename conflicts..." gentop=$output_objdir/${outputname}x func_append generated " $gentop" func_mkdir_p "$gentop" save_oldobjs=$oldobjs oldobjs= counter=1 for obj in $save_oldobjs do func_basename "$obj" objbase=$func_basename_result case " $oldobjs " in " ") oldobjs=$obj ;; *[\ /]"$objbase "*) while :; do # Make sure we don't pick an alternate name that also # overlaps. newobj=lt$counter-$objbase func_arith $counter + 1 counter=$func_arith_result case " $oldobjs " in *[\ /]"$newobj "*) ;; *) if test ! -f "$gentop/$newobj"; then break; fi ;; esac done func_show_eval "ln $obj $gentop/$newobj || cp $obj $gentop/$newobj" func_append oldobjs " $gentop/$newobj" ;; *) func_append oldobjs " $obj" ;; esac done fi func_to_tool_file "$oldlib" func_convert_file_msys_to_w32 tool_oldlib=$func_to_tool_file_result eval cmds=\"$old_archive_cmds\" func_len " $cmds" len=$func_len_result if test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then cmds=$old_archive_cmds elif test -n "$archiver_list_spec"; then func_verbose "using command file archive linking..." for obj in $oldobjs do func_to_tool_file "$obj" $ECHO "$func_to_tool_file_result" done > $output_objdir/$libname.libcmd func_to_tool_file "$output_objdir/$libname.libcmd" oldobjs=" $archiver_list_spec$func_to_tool_file_result" cmds=$old_archive_cmds else # the command line is too long to link in one step, link in parts func_verbose "using piecewise archive linking..." save_RANLIB=$RANLIB RANLIB=: objlist= concat_cmds= save_oldobjs=$oldobjs oldobjs= # Is there a better way of finding the last object in the list? for obj in $save_oldobjs do last_oldobj=$obj done eval test_cmds=\"$old_archive_cmds\" func_len " $test_cmds" len0=$func_len_result len=$len0 for obj in $save_oldobjs do func_len " $obj" func_arith $len + $func_len_result len=$func_arith_result func_append objlist " $obj" if test "$len" -lt "$max_cmd_len"; then : else # the above command should be used before it gets too long oldobjs=$objlist if test "$obj" = "$last_oldobj"; then RANLIB=$save_RANLIB fi test -z "$concat_cmds" || concat_cmds=$concat_cmds~ eval concat_cmds=\"\$concat_cmds$old_archive_cmds\" objlist= len=$len0 fi done RANLIB=$save_RANLIB oldobjs=$objlist if test -z "$oldobjs"; then eval cmds=\"\$concat_cmds\" else eval cmds=\"\$concat_cmds~\$old_archive_cmds\" fi fi fi func_execute_cmds "$cmds" 'exit $?' done test -n "$generated" && \ func_show_eval "${RM}r$generated" # Now create the libtool archive. case $output in *.la) old_library= test yes = "$build_old_libs" && old_library=$libname.$libext func_verbose "creating $output" # Preserve any variables that may affect compiler behavior for var in $variables_saved_for_relink; do if eval test -z \"\${$var+set}\"; then relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command" elif eval var_value=\$$var; test -z "$var_value"; then relink_command="$var=; export $var; $relink_command" else func_quote_for_eval "$var_value" relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command" fi done # Quote the link command for shipping. relink_command="(cd `pwd`; $SHELL \"$progpath\" $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)" relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"` if test yes = "$hardcode_automatic"; then relink_command= fi # Only create the output if not a dry run. $opt_dry_run || { for installed in no yes; do if test yes = "$installed"; then if test -z "$install_libdir"; then break fi output=$output_objdir/${outputname}i # Replace all uninstalled libtool libraries with the installed ones newdependency_libs= for deplib in $dependency_libs; do case $deplib in *.la) func_basename "$deplib" name=$func_basename_result func_resolve_sysroot "$deplib" eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $func_resolve_sysroot_result` test -z "$libdir" && \ func_fatal_error "'$deplib' is not a valid libtool archive" func_append newdependency_libs " ${lt_sysroot:+=}$libdir/$name" ;; -L*) func_stripname -L '' "$deplib" func_replace_sysroot "$func_stripname_result" func_append newdependency_libs " -L$func_replace_sysroot_result" ;; -R*) func_stripname -R '' "$deplib" func_replace_sysroot "$func_stripname_result" func_append newdependency_libs " -R$func_replace_sysroot_result" ;; *) func_append newdependency_libs " $deplib" ;; esac done dependency_libs=$newdependency_libs newdlfiles= for lib in $dlfiles; do case $lib in *.la) func_basename "$lib" name=$func_basename_result eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $lib` test -z "$libdir" && \ func_fatal_error "'$lib' is not a valid libtool archive" func_append newdlfiles " ${lt_sysroot:+=}$libdir/$name" ;; *) func_append newdlfiles " $lib" ;; esac done dlfiles=$newdlfiles newdlprefiles= for lib in $dlprefiles; do case $lib in *.la) # Only pass preopened files to the pseudo-archive (for # eventual linking with the app. that links it) if we # didn't already link the preopened objects directly into # the library: func_basename "$lib" name=$func_basename_result eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $lib` test -z "$libdir" && \ func_fatal_error "'$lib' is not a valid libtool archive" func_append newdlprefiles " ${lt_sysroot:+=}$libdir/$name" ;; esac done dlprefiles=$newdlprefiles else newdlfiles= for lib in $dlfiles; do case $lib in [\\/]* | [A-Za-z]:[\\/]*) abs=$lib ;; *) abs=`pwd`"/$lib" ;; esac func_append newdlfiles " $abs" done dlfiles=$newdlfiles newdlprefiles= for lib in $dlprefiles; do case $lib in [\\/]* | [A-Za-z]:[\\/]*) abs=$lib ;; *) abs=`pwd`"/$lib" ;; esac func_append newdlprefiles " $abs" done dlprefiles=$newdlprefiles fi $RM $output # place dlname in correct position for cygwin # In fact, it would be nice if we could use this code for all target # systems that can't hard-code library paths into their executables # and that have no shared library path variable independent of PATH, # but it turns out we can't easily determine that from inspecting # libtool variables, so we have to hard-code the OSs to which it # applies here; at the moment, that means platforms that use the PE # object format with DLL files. See the long comment at the top of # tests/bindir.at for full details. tdlname=$dlname case $host,$output,$installed,$module,$dlname in *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll | *cegcc*,*lai,yes,no,*.dll) # If a -bindir argument was supplied, place the dll there. if test -n "$bindir"; then func_relative_path "$install_libdir" "$bindir" tdlname=$func_relative_path_result/$dlname else # Otherwise fall back on heuristic. tdlname=../bin/$dlname fi ;; esac $ECHO > $output "\ # $outputname - a libtool library file # Generated by $PROGRAM (GNU $PACKAGE) $VERSION # # Please DO NOT delete this file! # It is necessary for linking the library. # The name that we can dlopen(3). dlname='$tdlname' # Names of this library. library_names='$library_names' # The name of the static archive. old_library='$old_library' # Linker flags that cannot go in dependency_libs. inherited_linker_flags='$new_inherited_linker_flags' # Libraries that this one depends upon. dependency_libs='$dependency_libs' # Names of additional weak libraries provided by this library weak_library_names='$weak_libs' # Version information for $libname. current=$current age=$age revision=$revision # Is this an already installed library? installed=$installed # Should we warn about portability when linking against -modules? shouldnotlink=$module # Files to dlopen/dlpreopen dlopen='$dlfiles' dlpreopen='$dlprefiles' # Directory that this library needs to be installed in: libdir='$install_libdir'" if test no,yes = "$installed,$need_relink"; then $ECHO >> $output "\ relink_command=\"$relink_command\"" fi done } # Do a symbolic link so that the libtool archive can be found in # LD_LIBRARY_PATH before the program is installed. func_show_eval '( cd "$output_objdir" && $RM "$outputname" && $LN_S "../$outputname" "$outputname" )' 'exit $?' ;; esac exit $EXIT_SUCCESS } if test link = "$opt_mode" || test relink = "$opt_mode"; then func_mode_link ${1+"$@"} fi # func_mode_uninstall arg... func_mode_uninstall () { $debug_cmd RM=$nonopt files= rmforce=false exit_status=0 # This variable tells wrapper scripts just to set variables rather # than running their programs. libtool_install_magic=$magic for arg do case $arg in -f) func_append RM " $arg"; rmforce=: ;; -*) func_append RM " $arg" ;; *) func_append files " $arg" ;; esac done test -z "$RM" && \ func_fatal_help "you must specify an RM program" rmdirs= for file in $files; do func_dirname "$file" "" "." dir=$func_dirname_result if test . = "$dir"; then odir=$objdir else odir=$dir/$objdir fi func_basename "$file" name=$func_basename_result test uninstall = "$opt_mode" && odir=$dir # Remember odir for removal later, being careful to avoid duplicates if test clean = "$opt_mode"; then case " $rmdirs " in *" $odir "*) ;; *) func_append rmdirs " $odir" ;; esac fi # Don't error if the file doesn't exist and rm -f was used. if { test -L "$file"; } >/dev/null 2>&1 || { test -h "$file"; } >/dev/null 2>&1 || test -f "$file"; then : elif test -d "$file"; then exit_status=1 continue elif $rmforce; then continue fi rmfiles=$file case $name in *.la) # Possibly a libtool archive, so verify it. if func_lalib_p "$file"; then func_source $dir/$name # Delete the libtool libraries and symlinks. for n in $library_names; do func_append rmfiles " $odir/$n" done test -n "$old_library" && func_append rmfiles " $odir/$old_library" case $opt_mode in clean) case " $library_names " in *" $dlname "*) ;; *) test -n "$dlname" && func_append rmfiles " $odir/$dlname" ;; esac test -n "$libdir" && func_append rmfiles " $odir/$name $odir/${name}i" ;; uninstall) if test -n "$library_names"; then # Do each command in the postuninstall commands. func_execute_cmds "$postuninstall_cmds" '$rmforce || exit_status=1' fi if test -n "$old_library"; then # Do each command in the old_postuninstall commands. func_execute_cmds "$old_postuninstall_cmds" '$rmforce || exit_status=1' fi # FIXME: should reinstall the best remaining shared library. ;; esac fi ;; *.lo) # Possibly a libtool object, so verify it. if func_lalib_p "$file"; then # Read the .lo file func_source $dir/$name # Add PIC object to the list of files to remove. if test -n "$pic_object" && test none != "$pic_object"; then func_append rmfiles " $dir/$pic_object" fi # Add non-PIC object to the list of files to remove. if test -n "$non_pic_object" && test none != "$non_pic_object"; then func_append rmfiles " $dir/$non_pic_object" fi fi ;; *) if test clean = "$opt_mode"; then noexename=$name case $file in *.exe) func_stripname '' '.exe' "$file" file=$func_stripname_result func_stripname '' '.exe' "$name" noexename=$func_stripname_result # $file with .exe has already been added to rmfiles, # add $file without .exe func_append rmfiles " $file" ;; esac # Do a test to see if this is a libtool program. if func_ltwrapper_p "$file"; then if func_ltwrapper_executable_p "$file"; then func_ltwrapper_scriptname "$file" relink_command= func_source $func_ltwrapper_scriptname_result func_append rmfiles " $func_ltwrapper_scriptname_result" else relink_command= func_source $dir/$noexename fi # note $name still contains .exe if it was in $file originally # as does the version of $file that was added into $rmfiles func_append rmfiles " $odir/$name $odir/${name}S.$objext" if test yes = "$fast_install" && test -n "$relink_command"; then func_append rmfiles " $odir/lt-$name" fi if test "X$noexename" != "X$name"; then func_append rmfiles " $odir/lt-$noexename.c" fi fi fi ;; esac func_show_eval "$RM $rmfiles" 'exit_status=1' done # Try to remove the $objdir's in the directories where we deleted files for dir in $rmdirs; do if test -d "$dir"; then func_show_eval "rmdir $dir >/dev/null 2>&1" fi done exit $exit_status } if test uninstall = "$opt_mode" || test clean = "$opt_mode"; then func_mode_uninstall ${1+"$@"} fi test -z "$opt_mode" && { help=$generic_help func_fatal_help "you must specify a MODE" } test -z "$exec_cmd" && \ func_fatal_help "invalid operation mode '$opt_mode'" if test -n "$exec_cmd"; then eval exec "$exec_cmd" exit $EXIT_FAILURE fi exit $exit_status # The TAGs below are defined such that we never get into a situation # where we disable both kinds of libraries. Given conflicting # choices, we go for a static library, that is the most portable, # since we can't tell whether shared libraries were disabled because # the user asked for that or because the platform doesn't support # them. This is particularly important on AIX, because we don't # support having both static and shared libraries enabled at the same # time on that platform, so we default to a shared-only configuration. # If a disable-shared tag is given, we'll fallback to a static-only # configuration. But we'll never go from static-only to shared-only. # ### BEGIN LIBTOOL TAG CONFIG: disable-shared build_libtool_libs=no build_old_libs=yes # ### END LIBTOOL TAG CONFIG: disable-shared # ### BEGIN LIBTOOL TAG CONFIG: disable-static build_old_libs=`case $build_libtool_libs in yes) echo no;; *) echo yes;; esac` # ### END LIBTOOL TAG CONFIG: disable-static # Local Variables: # mode:shell-script # sh-indentation:2 # End: libopenmpt-0.6.1+release.autotools/build-aux/missing0000755000175000017500000001533614175541545017570 00000000000000#! /bin/sh # Common wrapper for a few potentially missing GNU programs. scriptversion=2018-03-07.03; # UTC # Copyright (C) 1996-2020 Free Software Foundation, Inc. # Originally written by Fran,cois Pinard , 1996. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. if test $# -eq 0; then echo 1>&2 "Try '$0 --help' for more information" exit 1 fi case $1 in --is-lightweight) # Used by our autoconf macros to check whether the available missing # script is modern enough. exit 0 ;; --run) # Back-compat with the calling convention used by older automake. shift ;; -h|--h|--he|--hel|--help) echo "\ $0 [OPTION]... PROGRAM [ARGUMENT]... Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due to PROGRAM being missing or too old. Options: -h, --help display this help and exit -v, --version output version information and exit Supported PROGRAM values: aclocal autoconf autoheader autom4te automake makeinfo bison yacc flex lex help2man Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and 'g' are ignored when checking the name. Send bug reports to ." exit $? ;; -v|--v|--ve|--ver|--vers|--versi|--versio|--version) echo "missing $scriptversion (GNU Automake)" exit $? ;; -*) echo 1>&2 "$0: unknown '$1' option" echo 1>&2 "Try '$0 --help' for more information" exit 1 ;; esac # Run the given program, remember its exit status. "$@"; st=$? # If it succeeded, we are done. test $st -eq 0 && exit 0 # Also exit now if we it failed (or wasn't found), and '--version' was # passed; such an option is passed most likely to detect whether the # program is present and works. case $2 in --version|--help) exit $st;; esac # Exit code 63 means version mismatch. This often happens when the user # tries to use an ancient version of a tool on a file that requires a # minimum version. if test $st -eq 63; then msg="probably too old" elif test $st -eq 127; then # Program was missing. msg="missing on your system" else # Program was found and executed, but failed. Give up. exit $st fi perl_URL=https://www.perl.org/ flex_URL=https://github.com/westes/flex gnu_software_URL=https://www.gnu.org/software program_details () { case $1 in aclocal|automake) echo "The '$1' program is part of the GNU Automake package:" echo "<$gnu_software_URL/automake>" echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:" echo "<$gnu_software_URL/autoconf>" echo "<$gnu_software_URL/m4/>" echo "<$perl_URL>" ;; autoconf|autom4te|autoheader) echo "The '$1' program is part of the GNU Autoconf package:" echo "<$gnu_software_URL/autoconf/>" echo "It also requires GNU m4 and Perl in order to run:" echo "<$gnu_software_URL/m4/>" echo "<$perl_URL>" ;; esac } give_advice () { # Normalize program name to check for. normalized_program=`echo "$1" | sed ' s/^gnu-//; t s/^gnu//; t s/^g//; t'` printf '%s\n' "'$1' is $msg." configure_deps="'configure.ac' or m4 files included by 'configure.ac'" case $normalized_program in autoconf*) echo "You should only need it if you modified 'configure.ac'," echo "or m4 files included by it." program_details 'autoconf' ;; autoheader*) echo "You should only need it if you modified 'acconfig.h' or" echo "$configure_deps." program_details 'autoheader' ;; automake*) echo "You should only need it if you modified 'Makefile.am' or" echo "$configure_deps." program_details 'automake' ;; aclocal*) echo "You should only need it if you modified 'acinclude.m4' or" echo "$configure_deps." program_details 'aclocal' ;; autom4te*) echo "You might have modified some maintainer files that require" echo "the 'autom4te' program to be rebuilt." program_details 'autom4te' ;; bison*|yacc*) echo "You should only need it if you modified a '.y' file." echo "You may want to install the GNU Bison package:" echo "<$gnu_software_URL/bison/>" ;; lex*|flex*) echo "You should only need it if you modified a '.l' file." echo "You may want to install the Fast Lexical Analyzer package:" echo "<$flex_URL>" ;; help2man*) echo "You should only need it if you modified a dependency" \ "of a man page." echo "You may want to install the GNU Help2man package:" echo "<$gnu_software_URL/help2man/>" ;; makeinfo*) echo "You should only need it if you modified a '.texi' file, or" echo "any other file indirectly affecting the aspect of the manual." echo "You might want to install the Texinfo package:" echo "<$gnu_software_URL/texinfo/>" echo "The spurious makeinfo call might also be the consequence of" echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might" echo "want to install GNU make:" echo "<$gnu_software_URL/make/>" ;; *) echo "You might have modified some files without having the proper" echo "tools for further handling them. Check the 'README' file, it" echo "often tells you about the needed prerequisites for installing" echo "this package. You may also peek at any GNU archive site, in" echo "case some other package contains this missing '$1' program." ;; esac } give_advice "$1" | sed -e '1s/^/WARNING: /' \ -e '2,$s/^/ /' >&2 # Propagate the correct exit status (expected to be 127 for a program # not found, 63 for a program that failed due to version mismatch). exit $st # Local variables: # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: libopenmpt-0.6.1+release.autotools/build-aux/test-driver0000755000175000017500000001112714175541547020363 00000000000000#! /bin/sh # test-driver - basic testsuite driver script. scriptversion=2018-03-07.03; # UTC # Copyright (C) 2011-2020 Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # This file is maintained in Automake, please report # bugs to or send patches to # . # Make unconditional expansion of undefined variables an error. This # helps a lot in preventing typo-related bugs. set -u usage_error () { echo "$0: $*" >&2 print_usage >&2 exit 2 } print_usage () { cat <$log_file 2>&1 estatus=$? if test $enable_hard_errors = no && test $estatus -eq 99; then tweaked_estatus=1 else tweaked_estatus=$estatus fi case $tweaked_estatus:$expect_failure in 0:yes) col=$red res=XPASS recheck=yes gcopy=yes;; 0:*) col=$grn res=PASS recheck=no gcopy=no;; 77:*) col=$blu res=SKIP recheck=no gcopy=yes;; 99:*) col=$mgn res=ERROR recheck=yes gcopy=yes;; *:yes) col=$lgn res=XFAIL recheck=no gcopy=yes;; *:*) col=$red res=FAIL recheck=yes gcopy=yes;; esac # Report the test outcome and exit status in the logs, so that one can # know whether the test passed or failed simply by looking at the '.log' # file, without the need of also peaking into the corresponding '.trs' # file (automake bug#11814). echo "$res $test_name (exit status: $estatus)" >>$log_file # Report outcome to console. echo "${col}${res}${std}: $test_name" # Register the test result, and other relevant metadata. echo ":test-result: $res" > $trs_file echo ":global-test-result: $res" >> $trs_file echo ":recheck: $recheck" >> $trs_file echo ":copy-in-global-log: $gcopy" >> $trs_file # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: libopenmpt-0.6.1+release.autotools/build/0000755000175000017500000000000014175541574015450 500000000000000libopenmpt-0.6.1+release.autotools/build/svn_version/0000755000175000017500000000000014175541574020023 500000000000000libopenmpt-0.6.1+release.autotools/build/svn_version/svn_version.h0000644000175000017500000000054712666471503022472 00000000000000 #pragma once #if defined(MPT_PACKAGE) #define OPENMPT_VERSION_IS_PACKAGE 1 #else #define OPENMPT_VERSION_IS_PACKAGE 0 #endif #if defined(MPT_SVNURL) #define OPENMPT_VERSION_URL MPT_SVNURL #endif #if defined(MPT_SVNVERSION) #define OPENMPT_VERSION_SVNVERSION MPT_SVNVERSION #endif #if defined(MPT_SVNDATE) #define OPENMPT_VERSION_DATE MPT_SVNDATE #endif libopenmpt-0.6.1+release.autotools/common/0000755000175000017500000000000014175541575015642 500000000000000libopenmpt-0.6.1+release.autotools/common/BuildSettings.h0000644000175000017500000003727114155422765020522 00000000000000/* * BuildSettings.h * --------------- * Purpose: Global, user settable compile time flags (and some global system header configuration) * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "mpt/base/detect_compiler.hpp" #include "mpt/base/detect_os.hpp" #include "mpt/base/detect_quirks.hpp" // set windows version early so that we can deduce dependencies from SDK version #if MPT_OS_WINDOWS #if !defined(WINVER) && !defined(_WIN32_WINDOWS) && !defined(_WIN32_WINNT) #define _WIN32_WINNT 0x0601 // _WIN32_WINNT_WIN7 #endif #ifndef WINVER #if defined(_WIN32_WINNT) #define WINVER _WIN32_WINNT #elif defined(_WIN32_WINDOWS) #define WINVER _WIN32_WINDOWS #endif #endif #endif // MPT_OS_WINDOWS #if defined(MODPLUG_TRACKER) && defined(LIBOPENMPT_BUILD) #error "either MODPLUG_TRACKER or LIBOPENMPT_BUILD has to be defined" #elif defined(MODPLUG_TRACKER) // nothing #define MPT_INLINE_NS mptx #elif defined(LIBOPENMPT_BUILD) // nothing #define MPT_INLINE_NS mpt_libopenmpt #else #error "either MODPLUG_TRACKER or LIBOPENMPT_BUILD has to be defined" #endif // MODPLUG_TRACKER || LIBOPENMPT_BUILD #if defined(LIBOPENMPT_BUILD) // Fixup dependencies which are currently not used in libopenmpt itself, // however might be set by some build systems like autotools anyway for simplicity. #ifdef MPT_WITH_FLAC #undef MPT_WITH_FLAC #endif #endif // LIBOPENMPT_BUILD // Dependencies from the MSVC build system #if defined(MPT_BUILD_MSVC) // This section defines which dependencies are available when building with // MSVC. Other build systems provide MPT_WITH_* macros via command-line or other // means. // OpenMPT and libopenmpt should compile and run successfully (albeit with // reduced functionality) with any or all dependencies missing/disabled. // The defaults match the bundled third-party libraries with the addition of // ASIO and VST SDKs. #if defined(MODPLUG_TRACKER) #if MPT_OS_WINDOWS #if !defined(MPT_BUILD_WINESUPPORT) && !defined(MPT_BUILD_UPDATESIGNTOOL) #define MPT_WITH_MFC #endif // !MPT_BUILD_WINESUPPORT && !MPT_BUILD_UPDATESIGNTOOL #endif // MPT_OS_WINDOWS // OpenMPT-only dependencies #define MPT_WITH_ANCIENT #if !defined(MPT_BUILD_RETRO) && !MPT_COMPILER_CLANG && !MPT_MSVC_BEFORE(2019,0) // disabled for VS2017 because of multiple initialization of inline variables // https://developercommunity.visualstudio.com/t/static-inline-variable-gets-destroyed-multiple-tim/297876 #define MPT_WITH_ASIO #endif #if defined(MPT_BUILD_RETRO) #define MPT_WITH_DIRECTSOUND #endif #define MPT_WITH_DMO #define MPT_WITH_LAME #define MPT_WITH_LHASA #define MPT_WITH_MINIZIP #define MPT_WITH_NLOHMANNJSON #define MPT_WITH_OPUS #define MPT_WITH_OPUSENC #define MPT_WITH_OPUSFILE #define MPT_WITH_PORTAUDIO //#define MPT_WITH_PULSEAUDIO //#define MPT_WITH_PULSEAUDIOSIMPLE #define MPT_WITH_RTAUDIO #define MPT_WITH_SMBPITCHSHIFT #define MPT_WITH_UNRAR #define MPT_WITH_VORBISENC #define MPT_WITH_VST // OpenMPT and libopenmpt dependencies (not for openmp123, player plugins or examples) //#define MPT_WITH_DL #define MPT_WITH_FLAC //#define MPT_WITH_LTDL #if MPT_OS_WINDOWS #if (_WIN32_WINNT >= 0x0601) #define MPT_WITH_MEDIAFOUNDATION #endif #endif //#define MPT_WITH_MINIMP3 //#define MPT_WITH_MINIZ #define MPT_WITH_MPG123 #define MPT_WITH_OGG //#define MPT_WITH_STBVORBIS #define MPT_WITH_VORBIS #define MPT_WITH_VORBISFILE #if MPT_OS_WINDOWS #if (_WIN32_WINNT >= 0x0A00) #define MPT_WITH_WINDOWS10 #endif #endif #define MPT_WITH_ZLIB #endif // MODPLUG_TRACKER #if defined(LIBOPENMPT_BUILD) // OpenMPT and libopenmpt dependencies (not for openmp123, player plugins or examples) #if defined(LIBOPENMPT_BUILD_FULL) && defined(LIBOPENMPT_BUILD_SMALL) #error "only one of LIBOPENMPT_BUILD_FULL or LIBOPENMPT_BUILD_SMALL can be defined" #endif // LIBOPENMPT_BUILD_FULL && LIBOPENMPT_BUILD_SMALL #if defined(LIBOPENMPT_BUILD_SMALL) //#define MPT_WITH_DL //#define MPT_WITH_FLAC //#define MPT_WITH_LTDL //#define MPT_WITH_MEDIAFOUNDATION #define MPT_WITH_MINIMP3 #define MPT_WITH_MINIZ //#define MPT_WITH_MPG123 //#define MPT_WITH_OGG #define MPT_WITH_STBVORBIS //#define MPT_WITH_VORBIS //#define MPT_WITH_VORBISFILE //#define MPT_WITH_ZLIB #else // !LIBOPENMPT_BUILD_SMALL //#define MPT_WITH_DL //#define MPT_WITH_FLAC //#define MPT_WITH_LTDL //#define MPT_WITH_MEDIAFOUNDATION //#define MPT_WITH_MINIMP3 //#define MPT_WITH_MINIZ #define MPT_WITH_MPG123 #define MPT_WITH_OGG //#define MPT_WITH_STBVORBIS #define MPT_WITH_VORBIS #define MPT_WITH_VORBISFILE #define MPT_WITH_ZLIB #endif // LIBOPENMPT_BUILD_SMALL #endif // LIBOPENMPT_BUILD #endif // MPT_BUILD_MSVC #if defined(MPT_BUILD_XCODE) #if defined(MODPLUG_TRACKER) // n/a #endif // MODPLUG_TRACKER #if defined(LIBOPENMPT_BUILD) //#define MPT_WITH_DL //#define MPT_WITH_FLAC //#define MPT_WITH_LTDL //#define MPT_WITH_MEDIAFOUNDATION //#define MPT_WITH_MINIMP3 //#define MPT_WITH_MINIZ #define MPT_WITH_MPG123 #define MPT_WITH_OGG //#define MPT_WITH_STBVORBIS #define MPT_WITH_VORBIS #define MPT_WITH_VORBISFILE #define MPT_WITH_ZLIB #endif // LIBOPENMPT_BUILD #endif // MPT_BUILD_XCODE #if defined(MODPLUG_TRACKER) #define MPT_UPDATE_LEGACY 1 // Enable built-in test suite. #if defined(MPT_BUILD_DEBUG) || defined(MPT_BUILD_CHECKED) #define ENABLE_TESTS #endif // Disable any file saving functionality (not really useful except for the player library) //#define MODPLUG_NO_FILESAVE // Disable any debug logging #if !defined(MPT_BUILD_DEBUG) && !defined(MPT_BUILD_CHECKED) && !defined(MPT_BUILD_WINESUPPORT) #define MPT_LOG_GLOBAL_LEVEL_STATIC #define MPT_LOG_GLOBAL_LEVEL 0 #endif // Enable all individual logging macros and MPT_LOG calls //#define MPT_ALL_LOGGING // Disable all runtime asserts #if !defined(MPT_BUILD_DEBUG) && !defined(MPT_BUILD_CHECKED) && !defined(MPT_BUILD_WINESUPPORT) #define NO_ASSERTS #endif // Enable global ComponentManager #define MPT_COMPONENT_MANAGER 1 // Support for externally linked samples e.g. in MPTM files #define MPT_EXTERNAL_SAMPLES // Support mpt::ChartsetLocale #define MPT_ENABLE_CHARSET_LOCALE // Use architecture-specific intrinsics #define MPT_ENABLE_ARCH_INTRINSICS #if !defined(MPT_BUILD_RETRO) #define MPT_ENABLE_UPDATE #endif // !MPT_BUILD_RETRO // Disable unarchiving support //#define NO_ARCHIVE_SUPPORT // Disable the built-in reverb effect //#define NO_REVERB // Disable built-in miscellaneous DSP effects (surround, mega bass, noise reduction) //#define NO_DSP // Disable the built-in equalizer. //#define NO_EQ // Disable the built-in automatic gain control //#define NO_AGC // (HACK) Define to build without any plugin support //#define NO_PLUGINS #endif // MODPLUG_TRACKER #if defined(LIBOPENMPT_BUILD) #if (defined(_DEBUG) || defined(DEBUG)) && !defined(MPT_BUILD_DEBUG) #define MPT_BUILD_DEBUG #endif #if defined(LIBOPENMPT_BUILD_TEST) #define ENABLE_TESTS #else #define MODPLUG_NO_FILESAVE #endif #if defined(MPT_BUILD_ANALZYED) || defined(MPT_BUILD_DEBUG) || defined(MPT_BUILD_CHECKED) || defined(ENABLE_TESTS) // enable asserts #else #define NO_ASSERTS #endif //#define MPT_ALL_LOGGING #define MPT_COMPONENT_MANAGER 0 //#define MPT_EXTERNAL_SAMPLES #if defined(ENABLE_TESTS) || defined(MPT_BUILD_HACK_ARCHIVE_SUPPORT) #define MPT_ENABLE_CHARSET_LOCALE #else //#define MPT_ENABLE_CHARSET_LOCALE #endif // Do not use architecture-specifid intrinsics in library builds. There is just about no codepath which would use it anyway. //#define MPT_ENABLE_ARCH_INTRINSICS #if defined(MPT_BUILD_HACK_ARCHIVE_SUPPORT) //#define NO_ARCHIVE_SUPPORT #else #define NO_ARCHIVE_SUPPORT #endif //#define NO_REVERB #define NO_DSP #define NO_EQ #define NO_AGC //#define NO_PLUGINS #endif // LIBOPENMPT_BUILD #if MPT_OS_WINDOWS #ifndef MPT_ENABLE_CHARSET_LOCALE #define MPT_ENABLE_CHARSET_LOCALE #endif #elif MPT_OS_LINUX #elif MPT_OS_ANDROID #elif MPT_OS_EMSCRIPTEN #elif MPT_OS_MACOSX_OR_IOS #elif MPT_OS_DJGPP #endif #if (MPT_COMPILER_MSVC && !defined(MPT_USTRING_MODE_UTF8_FORCE)) || defined(MODPLUG_TRACKER) // Use wide strings for MSVC because this is the native encoding on // microsoft platforms. // mpt::ToWString, mpt::wfmt, ConvertStrTo // Required by the tracker to ease interfacing with WinAPI. // Required by MPT_USTRING_MODE_WIDE to ease type tunneling in mpt::format. #define MPT_WSTRING_FORMAT 1 #else #define MPT_WSTRING_FORMAT 0 #endif #if (MPT_COMPILER_MSVC && !defined(MPT_USTRING_MODE_UTF8_FORCE)) || MPT_OS_WINDOWS || MPT_WSTRING_FORMAT // mpt::ToWide // Required on Windows by mpt::PathString. // Required by MPT_USTRING_MODE_WIDE as they share the conversion functions. // Required by MPT_WSTRING_FORMAT because of std::string<->std::wstring conversion in mpt::ToAString and mpt::ToWString. #define MPT_WSTRING_CONVERT 1 #else #define MPT_WSTRING_CONVERT 0 #endif // fixing stuff up #if defined(MPT_BUILD_ANALYZED) || defined(MPT_BUILD_CHECKED) #ifdef NO_ASSERTS #undef NO_ASSERTS // static or dynamic analyzers want assertions on #endif #endif #if defined(MPT_BUILD_FUZZER) #ifndef MPT_FUZZ_TRACKER #define MPT_FUZZ_TRACKER #endif #endif #if defined(MPT_ENABLE_ARCH_INTRINSICS) #if MPT_COMPILER_MSVC && defined(_M_IX86) #define MPT_ENABLE_ARCH_X86 #define MPT_ENABLE_ARCH_INTRINSICS_SSE #define MPT_ENABLE_ARCH_INTRINSICS_SSE2 #elif MPT_COMPILER_MSVC && defined(_M_X64) #define MPT_ENABLE_ARCH_AMD64 #define MPT_ENABLE_ARCH_INTRINSICS_SSE #define MPT_ENABLE_ARCH_INTRINSICS_SSE2 #endif // arch #endif // MPT_ENABLE_ARCH_INTRINSICS #if defined(ENABLE_TESTS) && defined(MODPLUG_NO_FILESAVE) #undef MODPLUG_NO_FILESAVE // tests recommend file saving #endif #if defined(MPT_WITH_ZLIB) && defined(MPT_WITH_MINIZ) // Only one deflate implementation should be used. Prefer zlib. #undef MPT_WITH_MINIZ #endif #if !MPT_OS_WINDOWS && defined(MPT_WITH_MEDIAFOUNDATION) #undef MPT_WITH_MEDIAFOUNDATION // MediaFoundation requires Windows #endif #if !MPT_COMPILER_MSVC && !MPT_COMPILER_CLANG && defined(MPT_WITH_MEDIAFOUNDATION) #undef MPT_WITH_MEDIAFOUNDATION // MediaFoundation requires MSVC or Clang due to ATL (no MinGW support) #endif #if (defined(MPT_WITH_MPG123) || defined(MPT_WITH_MINIMP3)) && !defined(MPT_ENABLE_MP3_SAMPLES) #define MPT_ENABLE_MP3_SAMPLES #endif #if defined(ENABLE_TESTS) #define MPT_ENABLE_FILEIO // Test suite requires PathString for file loading. #endif #if defined(MODPLUG_TRACKER) && !defined(MPT_ENABLE_FILEIO) #define MPT_ENABLE_FILEIO // Tracker requires disk file io #endif #if defined(MPT_EXTERNAL_SAMPLES) && !defined(MPT_ENABLE_FILEIO) #define MPT_ENABLE_FILEIO // External samples require disk file io #endif #if defined(NO_PLUGINS) // Any plugin type requires NO_PLUGINS to not be defined. #if defined(MPT_WITH_VST) #undef MPT_WITH_VST #endif #endif #if defined(MODPLUG_TRACKER) && !defined(MPT_BUILD_WINESUPPORT) && !defined(MPT_BUILD_WINESUPPORT_WRAPPER) #ifndef MPT_NO_NAMESPACE #define MPT_NO_NAMESPACE #endif #endif #if defined(MPT_NO_NAMESPACE) #ifdef OPENMPT_NAMESPACE #undef OPENMPT_NAMESPACE #endif #define OPENMPT_NAMESPACE #ifdef OPENMPT_NAMESPACE_BEGIN #undef OPENMPT_NAMESPACE_BEGIN #endif #define OPENMPT_NAMESPACE_BEGIN #ifdef OPENMPT_NAMESPACE_END #undef OPENMPT_NAMESPACE_END #endif #define OPENMPT_NAMESPACE_END #else #ifndef OPENMPT_NAMESPACE #define OPENMPT_NAMESPACE OpenMPT #endif #ifndef OPENMPT_NAMESPACE_BEGIN #define OPENMPT_NAMESPACE_BEGIN namespace OPENMPT_NAMESPACE { #endif #ifndef OPENMPT_NAMESPACE_END #define OPENMPT_NAMESPACE_END } #endif #endif // platform configuration #ifdef MPT_WITH_MFC //#define MPT_MFC_FULL // use full MFC, including MFC controls #define _CSTRING_DISABLE_NARROW_WIDE_CONVERSION #endif // MPT_WITH_MFC #if defined(MODPLUG_TRACKER) #if MPT_OS_WINDOWS #if !defined(MPT_BUILD_WINESUPPORT) #ifndef MPT_MFC_FULL #define _AFX_NO_MFC_CONTROLS_IN_DIALOGS // Do not include support for MFC controls in dialogs (reduces binary bloat; remove this #define if you want to use MFC controls) #endif // !MPT_MFC_FULL #endif // !MPT_BUILD_WINESUPPORT #endif // MPT_OS_WINDOWS #endif // MODPLUG_TRACKER #if MPT_OS_WINDOWS #define WIN32_LEAN_AND_MEAN // windows.h excludes #define NOMEMMGR // GMEM_*, LMEM_*, GHND, LHND, associated routines #define NOMINMAX // Macros min(a,b) and max(a,b) #define NOSERVICE // All Service Controller routines, SERVICE_ equates, etc. #define NOCOMM // COMM driver routines #define NOKANJI // Kanji support stuff. #define NOPROFILER // Profiler interface. #define NOMCX // Modem Configuration Extensions // mmsystem.h excludes #define MMNODRV //#define MMNOSOUND //#define MMNOWAVE //#define MMNOMIDI #define MMNOAUX #define MMNOMIXER //#define MMNOTIMER #define MMNOJOY #define MMNOMCI //#define MMNOMMIO //#define MMNOMMSYSTEM // mmreg.h excludes #define NOMMIDS //#define NONEWWAVE #define NONEWRIFF #define NOJPEGDIB #define NONEWIC #define NOBITMAP #endif // MPT_OS_WINDOWS // stdlib configuration #if MPT_COMPILER_CLANG #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wreserved-id-macro" #endif #define __STDC_CONSTANT_MACROS #define __STDC_FORMAT_MACROS #define __STDC_LIMIT_MACROS #define _USE_MATH_DEFINES #ifndef _FILE_OFFSET_BITS #define _FILE_OFFSET_BITS 64 #endif #if MPT_COMPILER_CLANG #pragma clang diagnostic pop #endif // compiler configuration #if MPT_COMPILER_MSVC #define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers #pragma warning(default:4800) // Implicit conversion from 'int' to bool. Possible information loss #pragma warning(disable:4355) // 'this' : used in base member initializer list // happens for immutable classes (i.e. classes containing const members) #pragma warning(disable:4512) // assignment operator could not be generated #pragma warning(error:4309) // Treat "truncation of constant value"-warning as error. #pragma warning(error:4463) // Treat overflow; assigning value to bit-field that can only hold values from low_value to high_value"-warning as error. #ifdef MPT_BUILD_ANALYZED // Disable Visual Studio static analyzer warnings that generate too many false positives in VS2010. //#pragma warning(disable:6246) //#pragma warning(disable:6262) #pragma warning(disable:6297) // 32-bit value is shifted, then cast to 64-bit value. Results might not be an expected value. #pragma warning(disable:6326) // Potential comparison of a constant with another constant //#pragma warning(disable:6385) //#pragma warning(disable:6386) #endif // MPT_BUILD_ANALYZED #endif // MPT_COMPILER_MSVC #if MPT_COMPILER_CLANG #if defined(MPT_BUILD_MSVC) #pragma clang diagnostic warning "-Wimplicit-fallthrough" #endif // MPT_BUILD_MSVC #if defined(MODPLUG_TRACKER) #pragma clang diagnostic ignored "-Wunused-local-typedef" #endif // MODPLUG_TRACKER #endif // MPT_COMPILER_CLANG // standard library quirks // third-party library configuration #if MPT_OS_WINDOWS #ifndef UNICODE #define MPT_CHECK_WINDOWS_IGNORE_WARNING_NO_UNICODE #endif // !UNICODE #endif // MPT_OS_WINDOWS #ifdef MPT_WITH_ANCIENT #ifdef MPT_BUILD_MSVC_SHARED #define ANCIENT_API_DECLSPEC_DLLIMPORT #endif #endif #ifdef MPT_WITH_FLAC #ifdef MPT_BUILD_MSVC_STATIC #define FLAC__NO_DLL #endif #endif #ifdef MPT_WITH_SMBPITCHSHIFT #ifdef MPT_BUILD_MSVC_SHARED #define SMBPITCHSHIFT_USE_DLL #endif #endif #ifdef MPT_WITH_STBVORBIS #define STB_VORBIS_HEADER_ONLY #ifndef STB_VORBIS_NO_PULLDATA_API #define STB_VORBIS_NO_PULLDATA_API #endif #ifndef STB_VORBIS_NO_STDIO #define STB_VORBIS_NO_STDIO #endif #endif #ifdef MPT_WITH_VORBISFILE #ifndef OV_EXCLUDE_STATIC_CALLBACKS #define OV_EXCLUDE_STATIC_CALLBACKS #endif #endif #ifdef MPT_WITH_ZLIB #ifdef MPT_BUILD_MSVC_SHARED #define ZLIB_DLL #endif #endif #ifdef __cplusplus #include "mpt/base/namespace.hpp" OPENMPT_NAMESPACE_BEGIN namespace mpt { #ifndef MPT_NO_NAMESPACE using namespace ::mpt; #endif } // namespace mpt OPENMPT_NAMESPACE_END #endif libopenmpt-0.6.1+release.autotools/common/ComponentManager.cpp0000644000175000017500000002302614070073076021514 00000000000000/* * ComponentManager.cpp * -------------------- * Purpose: Manages loading of optional components. * Notes : (currently none) * Authors: Joern Heusipp * OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "ComponentManager.h" #include "mpt/mutex/mutex.hpp" #include "Logging.h" OPENMPT_NAMESPACE_BEGIN ComponentBase::ComponentBase(ComponentType type) : m_Type(type) , m_Initialized(false) , m_Available(false) { return; } ComponentBase::~ComponentBase() { return; } void ComponentBase::SetInitialized() { m_Initialized = true; } void ComponentBase::SetAvailable() { m_Available = true; } ComponentType ComponentBase::GetType() const { return m_Type; } bool ComponentBase::IsInitialized() const { return m_Initialized; } bool ComponentBase::IsAvailable() const { return m_Initialized && m_Available; } mpt::ustring ComponentBase::GetVersion() const { return mpt::ustring(); } void ComponentBase::Initialize() { if(IsInitialized()) { return; } if(DoInitialize()) { SetAvailable(); } SetInitialized(); } #if defined(MODPLUG_TRACKER) ComponentLibrary::ComponentLibrary(ComponentType type) : ComponentBase(type) , m_BindFailed(false) { return; } ComponentLibrary::~ComponentLibrary() { return; } bool ComponentLibrary::AddLibrary(const std::string &libName, const mpt::LibraryPath &libPath) { if(m_Libraries[libName].IsValid()) { // prefer previous return true; } mpt::Library lib(libPath); if(!lib.IsValid()) { return false; } m_Libraries[libName] = lib; return true; } void ComponentLibrary::ClearLibraries() { m_Libraries.clear(); } void ComponentLibrary::SetBindFailed() { m_BindFailed = true; } void ComponentLibrary::ClearBindFailed() { m_BindFailed = false; } bool ComponentLibrary::HasBindFailed() const { return m_BindFailed; } mpt::Library ComponentLibrary::GetLibrary(const std::string &libName) const { const auto it = m_Libraries.find(libName); if(it == m_Libraries.end()) { return mpt::Library(); } return it->second; } #endif // MODPLUG_TRACKER #if MPT_COMPONENT_MANAGER ComponentFactoryBase::ComponentFactoryBase(const std::string &id, const std::string &settingsKey) : m_ID(id) , m_SettingsKey(settingsKey) { return; } ComponentFactoryBase::~ComponentFactoryBase() { return; } std::string ComponentFactoryBase::GetID() const { return m_ID; } std::string ComponentFactoryBase::GetSettingsKey() const { return m_SettingsKey; } void ComponentFactoryBase::PreConstruct() const { MPT_LOG_GLOBAL(LogInformation, "Components", MPT_UFORMAT("Constructing Component {}") ( mpt::ToUnicode(mpt::Charset::ASCII, m_ID) ) ); } void ComponentFactoryBase::Initialize(ComponentManager &componentManager, std::shared_ptr component) const { if(componentManager.IsComponentBlocked(GetSettingsKey())) { return; } componentManager.InitializeComponent(component); } // Global list of component register functions. // We do not use a global scope static list head because the corresponding // mutex would be no POD type and would thus not be safe to be usable in // zero-initialized state. // Function scope static initialization is guaranteed to be thread safe // in C++11. // We use this implementation to be future-proof. // MSVC currently does not exploit the possibility of using multiple threads // for global lifetime object's initialization. // An implementation with a simple global list head and no mutex at all would // thus work fine for MSVC (currently). static mpt::mutex & ComponentListMutex() { static mpt::mutex g_ComponentListMutex; return g_ComponentListMutex; } static ComponentListEntry * & ComponentListHead() { static ComponentListEntry g_ComponentListHeadEmpty = {nullptr, nullptr}; static ComponentListEntry *g_ComponentListHead = &g_ComponentListHeadEmpty; return g_ComponentListHead; } bool ComponentListPush(ComponentListEntry *entry) { mpt::lock_guard guard(ComponentListMutex()); #if MPT_MSVC_BEFORE(2019,0) // Guard against VS2017 compiler bug causing repeated initialization of inline variables. // See . if(entry->next) { return false; } #endif entry->next = ComponentListHead(); ComponentListHead() = entry; return true; } static std::shared_ptr g_ComponentManager; void ComponentManager::Init(const IComponentManagerSettings &settings) { MPT_LOG_GLOBAL(LogInformation, "Components", U_("Init")); // cannot use make_shared because the constructor is private g_ComponentManager = std::shared_ptr(new ComponentManager(settings)); } void ComponentManager::Release() { MPT_LOG_GLOBAL(LogInformation, "Components", U_("Release")); g_ComponentManager = nullptr; } std::shared_ptr ComponentManager::Instance() { return g_ComponentManager; } ComponentManager::ComponentManager(const IComponentManagerSettings &settings) : m_Settings(settings) { mpt::lock_guard guard(ComponentListMutex()); for(ComponentListEntry *entry = ComponentListHead(); entry; entry = entry->next) { if(entry->reg) { entry->reg(*this); } } } void ComponentManager::Register(const IComponentFactory &componentFactory) { if(m_Components.find(componentFactory.GetID()) != m_Components.end()) { return; } RegisteredComponent registeredComponent; registeredComponent.settingsKey = componentFactory.GetSettingsKey(); registeredComponent.factoryMethod = componentFactory.GetStaticConstructor(); registeredComponent.instance = nullptr; registeredComponent.weakInstance = std::weak_ptr(); m_Components.insert(std::make_pair(componentFactory.GetID(), registeredComponent)); } void ComponentManager::Startup() { MPT_LOG_GLOBAL(LogDebug, "Components", U_("Startup")); if(m_Settings.LoadOnStartup()) { for(auto &it : m_Components) { it.second.instance = it.second.factoryMethod(*this); it.second.weakInstance = it.second.instance; } } if(!m_Settings.KeepLoaded()) { for(auto &it : m_Components) { it.second.instance = nullptr; } } } bool ComponentManager::IsComponentBlocked(const std::string &settingsKey) const { if(settingsKey.empty()) { return false; } return m_Settings.IsBlocked(settingsKey); } void ComponentManager::InitializeComponent(std::shared_ptr component) const { if(!component) { return; } if(component->IsInitialized()) { return; } component->Initialize(); } std::shared_ptr ComponentManager::GetComponent(const IComponentFactory &componentFactory) { std::shared_ptr component = nullptr; auto it = m_Components.find(componentFactory.GetID()); if(it != m_Components.end()) { // registered component if((*it).second.instance) { // loaded component = (*it).second.instance; } else { // not loaded component = (*it).second.weakInstance.lock(); if(!component) { component = (*it).second.factoryMethod(*this); } if(m_Settings.KeepLoaded()) { // keep the component loaded (*it).second.instance = component; } (*it).second.weakInstance = component; } } else { // unregistered component component = componentFactory.Construct(*this); } MPT_ASSERT(component); return component; } std::shared_ptr ComponentManager::ReloadComponent(const IComponentFactory &componentFactory) { std::shared_ptr component = nullptr; auto it = m_Components.find(componentFactory.GetID()); if(it != m_Components.end()) { // registered component if((*it).second.instance) { // loaded (*it).second.instance = nullptr; if(!(*it).second.weakInstance.expired()) { throw std::runtime_error("Component not completely unloaded. Cannot reload."); } (*it).second.weakInstance = std::weak_ptr(); } // not loaded component = (*it).second.factoryMethod(*this); if(m_Settings.KeepLoaded()) { // keep the component loaded (*it).second.instance = component; } (*it).second.weakInstance = component; } else { // unregistered component component = componentFactory.Construct(*this); } MPT_ASSERT(component); return component; } std::vector ComponentManager::GetRegisteredComponents() const { std::vector result; result.reserve(m_Components.size()); for(const auto &it : m_Components) { result.push_back(it.first); } return result; } ComponentInfo ComponentManager::GetComponentInfo(std::string name) const { ComponentInfo result; result.name = name; result.state = ComponentStateUnregistered; result.settingsKey = ""; result.type = ComponentTypeUnknown; const auto it = m_Components.find(name); if(it == m_Components.end()) { result.state = ComponentStateUnregistered; return result; } result.settingsKey = it->second.settingsKey; if(IsComponentBlocked(it->second.settingsKey)) { result.state = ComponentStateBlocked; return result; } std::shared_ptr component = it->second.instance; if(!component) { component = it->second.weakInstance.lock(); } if(!component) { result.state = ComponentStateUnintialized; return result; } result.type = component->GetType(); if(!component->IsInitialized()) { result.state = ComponentStateUnintialized; return result; } if(!component->IsAvailable()) { result.state = ComponentStateUnavailable; return result; } result.state = ComponentStateAvailable; return result; } mpt::PathString ComponentManager::GetComponentPath() const { return m_Settings.Path(); } #endif // MPT_COMPONENT_MANAGER OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/common/ComponentManager.h0000644000175000017500000002632214052666041021163 00000000000000/* * ComponentManager.h * ------------------ * Purpose: Manages loading of optional components. * Notes : (currently none) * Authors: Joern Heusipp * OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "mpt/mutex/mutex.hpp" #include #include #include "../common/misc_util.h" #if defined(MODPLUG_TRACKER) #include "../misc/mptLibrary.h" #endif OPENMPT_NAMESPACE_BEGIN enum ComponentType { ComponentTypeUnknown = 0, ComponentTypeBuiltin, // PortAudio ComponentTypeSystem, // mf.dll ComponentTypeSystemInstallable, // acm mp3 codec ComponentTypeBundled, // libsoundtouch ComponentTypeForeign, // libmp3lame }; class ComponentFactoryBase; class IComponent { friend class ComponentFactoryBase; protected: IComponent() = default; public: virtual ~IComponent() = default; public: virtual ComponentType GetType() const = 0; virtual bool IsInitialized() const = 0; // Initialize() has been called virtual bool IsAvailable() const = 0; // Initialize() has been successfull virtual mpt::ustring GetVersion() const = 0; virtual void Initialize() = 0; // try to load the component }; class ComponentBase : public IComponent { private: ComponentType m_Type; bool m_Initialized; bool m_Available; protected: ComponentBase(ComponentType type); public: ~ComponentBase() override; protected: void SetInitialized(); void SetAvailable(); public: ComponentType GetType() const override; bool IsInitialized() const override; bool IsAvailable() const override; mpt::ustring GetVersion() const override; public: void Initialize() override; protected: virtual bool DoInitialize() = 0; }; class ComponentBuiltin : public ComponentBase { public: ComponentBuiltin() : ComponentBase(ComponentTypeBuiltin) { return; } bool DoInitialize() override { return true; } }; #define MPT_GLOBAL_BIND(lib, name) name = &::name; #if defined(MODPLUG_TRACKER) class ComponentLibrary : public ComponentBase { private: typedef std::map TLibraryMap; TLibraryMap m_Libraries; bool m_BindFailed; protected: ComponentLibrary(ComponentType type); public: virtual ~ComponentLibrary(); protected: bool AddLibrary(const std::string &libName, const mpt::LibraryPath &libPath); void ClearLibraries(); void SetBindFailed(); void ClearBindFailed(); bool HasBindFailed() const; public: virtual mpt::Library GetLibrary(const std::string &libName) const; template bool Bind(Tfunc * & f, const std::string &libName, const std::string &symbol) const { return GetLibrary(libName).Bind(f, symbol); } protected: bool DoInitialize() override = 0; }; #define MPT_COMPONENT_BIND(libName, func) do { if(!Bind( func , libName , #func )) { SetBindFailed(); } } while(0) #define MPT_COMPONENT_BIND_OPTIONAL(libName, func) Bind( func , libName , #func ) #define MPT_COMPONENT_BIND_SYMBOL(libName, symbol, func) do { if(!Bind( func , libName , symbol )) { SetBindFailed(); } } while(0) #define MPT_COMPONENT_BIND_SYMBOL_OPTIONAL(libName, symbol, func) Bind( func , libName , symbol ) #if MPT_OS_WINDOWS #ifdef UNICODE #define MPT_COMPONENT_BINDWIN_SUFFIX "W" #else #define MPT_COMPONENT_BINDWIN_SUFFIX "A" #endif #define MPT_COMPONENT_BINDWIN(libName, func) do { if(!Bind( func , libName , #func MPT_COMPONENT_BINDWIN_SUFFIX )) { SetBindFailed(); } } while(0) #define MPT_COMPONENT_BINDWIN_OPTIONAL(libName, func) Bind( func , libName , #func MPT_COMPONENT_BINDWIN_SUFFIX ) #define MPT_COMPONENT_BINDWIN_SYMBOL(libName, symbol, func) do { if(!Bind( func , libName , symbol MPT_COMPONENT_BINDWIN_SUFFIX )) { SetBindFailed(); } } while(0) #define MPT_COMPONENT_BINDWIN_SYMBOL_OPTIONAL(libName, symbol, func) Bind( func , libName , symbol MPT_COMPONENT_BINDWIN_SUFFIX ) #endif class ComponentSystemDLL : public ComponentLibrary { private: mpt::PathString m_BaseName; public: ComponentSystemDLL(const mpt::PathString &baseName) : ComponentLibrary(ComponentTypeSystem) , m_BaseName(baseName) { return; } bool DoInitialize() override { AddLibrary(m_BaseName.ToUTF8(), mpt::LibraryPath::System(m_BaseName)); return GetLibrary(m_BaseName.ToUTF8()).IsValid(); } }; class ComponentBundledDLL : public ComponentLibrary { private: mpt::PathString m_FullName; public: ComponentBundledDLL(const mpt::PathString &fullName) : ComponentLibrary(ComponentTypeBundled) , m_FullName(fullName) { return; } bool DoInitialize() override { AddLibrary(m_FullName.ToUTF8(), mpt::LibraryPath::AppFullName(m_FullName)); return GetLibrary(m_FullName.ToUTF8()).IsValid(); } }; #endif // MODPLUG_TRACKER #if MPT_COMPONENT_MANAGER class ComponentManager; typedef std::shared_ptr (*ComponentFactoryMethod)(ComponentManager &componentManager); class IComponentFactory { protected: IComponentFactory() = default; public: virtual ~IComponentFactory() = default; public: virtual std::string GetID() const = 0; virtual std::string GetSettingsKey() const = 0; virtual std::shared_ptr Construct(ComponentManager &componentManager) const = 0; virtual ComponentFactoryMethod GetStaticConstructor() const = 0; }; class ComponentFactoryBase : public IComponentFactory { private: std::string m_ID; std::string m_SettingsKey; protected: ComponentFactoryBase(const std::string &id, const std::string &settingsKey); void PreConstruct() const; void Initialize(ComponentManager &componentManager, std::shared_ptr component) const; public: virtual ~ComponentFactoryBase(); std::string GetID() const override; std::string GetSettingsKey() const override; std::shared_ptr Construct(ComponentManager &componentManager) const override = 0; ComponentFactoryMethod GetStaticConstructor() const override = 0; }; template class ComponentFactory : public ComponentFactoryBase { public: ComponentFactory() : ComponentFactoryBase(T::g_ID, T::g_SettingsKey) { return; } public: std::shared_ptr Construct(ComponentManager &componentManager) const override { PreConstruct(); std::shared_ptr component = std::make_shared(); Initialize(componentManager, component); return component; } static std::shared_ptr StaticConstruct(ComponentManager &componentManager) { return ComponentFactory().Construct(componentManager); } virtual ComponentFactoryMethod GetStaticConstructor() const override { return &StaticConstruct; } }; class IComponentManagerSettings { public: virtual bool LoadOnStartup() const = 0; virtual bool KeepLoaded() const = 0; virtual bool IsBlocked(const std::string &key) const = 0; virtual mpt::PathString Path() const = 0; protected: virtual ~IComponentManagerSettings() = default; }; class ComponentManagerSettingsDefault : public IComponentManagerSettings { public: bool LoadOnStartup() const override { return false; } bool KeepLoaded() const override { return true; } bool IsBlocked(const std::string & /*key*/ ) const override { return false; } mpt::PathString Path() const override { return mpt::PathString(); } }; enum ComponentState { ComponentStateUnregistered, ComponentStateBlocked, ComponentStateUnintialized, ComponentStateUnavailable, ComponentStateAvailable, }; struct ComponentInfo { std::string name; ComponentState state; std::string settingsKey; ComponentType type; }; class ComponentManager { friend class ComponentFactoryBase; public: static void Init(const IComponentManagerSettings &settings); static void Release(); static std::shared_ptr Instance(); private: ComponentManager(const IComponentManagerSettings &settings); private: struct RegisteredComponent { std::string settingsKey; ComponentFactoryMethod factoryMethod; std::shared_ptr instance; std::weak_ptr weakInstance; }; typedef std::map TComponentMap; const IComponentManagerSettings &m_Settings; TComponentMap m_Components; private: bool IsComponentBlocked(const std::string &settingsKey) const; void InitializeComponent(std::shared_ptr component) const; public: void Register(const IComponentFactory &componentFactory); void Startup(); std::shared_ptr GetComponent(const IComponentFactory &componentFactory); std::shared_ptr ReloadComponent(const IComponentFactory &componentFactory); std::vector GetRegisteredComponents() const; ComponentInfo GetComponentInfo(std::string name) const; mpt::PathString GetComponentPath() const; }; struct ComponentListEntry { ComponentListEntry *next; void (*reg)(ComponentManager &componentManager); }; bool ComponentListPush(ComponentListEntry *entry); template struct ComponentRegisterer { static inline void RegisterComponent(ComponentManager &componentManager) { componentManager.Register(ComponentFactory()); } static inline ComponentListEntry &GetComponentListEntry() { static ComponentListEntry s_ComponentListEntry = {nullptr, &RegisterComponent}; return s_ComponentListEntry; } static inline bool g_ComponentRegistered = ComponentListPush(&GetComponentListEntry()); }; #define MPT_DECLARE_COMPONENT_MEMBERS(name, settingsKey) \ public: \ static constexpr const char *g_ID = #name ; \ static constexpr const char *g_SettingsKey = settingsKey ; \ static inline ComponentRegisterer< name > s_ComponentRegisterer; \ /**/ template std::shared_ptr GetComponent() { return std::dynamic_pointer_cast(ComponentManager::Instance()->GetComponent(ComponentFactory())); } template std::shared_ptr ReloadComponent() { return std::dynamic_pointer_cast(ComponentManager::Instance()->ReloadComponent(ComponentFactory())); } inline mpt::PathString GetComponentPath() { return ComponentManager::Instance()->GetComponentPath(); } #else // !MPT_COMPONENT_MANAGER #define MPT_DECLARE_COMPONENT_MEMBERS(name, settingsKey) template std::shared_ptr GetComponent() { static std::weak_ptr cache; static mpt::mutex m; mpt::lock_guard l(m); std::shared_ptr component = cache.lock(); if(!component) { component = std::make_shared(); component->Initialize(); cache = component; } return component; } #endif // MPT_COMPONENT_MANAGER // Simple wrapper around std::shared_ptr which automatically // gets a reference to the component (or constructs it) on initialization. template class ComponentHandle { private: std::shared_ptr component; public: ComponentHandle() : component(GetComponent()) { return; } ~ComponentHandle() { return; } bool IsAvailable() const { return component && component->IsAvailable(); } const T *get() const { return component.get(); } const T &operator*() const { return *component; } const T *operator->() const { return &*component; } #if MPT_COMPONENT_MANAGER void Reload() { component = nullptr; component = ReloadComponent(); } #endif }; template bool IsComponentAvailable(const ComponentHandle &handle) { return handle.IsAvailable(); } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/common/Dither.h0000644000175000017500000000355714053107667017157 00000000000000/* * Dither.h * -------- * Purpose: Dithering when converting to lower resolution sample formats. * Notes : (currently none) * Authors: Olivier Lapicque * OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "mpt/base/macros.hpp" #include "mpt/string/types.hpp" #include "openmpt/soundbase/Dither.hpp" #include "openmpt/soundbase/DitherModPlug.hpp" #include "openmpt/soundbase/DitherNone.hpp" #include "openmpt/soundbase/DitherSimple.hpp" #include "mptRandom.h" #include #include #include OPENMPT_NAMESPACE_BEGIN using Dither_Default = Dither_Simple; class DitherNamesOpenMPT { public: static mpt::ustring GetModeName(std::size_t mode) { mpt::ustring result; switch(mode) { case 0: // no dither result = MPT_USTRING("no"); break; case 1: // chosen by OpenMPT code, might change result = MPT_USTRING("default"); break; case 2: // rectangular, 0.5 bit depth, no noise shaping (original ModPlug Tracker) result = MPT_USTRING("0.5 bit"); break; case 3: // rectangular, 1 bit depth, simple 1st order noise shaping result = MPT_USTRING("1 bit"); break; default: result = MPT_USTRING(""); break; } return result; } }; using DithersOpenMPT = Dithers, MultiChannelDither, MultiChannelDither, MultiChannelDither>, DitherNamesOpenMPT, 4, 1, 0, mpt::good_prng>; struct DithersWrapperOpenMPT : DithersOpenMPT { template DithersWrapperOpenMPT(Trd &rd, std::size_t mode = DithersOpenMPT::DefaultDither, std::size_t channels = DithersOpenMPT::DefaultChannels) : DithersOpenMPT(rd, mode, channels) { return; } }; OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/common/FileReader.h0000644000175000017500000003500414072320411017713 00000000000000/* * FileReader.h * ------------ * Purpose: A basic class for transparent reading of memory-based files. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "mpt/io_read/filecursor.hpp" #include "mpt/io_read/filecursor_filename_traits.hpp" #include "mpt/io_read/filecursor_traits_filedata.hpp" #include "mpt/io_read/filecursor_traits_memory.hpp" #include "mpt/io_read/filereader.hpp" #include "openmpt/base/Types.hpp" #include "mptPathString.h" #include "mptStringBuffer.h" #include #include #include #include #include #include #include #include "FileReaderFwd.h" OPENMPT_NAMESPACE_BEGIN namespace FileReaderExt { // Read a string of length srcSize into fixed-length char array destBuffer using a given read mode. // The file cursor is advanced by "srcSize" bytes. // Returns true if at least one byte could be read or 0 bytes were requested. template bool ReadString(TFileCursor &f, char (&destBuffer)[destSize], const typename TFileCursor::pos_type srcSize) { typename TFileCursor::PinnedView source = f.ReadPinnedView(srcSize); // Make sure the string is cached properly. typename TFileCursor::pos_type realSrcSize = source.size(); // In case fewer bytes are available mpt::String::WriteAutoBuf(destBuffer) = mpt::String::ReadBuf(mode, mpt::byte_cast(source.data()), realSrcSize); return (realSrcSize > 0 || srcSize == 0); } // Read a string of length srcSize into a std::string dest using a given read mode. // The file cursor is advanced by "srcSize" bytes. // Returns true if at least one character could be read or 0 characters were requested. template bool ReadString(TFileCursor &f, std::string &dest, const typename TFileCursor::pos_type srcSize) { dest.clear(); typename TFileCursor::PinnedView source = f.ReadPinnedView(srcSize); // Make sure the string is cached properly. typename TFileCursor::pos_type realSrcSize = source.size(); // In case fewer bytes are available dest = mpt::String::ReadBuf(mode, mpt::byte_cast(source.data()), realSrcSize); return (realSrcSize > 0 || srcSize == 0); } // Read a string of length srcSize into a mpt::charbuf dest using a given read mode. // The file cursor is advanced by "srcSize" bytes. // Returns true if at least one character could be read or 0 characters were requested. template bool ReadString(TFileCursor &f, mpt::charbuf &dest, const typename TFileCursor::pos_type srcSize) { typename TFileCursor::PinnedView source = f.ReadPinnedView(srcSize); // Make sure the string is cached properly. typename TFileCursor::pos_type realSrcSize = source.size(); // In case fewer bytes are available dest = mpt::String::ReadBuf(mode, mpt::byte_cast(source.data()), realSrcSize); return (realSrcSize > 0 || srcSize == 0); } // Read a charset encoded string of length srcSize into a mpt::ustring dest using a given read mode. // The file cursor is advanced by "srcSize" bytes. // Returns true if at least one character could be read or 0 characters were requested. template bool ReadString(TFileCursor &f, mpt::ustring &dest, mpt::Charset charset, const typename TFileCursor::pos_type srcSize) { dest.clear(); typename TFileCursor::PinnedView source = f.ReadPinnedView(srcSize); // Make sure the string is cached properly. typename TFileCursor::pos_type realSrcSize = source.size(); // In case fewer bytes are available dest = mpt::ToUnicode(charset, mpt::String::ReadBuf(mode, mpt::byte_cast(source.data()), realSrcSize)); return (realSrcSize > 0 || srcSize == 0); } // Read a string with a preprended length field of type Tsize (must be a packed<*,*> type) into a std::string dest using a given read mode. // The file cursor is advanced by the string length. // Returns true if the size field could be read and at least one character could be read or 0 characters were requested. template bool ReadSizedString(TFileCursor &f, char (&destBuffer)[destSize], const typename TFileCursor::pos_type maxLength = std::numeric_limits::max()) { mpt::packed srcSize; // Enforce usage of a packed type by ensuring that the passed type has the required typedefs if(!mpt::IO::FileReader::Read(f, srcSize)) { return false; } return FileReaderExt::ReadString(f, destBuffer, std::min(static_cast(srcSize), maxLength)); } // Read a string with a preprended length field of type Tsize (must be a packed<*,*> type) into a std::string dest using a given read mode. // The file cursor is advanced by the string length. // Returns true if the size field could be read and at least one character could be read or 0 characters were requested. template bool ReadSizedString(TFileCursor &f, std::string &dest, const typename TFileCursor::pos_type maxLength = std::numeric_limits::max()) { mpt::packed srcSize; // Enforce usage of a packed type by ensuring that the passed type has the required typedefs if(!mpt::IO::FileReader::Read(f, srcSize)) { return false; } return FileReaderExt::ReadString(f, dest, std::min(static_cast(srcSize), maxLength)); } // Read a string with a preprended length field of type Tsize (must be a packed<*,*> type) into a mpt::charbuf dest using a given read mode. // The file cursor is advanced by the string length. // Returns true if the size field could be read and at least one character could be read or 0 characters were requested. template bool ReadSizedString(TFileCursor &f, mpt::charbuf &dest, const typename TFileCursor::pos_type maxLength = std::numeric_limits::max()) { mpt::packed srcSize; // Enforce usage of a packed type by ensuring that the passed type has the required typedefs if(!mpt::IO::FileReader::Read(f, srcSize)) { return false; } return FileReaderExt::ReadString(f, dest, std::min(static_cast(srcSize), maxLength)); } } // namespace FileReaderExt namespace detail { template using FileCursor = mpt::IO::FileCursor; template class FileReader : public FileCursor { private: using traits_type = Ttraits; using filename_traits_type = Tfilenametraits; public: using pos_type = typename traits_type::pos_type; using off_t = pos_type; using data_type = typename traits_type::data_type; using ref_data_type = typename traits_type::ref_data_type; using shared_data_type = typename traits_type::shared_data_type; using value_data_type = typename traits_type::value_data_type; using shared_filename_type = typename filename_traits_type::shared_filename_type; public: // Initialize invalid file reader object. FileReader() { return; } FileReader(const FileCursor &other) : FileCursor(other) { return; } FileReader(FileCursor &&other) : FileCursor(std::move(other)) { return; } // Initialize file reader object with pointer to data and data length. template explicit FileReader(mpt::span bytedata, shared_filename_type filename = shared_filename_type{}) : FileCursor(bytedata, std::move(filename)) { return; } // Initialize file reader object based on an existing file reader object window. explicit FileReader(value_data_type other, shared_filename_type filename = shared_filename_type{}) : FileCursor(std::move(other), std::move(filename)) { return; } public: template bool Read(T &target) { return mpt::IO::FileReader::Read(*this, target); } template T ReadIntLE() { return mpt::IO::FileReader::ReadIntLE(*this); } template T ReadIntBE() { return mpt::IO::FileReader::ReadIntLE(*this); } template T ReadTruncatedIntLE(pos_type size) { return mpt::IO::FileReader::ReadTruncatedIntLE(*this, size); } template T ReadSizedIntLE(pos_type size) { return mpt::IO::FileReader::ReadSizedIntLE(*this, size); } uint32 ReadUint32LE() { return mpt::IO::FileReader::ReadUint32LE(*this); } uint32 ReadUint32BE() { return mpt::IO::FileReader::ReadUint32BE(*this); } int32 ReadInt32LE() { return mpt::IO::FileReader::ReadInt32LE(*this); } int32 ReadInt32BE() { return mpt::IO::FileReader::ReadInt32BE(*this); } uint32 ReadUint24LE() { return mpt::IO::FileReader::ReadUint24LE(*this); } uint32 ReadUint24BE() { return mpt::IO::FileReader::ReadUint24BE(*this); } uint16 ReadUint16LE() { return mpt::IO::FileReader::ReadUint16LE(*this); } uint16 ReadUint16BE() { return mpt::IO::FileReader::ReadUint16BE(*this); } int16 ReadInt16LE() { return mpt::IO::FileReader::ReadInt16LE(*this); } int16 ReadInt16BE() { return mpt::IO::FileReader::ReadInt16BE(*this); } char ReadChar() { return mpt::IO::FileReader::ReadChar(*this); } uint8 ReadUint8() { return mpt::IO::FileReader::ReadUint8(*this); } int8 ReadInt8() { return mpt::IO::FileReader::ReadInt8(*this); } float ReadFloatLE() { return mpt::IO::FileReader::ReadFloatLE(*this); } float ReadFloatBE() { return mpt::IO::FileReader::ReadFloatBE(*this); } double ReadDoubleLE() { return mpt::IO::FileReader::ReadDoubleLE(*this); } double ReadDoubleBE() { return mpt::IO::FileReader::ReadDoubleBE(*this); } template bool ReadStruct(T &target) { return mpt::IO::FileReader::ReadStruct(*this, target); } template size_t ReadStructPartial(T &target, size_t partialSize = sizeof(T)) { return mpt::IO::FileReader::ReadStructPartial(*this, target, partialSize); } bool ReadNullString(std::string &dest, const pos_type maxLength = std::numeric_limits::max()) { return mpt::IO::FileReader::ReadNullString(*this, dest, maxLength); } bool ReadLine(std::string &dest, const pos_type maxLength = std::numeric_limits::max()) { return mpt::IO::FileReader::ReadLine(*this, dest, maxLength); } template bool ReadArray(T (&destArray)[destSize]) { return mpt::IO::FileReader::ReadArray(*this, destArray); } template bool ReadArray(std::array &destArray) { return mpt::IO::FileReader::ReadArray(*this, destArray); } template std::array ReadArray() { return mpt::IO::FileReader::ReadArray(*this); } template bool ReadVector(std::vector &destVector, size_t destSize) { return mpt::IO::FileReader::ReadVector(*this, destVector, destSize); } template bool ReadMagic(const char (&magic)[N]) { return mpt::IO::FileReader::ReadMagic(*this, magic); } template bool ReadVarInt(T &target) { return mpt::IO::FileReader::ReadVarInt(*this, target); } template using Item = mpt::IO::FileReader::Chunk; template using ChunkList = mpt::IO::FileReader::ChunkList; template Item ReadNextChunk(off_t alignment) { return mpt::IO::FileReader::ReadNextChunk(*this, alignment); } template ChunkList ReadChunks(off_t alignment) { return mpt::IO::FileReader::ReadChunks(*this, alignment); } template ChunkList ReadChunksUntil(off_t alignment, decltype(T().GetID()) stopAtID) { return mpt::IO::FileReader::ReadChunksUntil(*this, alignment, stopAtID); } template bool ReadString(char (&destBuffer)[destSize], const pos_type srcSize) { return FileReaderExt::ReadString(*this, destBuffer, srcSize); } template bool ReadString(std::string &dest, const pos_type srcSize) { return FileReaderExt::ReadString(*this, dest, srcSize); } template bool ReadString(mpt::charbuf &dest, const pos_type srcSize) { return FileReaderExt::ReadString(*this, dest, srcSize); } template bool ReadString(mpt::ustring &dest, mpt::Charset charset, const pos_type srcSize) { return FileReaderExt::ReadString(*this, dest, charset, srcSize); } template bool ReadSizedString(char (&destBuffer)[destSize], const pos_type maxLength = std::numeric_limits::max()) { return FileReaderExt::ReadSizedString(*this, destBuffer, maxLength); } template bool ReadSizedString(std::string &dest, const pos_type maxLength = std::numeric_limits::max()) { return FileReaderExt::ReadSizedString(*this, dest, maxLength); } template bool ReadSizedString(mpt::charbuf &dest, const pos_type maxLength = std::numeric_limits::max()) { return FileReaderExt::ReadSizedString(*this, dest, maxLength); } }; } // namespace detail using FileCursor = detail::FileCursor>; using FileReader = detail::FileReader>; using ChunkReader = FileReader; using MemoryFileCursor = detail::FileCursor; using MemoryFileReader = detail::FileReader; OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/common/FileReaderFwd.h0000644000175000017500000000274514056206101020363 00000000000000/* * FileReaderFwd.h * --------------- * Purpose: Forward declaration for class FileReader. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "mpt/base/namespace.hpp" namespace mpt { inline namespace MPT_INLINE_NS { namespace IO { class FileCursorTraitsMemory; class FileCursorTraitsFileData; class FileCursorFilenameTraitsNone; template class FileCursorFilenameTraits; template class FileCursor; } } } OPENMPT_NAMESPACE_BEGIN namespace mpt { } // namespace mpt namespace detail { template using FileCursor = mpt::IO::FileCursor; template class FileReader; } // namespace detail namespace mpt { class PathString; } // namespace mpt using FileCursor = detail::FileCursor>; using FileReader = detail::FileReader>; using MemoryFileCursor = detail::FileCursor; using MemoryFileReader = detail::FileReader; OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/common/Logging.cpp0000644000175000017500000002336214056354456017660 00000000000000/* * Logging.cpp * ----------- * Purpose: General logging * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Logging.h" #include "mpt/io/base.hpp" #include "mpt/io/io.hpp" #include "mpt/io/io_stdstream.hpp" #include "mptFileIO.h" #if defined(MODPLUG_TRACKER) #include #endif #include "version.h" #include #include #include #include OPENMPT_NAMESPACE_BEGIN namespace mpt { namespace log { #if !defined(MPT_LOG_GLOBAL_LEVEL_STATIC) #if defined(MPT_LOG_GLOBAL_LEVEL) int GlobalLogLevel = static_cast(MPT_LOG_GLOBAL_LEVEL); #else int GlobalLogLevel = static_cast(LogDebug); #endif #endif #if defined(MODPLUG_TRACKER) && !defined(MPT_LOG_IS_DISABLED) bool FileEnabled = false; bool DebuggerEnabled = true; bool ConsoleEnabled = false; static char g_FacilitySolo[1024] = {0}; static char g_FacilityBlocked[1024] = {0}; void SetFacilities(const std::string &solo, const std::string &blocked) { std::strcpy(g_FacilitySolo, solo.c_str()); std::strcpy(g_FacilityBlocked, blocked.c_str()); } bool IsFacilityActive(const char *facility) noexcept { if(facility) { if(std::strlen(g_FacilitySolo) > 0) { if(std::strcmp(facility, g_FacilitySolo) != 0) { return false; } } if(std::strlen(g_FacilityBlocked) > 0) { if(std::strcmp(facility, g_FacilitySolo) == 0) { return false; } } } return true; } #endif void GlobalLogger::SendLogMessage(const mpt::source_location &loc, LogLevel level, const char *facility, const mpt::ustring &text) const { #ifdef MPT_LOG_IS_DISABLED MPT_UNREFERENCED_PARAMETER(loc); MPT_UNREFERENCED_PARAMETER(level); MPT_UNREFERENCED_PARAMETER(facility); MPT_UNREFERENCED_PARAMETER(text); #else // !MPT_LOG_IS_DISABLED MPT_MAYBE_CONSTANT_IF(mpt::log::GlobalLogLevel < level) { return; } #if defined(MODPLUG_TRACKER) if(!IsFacilityActive(facility)) { return; } #else // !MODPLUG_TRACKER MPT_UNREFERENCED_PARAMETER(facility); #endif // MODPLUG_TRACKER // remove eol if already present and add log level prefix const mpt::ustring message = LogLevelToString(level) + U_(": ") + mpt::trim_right(text, U_("\r\n")); const mpt::ustring file = mpt::ToUnicode(mpt::CharsetSource, loc.file_name() ? loc.file_name() : ""); const mpt::ustring function = mpt::ToUnicode(mpt::CharsetSource, loc.function_name() ? loc.function_name() : ""); const mpt::ustring line = mpt::ufmt::dec(loc.line()); #if defined(MODPLUG_TRACKER) && !defined(MPT_BUILD_WINESUPPORT) #if MPT_OS_WINDOWS static uint64 s_lastlogtime = 0; uint64 cur = mpt::Date::ANSI::Now(); uint64 diff = cur/10000 - s_lastlogtime; s_lastlogtime = cur/10000; #else uint64 cur = 0; uint64 diff = 0; #endif if(mpt::log::FileEnabled) { static std::optional s_logfile; if(!s_logfile) { s_logfile.emplace(P_("mptrack.log"), std::ios::app); } if(s_logfile) { mpt::IO::WriteText(*s_logfile, mpt::ToCharset(mpt::CharsetLogfile, MPT_UFORMAT("{}+{} {}({}): {} [{}]\n") ( mpt::Date::ANSI::ToUString(cur) , mpt::ufmt::right(6, mpt::ufmt::dec(diff)) , file , line , message , function ))); mpt::IO::Flush(*s_logfile); } } if(mpt::log::DebuggerEnabled) { OutputDebugStringW(mpt::ToWide(MPT_UFORMAT("{}({}): +{} {} [{}]\n") ( file , line , mpt::ufmt::right(6, mpt::ufmt::dec(diff)) , message , function )).c_str()); } if(mpt::log::ConsoleEnabled) { static bool consoleInited = false; if(!consoleInited) { AllocConsole(); consoleInited = true; } std::wstring consoletext = mpt::ToWide(message) + L"\r\n"; DWORD dummy = 0; WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), consoletext.c_str(), mpt::saturate_cast(consoletext.length()), &dummy, NULL); } #elif defined(MODPLUG_TRACKER) && defined(MPT_BUILD_WINESUPPORT) std::clog << "NativeSupport: " << mpt::ToCharset(mpt::CharsetStdIO, file) << "(" << mpt::ToCharset(mpt::CharsetStdIO, line) << ")" << ": " << mpt::ToCharset(mpt::CharsetStdIO, message) << " [" << mpt::ToCharset(mpt::CharsetStdIO, function) << "]" << std::endl; #else // !MODPLUG_TRACKER std::clog << "libopenmpt: " << mpt::ToCharset(mpt::CharsetStdIO, file) << "(" << mpt::ToCharset(mpt::CharsetStdIO, line) << ")" << ": " << mpt::ToCharset(mpt::CharsetStdIO, message) << " [" << mpt::ToCharset(mpt::CharsetStdIO, function) << "]" << std::endl; #endif // MODPLUG_TRACKER #endif // MPT_LOG_IS_DISABLED } #if defined(MODPLUG_TRACKER) namespace Trace { #if MPT_OS_WINDOWS // Debugging functionality will use simple globals. std::atomic g_Enabled{false}; static bool g_Sealed = false; struct Entry { uint32 Index; uint32 ThreadId; uint64 Timestamp; const char * Function; const char * File; int Line; Direction Direction; }; static MPT_FORCEINLINE bool operator < (const Entry &a, const Entry &b) noexcept { /* return false || (a.Timestamp < b.Timestamp) || (a.ThreadID < b.ThreadID) || (a.File < b.File) || (a.Line < b.Line) || (a.Function < b.Function) ; */ return false || (a.Index < b.Index) ; } static std::vector Entries; static std::atomic NextIndex(0); static uint32 ThreadIdGUI = 0; static uint32 ThreadIdAudio = 0; static uint32 ThreadIdNotify = 0; static uint32 ThreadIdWatchdir = 0; void Enable(std::size_t numEntries) { if(g_Sealed) { return; } Entries.clear(); Entries.resize(numEntries); NextIndex.store(0); g_Enabled = (numEntries > 0); } void Disable() { if(g_Sealed) { return; } g_Enabled = false; } MPT_NOINLINE void Trace(const mpt::source_location & loc, Direction direction) noexcept { // This will get called in realtime contexts and hot paths. // No blocking allowed here. const uint32 index = NextIndex.fetch_add(1); const std::size_t numEntries = Entries.size(); #if 1 LARGE_INTEGER time; time.QuadPart = 0; QueryPerformanceCounter(&time); const uint64 timestamp = time.QuadPart; #else FILETIME time = FILETIME(); GetSystemTimeAsFileTime(&time); const uint64 timestamp = (static_cast(time.dwHighDateTime) << 32) | (static_cast(time.dwLowDateTime) << 0); #endif const uint32 threadid = static_cast(GetCurrentThreadId()); mpt::log::Trace::Entry & entry = Entries[index % numEntries]; entry.Index = index; entry.ThreadId = threadid; entry.Timestamp = timestamp; entry.Function = loc.function_name(); entry.File = loc.file_name(); entry.Line = loc.line(); entry.Direction = direction; } void Seal() { if(!g_Enabled) { return; } g_Enabled = false; g_Sealed = true; uint32 count = NextIndex.fetch_add(0); if(count < Entries.size()) { Entries.resize(count); } } bool Dump(const mpt::PathString &filename) { if(!g_Sealed) { return false; } LARGE_INTEGER qpcNow; qpcNow.QuadPart = 0; QueryPerformanceCounter(&qpcNow); uint64 ftNow = mpt::Date::ANSI::Now(); // sort according to index in case of overflows std::stable_sort(Entries.begin(), Entries.end()); mpt::ofstream f(filename); f << "Build: OpenMPT " << mpt::ToCharset(mpt::CharsetLogfile, Build::GetVersionStringExtended()) << std::endl; bool qpcValid = false; LARGE_INTEGER qpcFreq; qpcFreq.QuadPart = 0; QueryPerformanceFrequency(&qpcFreq); if(qpcFreq.QuadPart > 0) { qpcValid = true; } f << "Dump: " << mpt::ToCharset(mpt::CharsetLogfile, mpt::Date::ANSI::ToUString(ftNow)) << std::endl; f << "Captured events: " << Entries.size() << std::endl; if(qpcValid && (Entries.size() > 0)) { double period = static_cast(Entries[Entries.size() - 1].Timestamp - Entries[0].Timestamp) / static_cast(qpcFreq.QuadPart); double eventsPerSecond = Entries.size() / period; f << "Period [s]: " << mpt::afmt::fix(period) << std::endl; f << "Events/second: " << mpt::afmt::fix(eventsPerSecond) << std::endl; } for(auto &entry : Entries) { if(!entry.Function) entry.Function = ""; if(!entry.File) entry.File = ""; std::string time; if(qpcValid) { time = mpt::ToCharset(mpt::CharsetLogfile, mpt::Date::ANSI::ToUString( ftNow - static_cast( static_cast(qpcNow.QuadPart - entry.Timestamp) * (10000000.0 / static_cast(qpcFreq.QuadPart) ) ) ) ); } else { time = MPT_AFORMAT("0x{}")(mpt::afmt::hex0<16>(entry.Timestamp)); } f << time; if(entry.ThreadId == ThreadIdGUI) { f << " -----GUI "; } else if(entry.ThreadId == ThreadIdAudio) { f << " ---Audio "; } else if(entry.ThreadId == ThreadIdNotify) { f << " --Notify "; } else if(entry.ThreadId == ThreadIdWatchdir) { f << " WatchDir "; } else { f << " " << mpt::afmt::hex0<8>(entry.ThreadId) << " "; } f << (entry.Direction == mpt::log::Trace::Direction::Enter ? ">" : entry.Direction == mpt::log::Trace::Direction::Leave ? "<" : " ") << " "; f << entry.File << "(" << entry.Line << "): " << entry.Function; f << std::endl; } return true; } void SetThreadId(mpt::log::Trace::ThreadKind kind, uint32 id) { if(id == 0) { return; } switch(kind) { case ThreadKindGUI: ThreadIdGUI = id; break; case ThreadKindAudio: ThreadIdAudio = id; break; case ThreadKindNotify: ThreadIdNotify = id; break; case ThreadKindWatchdir: ThreadIdWatchdir = id; break; } } uint32 GetThreadId(mpt::log::Trace::ThreadKind kind) { uint32 result = 0; switch(kind) { case ThreadKindGUI: result = ThreadIdGUI; break; case ThreadKindAudio: result = ThreadIdAudio; break; case ThreadKindNotify: result = ThreadIdNotify; break; case ThreadKindWatchdir: result = ThreadIdWatchdir; break; } return result; } #endif // MPT_OS_WINDOWS } // namespace Trace #endif // MODPLUG_TRACKER } // namespace log } // namespace mpt OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/common/Logging.h0000644000175000017500000001350614053470755017322 00000000000000/* * Logging.h * --------- * Purpose: General logging * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "openmpt/logging/Logger.hpp" #include "mptPathString.h" #include "mptString.h" #if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS #include #endif OPENMPT_NAMESPACE_BEGIN /* Build time logging configuration: * #define MPT_LOG_GLOBAL_LEVEL_STATIC #define MPT_LOG_GLOBAL_LEVEL # Define the former (to anything) and the latter (to one of the log levels below) in order to statically select the verbosity of logging at build time. MPT_LOG calls that exceed the specified logging level will get dead-code eliminated at compile time. This especially means that, when setting MPT_LOG_GLOBAL_LEVEL to 0, no MPT_LOG call (with a constant level parameter) remains in the resulting binary, however, they still do get parsed and properly type checked by the compiler. Logging: If the context is related to a particular CSoundfile instance, use CSoundfile::AddToLog. Logging a simple message: MPT_LOG_GLOBAL(LogWarning, "sounddev", "some message"); MPT_LOG_GLOBAL(LogWarning, "sounddev", U_("some message")); Facility is some course grained code section identifier (more coarse grained than the current file name probably), useful to do some selective logging. Logging a more complex message: MPT_LOG_GLOBAL(LogWarning, "sounddev", MPT_UFORMAT("Some message: foo={}, bar=0x{}")(foo, mpt::ufmt::hex0<8>(bar))); Note that even with full enabled logging and a runtime configurable logging level, the runtime overhead of a MPT_LOG_GLOBAL(level, facility, text) call is just a single conditional in case the verbosity does not require logging the respective message. Even the expression "text" is not evaluated. */ inline mpt::ustring LogLevelToString(LogLevel level) { switch(level) { case LogError: return U_("error"); break; case LogWarning: return U_("warning"); break; case LogNotification: return U_("notify"); break; case LogInformation: return U_("info"); break; case LogDebug: return U_("debug"); break; } return U_("unknown"); } class ILog { protected: virtual ~ILog() { } public: virtual void AddToLog(LogLevel level, const mpt::ustring &text) const = 0; }; namespace mpt { namespace log { #if defined(MPT_LOG_GLOBAL_LEVEL_STATIC) #if (MPT_LOG_GLOBAL_LEVEL <= 0) // All logging has beeen statically disabled. // All logging code gets compiled and immediately dead-code eliminated. #define MPT_LOG_IS_DISABLED #endif inline constexpr int GlobalLogLevel = MPT_LOG_GLOBAL_LEVEL ; #else extern int GlobalLogLevel; #endif #if defined(MODPLUG_TRACKER) && !defined(MPT_LOG_IS_DISABLED) extern bool FileEnabled; extern bool DebuggerEnabled; extern bool ConsoleEnabled; void SetFacilities(const std::string &solo, const std::string &blocked); bool IsFacilityActive(const char *facility) noexcept; #else MPT_FORCEINLINE bool IsFacilityActive(const char * /*facility*/ ) noexcept { return true; } #endif class GlobalLogger final : public ILogger { public: GlobalLogger() = default; ~GlobalLogger() final = default; public: bool IsLevelActive(LogLevel level) const noexcept override { return (mpt::log::GlobalLogLevel >= level); } bool IsFacilityActive(const char *facility) const noexcept override { return mpt::log::IsFacilityActive(facility); } void SendLogMessage(const mpt::source_location &loc, LogLevel level, const char *facility, const mpt::ustring &message) const override; }; #define MPT_LOG_GLOBAL(level, facility, text) MPT_LOG(mpt::log::GlobalLogger{}, (level), (facility), (text)) #if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS namespace Trace { // This is not strictly thread safe in all corner cases because of missing barriers. // We do not care in order to not harm the fast path with additional barriers. // Enabled tracing incurs a runtime overhead with multiple threads as a global atomic variable // gets modified. // This cacheline bouncing does not matter at all // if there are not multiple thread adding trace points at high frequency (way greater than 1000Hz), // which, in OpenMPT, is only ever the case for just a single thread (the audio thread), if at all. extern std::atomic g_Enabled; inline bool IsEnabled() { return g_Enabled; } enum class Direction : int8 { Unknown = 0, Enter = 1, Leave = -1, }; MPT_NOINLINE void Trace(const mpt::source_location & loc, Direction direction = Direction::Unknown) noexcept; enum ThreadKind { ThreadKindGUI, ThreadKindAudio, ThreadKindNotify, ThreadKindWatchdir, }; void Enable(std::size_t numEntries); void Disable(); void SetThreadId(mpt::log::Trace::ThreadKind kind, uint32 id); uint32 GetThreadId(mpt::log::Trace::ThreadKind kind); void Seal(); bool Dump(const mpt::PathString &filename); class Scope { private: const mpt::source_location loc; public: MPT_FORCEINLINE Scope(mpt::source_location loc) noexcept : loc(loc) { if(mpt::log::Trace::g_Enabled) { mpt::log::Trace::Trace(loc, mpt::log::Trace::Direction::Enter); } } MPT_FORCEINLINE ~Scope() noexcept { if(mpt::log::Trace::g_Enabled) { mpt::log::Trace::Trace(loc, mpt::log::Trace::Direction::Leave); } } }; #define MPT_TRACE_CONCAT_HELPER(x, y) x ## y #define MPT_TRACE_CONCAT(x, y) MPT_TRACE_CONCAT_HELPER(x, y) #define MPT_TRACE_SCOPE() mpt::log::Trace::Scope MPT_TRACE_CONCAT(MPT_TRACE_VAR, __LINE__)(MPT_SOURCE_LOCATION_CURRENT()) #define MPT_TRACE() do { if(mpt::log::Trace::g_Enabled) { mpt::log::Trace::Trace(MPT_SOURCE_LOCATION_CURRENT()); } } while(0) } // namespace Trace #else // !MODPLUG_TRACKER #define MPT_TRACE_SCOPE() do { } while(0) #define MPT_TRACE() do { } while(0) #endif // MODPLUG_TRACKER } // namespace log } // namespace mpt OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/common/misc_util.h0000644000175000017500000001105014053631251017702 00000000000000/* * misc_util.h * ----------- * Purpose: Various useful utility functions. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "mpt/base/span.hpp" #include "mpt/exception_text/exception_text.hpp" #include "mptAssert.h" #include "mptBaseMacros.h" #include "mptBaseTypes.h" #include "mptBaseUtils.h" #include "mptString.h" // old #include "mptBaseUtils.h" #include "mptStringFormat.h" #include "mptStringParse.h" #include "mptTime.h" #include #include #include #include #include OPENMPT_NAMESPACE_BEGIN namespace Util { // Insert a range of items [insStart, insEnd], and possibly shift item fix to the left. template void InsertItem(const T insStart, const T insEnd, T &fix) { MPT_ASSERT(insEnd >= insStart); if(fix >= insStart) { fix += (insEnd - insStart + 1); } } // Insert a range of items [insStart, insEnd], and possibly shift items in range [fixStart, fixEnd] to the right. template void InsertRange(const T insStart, const T insEnd, T &fixStart, T &fixEnd) { MPT_ASSERT(insEnd >= insStart); const T insLength = insEnd - insStart + 1; if(fixStart >= insEnd) { fixStart += insLength; } if(fixEnd >= insEnd) { fixEnd += insLength; } } // Delete a range of items [delStart, delEnd], and possibly shift item fix to the left. template void DeleteItem(const T delStart, const T delEnd, T &fix) { MPT_ASSERT(delEnd >= delStart); if(fix > delEnd) { fix -= (delEnd - delStart + 1); } } // Delete a range of items [delStart, delEnd], and possibly shift items in range [fixStart, fixEnd] to the left. template void DeleteRange(const T delStart, const T delEnd, T &fixStart, T &fixEnd) { MPT_ASSERT(delEnd >= delStart); const T delLength = delEnd - delStart + 1; if(delStart < fixStart && delEnd < fixStart) { // cut part is before loop start fixStart -= delLength; fixEnd -= delLength; } else if(delStart < fixStart && delEnd < fixEnd) { // cut part is partly before loop start fixStart = delStart; fixEnd -= delLength; } else if(delStart >= fixStart && delEnd < fixEnd) { // cut part is in the loop fixEnd -= delLength; } else if(delStart >= fixStart && delStart < fixEnd && delEnd > fixEnd) { // cut part is partly before loop end fixEnd = delStart; } } template class fixed_size_queue { private: T buffer[n+1]; std::size_t read_position; std::size_t write_position; public: fixed_size_queue() : read_position(0), write_position(0) { return; } void clear() { read_position = 0; write_position = 0; } std::size_t read_size() const { if ( write_position > read_position ) { return write_position - read_position; } else if ( write_position < read_position ) { return write_position - read_position + n + 1; } else { return 0; } } std::size_t write_size() const { if ( write_position > read_position ) { return read_position - write_position + n; } else if ( write_position < read_position ) { return read_position - write_position - 1; } else { return n; } } bool push( const T & v ) { if ( !write_size() ) { return false; } buffer[write_position] = v; write_position = ( write_position + 1 ) % ( n + 1 ); return true; } bool pop() { if ( !read_size() ) { return false; } read_position = ( read_position + 1 ) % ( n + 1 ); return true; } T peek() { if ( !read_size() ) { return T(); } return buffer[read_position]; } const T * peek_p() { if ( !read_size() ) { return nullptr; } return &(buffer[read_position]); } const T * peek_next_p() { if ( read_size() < 2 ) { return nullptr; } return &(buffer[(read_position+1)%(n+1)]); } }; } // namespace Util #if MPT_OS_WINDOWS template Tstring ParseMaybeNullTerminatedStringFromBufferWithSizeInBytes(const Tbuf *buf, Tsize sizeBytes) { // REG_SZ may contain a single NUL terminator, multiple NUL terminators, or no NUL terminator at all return Tstring(reinterpret_cast(buf), reinterpret_cast(buf) + (sizeBytes / sizeof(typename Tstring::value_type))).c_str(); } #endif // MPT_OS_WINDOWS OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/common/mptAssert.h0000644000175000017500000001114714052666041017707 00000000000000/* * mptAssert.h * ----------- * Purpose: assert and static_assert * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "mpt/base/source_location.hpp" #include "mptBaseMacros.h" OPENMPT_NAMESPACE_BEGIN // Static code checkers might need to get the knowledge of our assertions transferred to them. #define MPT_CHECKER_ASSUME_ASSERTIONS 1 //#define MPT_CHECKER_ASSUME_ASSERTIONS 0 #ifdef MPT_BUILD_ANALYZED #if MPT_COMPILER_MSVC #if MPT_CHECKER_ASSUME_ASSERTIONS #define MPT_CHECKER_ASSUME(x) __analysis_assume(!!(x)) #endif #elif MPT_COMPILER_CLANG #if MPT_CHECKER_ASSUME_ASSERTIONS #ifdef NDEBUG #error "Builds for static analyzers depend on assert() being enabled, but the current build has #define NDEBUG. This makes no sense." #endif OPENMPT_NAMESPACE_END #include OPENMPT_NAMESPACE_BEGIN #define MPT_CHECKER_ASSUME(x) assert(!!(x)) #endif #endif // MPT_COMPILER #endif // MPT_BUILD_ANALYZED #ifndef MPT_CHECKER_ASSUME #define MPT_CHECKER_ASSUME(x) do { } while(0) #endif #if defined(MPT_WITH_MFC) && !defined(MPT_CPPCHECK_CUSTOM) #if !defined(ASSERT) #error "MFC is expected to #define ASSERT" #endif // !defined(ASERRT) #define MPT_FRAMEWORK_ASSERT_IS_DEFINED #if defined(_DEBUG) #define MPT_FRAMEWORK_ASSERT_IS_ACTIVE 1 #else // !_DEBUG #define MPT_FRAMEWORK_ASSERT_IS_ACTIVE 0 #endif // _DEBUG // let MFC handle our asserts #define MPT_ASSERT_USE_FRAMEWORK 1 #else // !MPT_WITH_MFC #if defined(ASSERT) #define MPT_FRAMEWORK_ASSERT_IS_DEFINED #if defined(_DEBUG) #define MPT_FRAMEWORK_ASSERT_IS_ACTIVE 1 #else // !_DEBUG #define MPT_FRAMEWORK_ASSERT_IS_ACTIVE 0 #endif // _DEBUG #endif // !defined(ASERRT) // handle assert in our own way without relying on some platform-/framework-specific assert implementation #define MPT_ASSERT_USE_FRAMEWORK 0 #endif // MPT_WITH_MFC #if defined(MPT_FRAMEWORK_ASSERT_IS_DEFINED) && (MPT_ASSERT_USE_FRAMEWORK == 1) #define MPT_ASSERT_NOTREACHED() ASSERT(0) #define MPT_ASSERT(expr) ASSERT((expr)) #define MPT_ASSERT_MSG(expr, msg) ASSERT((expr) && (msg)) #if (MPT_FRAMEWORK_ASSERT_IS_ACTIVE == 1) #define MPT_ASSERT_ALWAYS(expr) ASSERT((expr)) #define MPT_ASSERT_ALWAYS_MSG(expr, msg) ASSERT((expr) && (msg)) #else #define MPT_ASSERT_ALWAYS(expr) do { if(!(expr)) { OPENMPT_NAMESPACE::AssertHandler(MPT_SOURCE_LOCATION_CURRENT(), #expr); } MPT_CHECKER_ASSUME(expr); } while(0) #define MPT_ASSERT_ALWAYS_MSG(expr, msg) do { if(!(expr)) { OPENMPT_NAMESPACE::AssertHandler(MPT_SOURCE_LOCATION_CURRENT(), #expr, msg); } MPT_CHECKER_ASSUME(expr); } while(0) #ifndef MPT_ASSERT_HANDLER_NEEDED #define MPT_ASSERT_HANDLER_NEEDED #endif #endif #elif defined(NO_ASSERTS) #define MPT_ASSERT_NOTREACHED() MPT_CHECKER_ASSUME(0) #define MPT_ASSERT(expr) MPT_CHECKER_ASSUME(expr) #define MPT_ASSERT_MSG(expr, msg) MPT_CHECKER_ASSUME(expr) #define MPT_ASSERT_ALWAYS(expr) do { if(!(expr)) { OPENMPT_NAMESPACE::AssertHandler(MPT_SOURCE_LOCATION_CURRENT(), #expr); } MPT_CHECKER_ASSUME(expr); } while(0) #define MPT_ASSERT_ALWAYS_MSG(expr, msg) do { if(!(expr)) { OPENMPT_NAMESPACE::AssertHandler(MPT_SOURCE_LOCATION_CURRENT(), #expr, msg); } MPT_CHECKER_ASSUME(expr); } while(0) #ifndef MPT_ASSERT_HANDLER_NEEDED #define MPT_ASSERT_HANDLER_NEEDED #endif #else // !NO_ASSERTS #define MPT_ASSERT_NOTREACHED() do { if constexpr(!(0)) { OPENMPT_NAMESPACE::AssertHandler(MPT_SOURCE_LOCATION_CURRENT(), "0"); } MPT_CHECKER_ASSUME(0); } while(0) #define MPT_ASSERT(expr) do { if(!(expr)) { OPENMPT_NAMESPACE::AssertHandler(MPT_SOURCE_LOCATION_CURRENT(), #expr); } MPT_CHECKER_ASSUME(expr); } while(0) #define MPT_ASSERT_MSG(expr, msg) do { if(!(expr)) { OPENMPT_NAMESPACE::AssertHandler(MPT_SOURCE_LOCATION_CURRENT(), #expr, msg); } MPT_CHECKER_ASSUME(expr); } while(0) #define MPT_ASSERT_ALWAYS(expr) do { if(!(expr)) { OPENMPT_NAMESPACE::AssertHandler(MPT_SOURCE_LOCATION_CURRENT(), #expr); } MPT_CHECKER_ASSUME(expr); } while(0) #define MPT_ASSERT_ALWAYS_MSG(expr, msg) do { if(!(expr)) { OPENMPT_NAMESPACE::AssertHandler(MPT_SOURCE_LOCATION_CURRENT(), #expr, msg); } MPT_CHECKER_ASSUME(expr); } while(0) #ifndef MPT_ASSERT_HANDLER_NEEDED #define MPT_ASSERT_HANDLER_NEEDED #endif #endif // NO_ASSERTS #if defined(MPT_ASSERT_HANDLER_NEEDED) // custom assert handler needed MPT_NOINLINE void AssertHandler(const mpt::source_location &loc, const char *expr, const char *msg=nullptr); #endif // MPT_ASSERT_HANDLER_NEEDED OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/common/mptBaseMacros.h0000644000175000017500000000354614052666041020471 00000000000000/* * mptBaseMacros.h * --------------- * Purpose: Basic assorted compiler-related helpers. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "mpt/base/preprocessor.hpp" #include "mpt/base/compiletime_warning.hpp" #include "mpt/base/macros.hpp" #if MPT_CXX_AT_LEAST(20) #include #else // !C++20 #include #endif // C++20 #include #include #include #include #include #include #include OPENMPT_NAMESPACE_BEGIN #define MPT_UNREFERENCED_PARAMETER(x) MPT_UNUSED(x) #define MPT_UNUSED_VARIABLE(x) MPT_UNUSED(x) #if MPT_COMPILER_MSVC // warning LNK4221: no public symbols found; archive member will be inaccessible // There is no way to selectively disable linker warnings. // #pragma warning does not apply and a command line option does not exist. // Some options: // 1. Macro which generates a variable with a unique name for each file (which means we have to pass the filename to the macro) // 2. unnamed namespace containing any symbol (does not work for c++11 compilers because they actually have internal linkage now) // 3. An unused trivial inline function. // Option 3 does not actually solve the problem though, which leaves us with option 1. // In any case, for optimized builds, the linker will just remove the useless symbol. #define MPT_MSVC_WORKAROUND_LNK4221_CONCAT_DETAIL(x,y) x##y #define MPT_MSVC_WORKAROUND_LNK4221_CONCAT(x,y) MPT_MSVC_WORKAROUND_LNK4221_CONCAT_DETAIL(x,y) #define MPT_MSVC_WORKAROUND_LNK4221(x) int MPT_MSVC_WORKAROUND_LNK4221_CONCAT(mpt_msvc_workaround_lnk4221_,x) = 0; #endif #ifndef MPT_MSVC_WORKAROUND_LNK4221 #define MPT_MSVC_WORKAROUND_LNK4221(x) #endif OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/common/mptBaseTypes.h0000644000175000017500000000304314052666041020341 00000000000000/* * mptBaseTypes.h * -------------- * Purpose: Basic data type definitions. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "mpt/base/integer.hpp" #include "mpt/base/floatingpoint.hpp" #include "mpt/base/pointer.hpp" #include "mpt/base/check_platform.hpp" #include "mpt/base/source_location.hpp" #include "openmpt/base/Types.hpp" #include "mptBaseMacros.h" #include #include #include #include #include #include OPENMPT_NAMESPACE_BEGIN constexpr inline int8 int8_min = std::numeric_limits::min(); constexpr inline int16 int16_min = std::numeric_limits::min(); constexpr inline int32 int32_min = std::numeric_limits::min(); constexpr inline int64 int64_min = std::numeric_limits::min(); constexpr inline int8 int8_max = std::numeric_limits::max(); constexpr inline int16 int16_max = std::numeric_limits::max(); constexpr inline int32 int32_max = std::numeric_limits::max(); constexpr inline int64 int64_max = std::numeric_limits::max(); constexpr inline uint8 uint8_max = std::numeric_limits::max(); constexpr inline uint16 uint16_max = std::numeric_limits::max(); constexpr inline uint32 uint32_max = std::numeric_limits::max(); constexpr inline uint64 uint64_max = std::numeric_limits::max(); OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/common/mptBaseUtils.h0000644000175000017500000001021014056707252020333 00000000000000/* * mptBaseUtils.h * -------------- * Purpose: Various useful utility functions. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "mpt/base/algorithm.hpp" #include "mpt/base/arithmetic_shift.hpp" #include "mpt/base/array.hpp" #include "mpt/base/bit.hpp" #include "mpt/base/constexpr_throw.hpp" #include "mpt/base/math.hpp" #include "mpt/base/memory.hpp" #include "mpt/base/numeric.hpp" #include "mpt/base/saturate_cast.hpp" #include "mpt/base/saturate_round.hpp" #include "mpt/base/utility.hpp" #include "mpt/base/wrapping_divide.hpp" #include "mptBaseMacros.h" #include "mptBaseTypes.h" #include #include #include #include #include #include #include #include #if MPT_COMPILER_MSVC #include #endif OPENMPT_NAMESPACE_BEGIN template inline void Clear(T& x) { static_assert(!std::is_pointer::value); mpt::reset(x); } // Memset given object to zero. template inline void MemsetZero(T& a) { static_assert(std::is_pointer::value == false, "Won't memset pointers."); mpt::memclear(a); } // Limits 'val' to given range. If 'val' is less than 'lowerLimit', 'val' is set to value 'lowerLimit'. // Similarly if 'val' is greater than 'upperLimit', 'val' is set to value 'upperLimit'. // If 'lowerLimit' > 'upperLimit', 'val' won't be modified. template inline void Limit(T& val, const C lowerLimit, const C upperLimit) { if(lowerLimit > upperLimit) return; if(val < lowerLimit) val = lowerLimit; else if(val > upperLimit) val = upperLimit; } // Like Limit, but returns value template inline T Clamp(T val, const C lowerLimit, const C upperLimit) { if(val < lowerLimit) return lowerLimit; else if(val > upperLimit) return upperLimit; else return val; } // Like Limit, but with upperlimit only. template inline void LimitMax(T& val, const C upperLimit) { if(val > upperLimit) val = upperLimit; } namespace Util { // Returns maximum value of given integer type. template constexpr T MaxValueOfType(const T&) {static_assert(std::numeric_limits::is_integer == true, "Only integer types are allowed."); return (std::numeric_limits::max)();} } // namespace Util namespace Util { // Multiply two 32-bit integers, receive 64-bit result. // MSVC generates unnecessarily complicated code for the unoptimized variant using _allmul. MPT_CONSTEXPR20_FUN int64 mul32to64(int32 a, int32 b) { #if MPT_COMPILER_MSVC && (defined(_M_IX86) || defined(_M_X64)) MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) { return static_cast(a) * b; } else { return __emul(a, b); } #else return static_cast(a) * b; #endif } MPT_CONSTEXPR20_FUN uint64 mul32to64_unsigned(uint32 a, uint32 b) { #if MPT_COMPILER_MSVC && (defined(_M_IX86) || defined(_M_X64)) MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) { return static_cast(a) * b; } else { return __emulu(a, b); } #else return static_cast(a) * b; #endif } MPT_CONSTEXPR20_FUN int32 muldiv(int32 a, int32 b, int32 c) { return mpt::saturate_cast( mul32to64( a, b ) / c ); } MPT_CONSTEXPR20_FUN int32 muldivr(int32 a, int32 b, int32 c) { return mpt::saturate_cast( ( mul32to64( a, b ) + ( c / 2 ) ) / c ); } // Do not use overloading because catching unsigned version by accident results in slower X86 code. MPT_CONSTEXPR20_FUN uint32 muldiv_unsigned(uint32 a, uint32 b, uint32 c) { return mpt::saturate_cast( mul32to64_unsigned( a, b ) / c ); } MPT_CONSTEXPR20_FUN uint32 muldivr_unsigned(uint32 a, uint32 b, uint32 c) { return mpt::saturate_cast( ( mul32to64_unsigned( a, b ) + ( c / 2u ) ) / c ); } constexpr MPT_FORCEINLINE int32 muldivrfloor(int64 a, uint32 b, uint32 c) { a *= b; a += c / 2u; return (a >= 0) ? mpt::saturate_cast(a / c) : mpt::saturate_cast((a - (c - 1)) / c); } } // namespace Util OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/common/mptFileIO.cpp0000644000175000017500000003252414056704427020117 00000000000000/* * mptFileIO.cpp * ------------- * Purpose: File I/O wrappers * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "mptFileIO.h" #if defined(MPT_ENABLE_FILEIO) #include "mpt/io/io.hpp" #include "mpt/io/io_stdstream.hpp" #if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS #include "mpt/system_error/system_error.hpp" #include "FileReader.h" #endif // MODPLUG_TRACKER && MPT_OS_WINDOWS #endif // MPT_ENABLE_FILEIO #ifdef MODPLUG_TRACKER #if MPT_OS_WINDOWS #include #include #include #endif // MPT_OS_WINDOWS #endif // MODPLUG_TRACKER #if defined(MPT_ENABLE_FILEIO) #if MPT_COMPILER_MSVC #include #include #endif // MPT_COMPILER_MSVC #endif // MPT_ENABLE_FILEIO OPENMPT_NAMESPACE_BEGIN #if defined(MPT_ENABLE_FILEIO) #if !defined(MPT_BUILD_SILENCE_LIBOPENMPT_CONFIGURATION_WARNINGS) #if defined(MPT_COMPILER_QUIRK_WINDOWS_FSTREAM_NO_WCHAR) #if MPT_GCC_BEFORE(9,1,0) MPT_WARNING("Warning: MinGW with GCC earlier than 9.1 detected. Standard library does neither provide std::fstream wchar_t overloads nor std::filesystem with wchar_t support. Unicode filename support is thus unavailable.") #endif // MPT_GCC_AT_LEAST(9,1,0) #endif // MPT_COMPILER_QUIRK_WINDOWS_FSTREAM_NO_WCHAR #endif // !MPT_BUILD_SILENCE_LIBOPENMPT_CONFIGURATION_WARNINGS #ifdef MODPLUG_TRACKER #if MPT_OS_WINDOWS bool SetFilesystemCompression(HANDLE hFile) { if(hFile == INVALID_HANDLE_VALUE) { return false; } USHORT format = COMPRESSION_FORMAT_DEFAULT; DWORD dummy = 0; BOOL result = DeviceIoControl(hFile, FSCTL_SET_COMPRESSION, (LPVOID)&format, sizeof(format), NULL, 0, &dummy /*required*/ , NULL); return result != FALSE; } bool SetFilesystemCompression(int fd) { if(fd < 0) { return false; } uintptr_t fhandle = _get_osfhandle(fd); HANDLE hFile = (HANDLE)fhandle; if(hFile == INVALID_HANDLE_VALUE) { return false; } return SetFilesystemCompression(hFile); } bool SetFilesystemCompression(const mpt::PathString &filename) { DWORD attributes = GetFileAttributes(filename.AsNativePrefixed().c_str()); if(attributes == INVALID_FILE_ATTRIBUTES) { return false; } if(attributes & FILE_ATTRIBUTE_COMPRESSED) { return true; } HANDLE hFile = CreateFile(filename.AsNativePrefixed().c_str(), GENERIC_ALL, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if(hFile == INVALID_HANDLE_VALUE) { return false; } bool result = SetFilesystemCompression(hFile); CloseHandle(hFile); return result; } #endif // MPT_OS_WINDOWS #endif // MODPLUG_TRACKER #ifdef MODPLUG_TRACKER namespace mpt { #if MPT_COMPILER_MSVC mpt::tstring SafeOutputFile::convert_mode(std::ios_base::openmode mode, FlushMode flushMode) { mpt::tstring fopen_mode; switch(mode & ~(std::ios_base::ate | std::ios_base::binary)) { case std::ios_base::in: fopen_mode = _T("r"); break; case std::ios_base::out: [[fallthrough]]; case std::ios_base::out | std::ios_base::trunc: fopen_mode = _T("w"); break; case std::ios_base::app: [[fallthrough]]; case std::ios_base::out | std::ios_base::app: fopen_mode = _T("a"); break; case std::ios_base::out | std::ios_base::in: fopen_mode = _T("r+"); break; case std::ios_base::out | std::ios_base::in | std::ios_base::trunc: fopen_mode = _T("w+"); break; case std::ios_base::out | std::ios_base::in | std::ios_base::app: [[fallthrough]]; case std::ios_base::in | std::ios_base::app: fopen_mode = _T("a+"); break; } if(fopen_mode.empty()) { return fopen_mode; } if(mode & std::ios_base::binary) { fopen_mode += _T("b"); } if(flushMode == FlushMode::Full) { fopen_mode += _T("c"); // force commit on fflush (MSVC specific) } return fopen_mode; } std::FILE * SafeOutputFile::internal_fopen(const mpt::PathString &filename, std::ios_base::openmode mode, FlushMode flushMode) { m_f = nullptr; mpt::tstring fopen_mode = convert_mode(mode, flushMode); if(fopen_mode.empty()) { return nullptr; } std::FILE *f = #ifdef UNICODE _wfopen(filename.AsNativePrefixed().c_str(), fopen_mode.c_str()) #else std::fopen(filename.AsNativePrefixed().c_str(), fopen_mode.c_str()) #endif ; if(!f) { return nullptr; } if(mode & std::ios_base::ate) { if(std::fseek(f, 0, SEEK_END) != 0) { std::fclose(f); f = nullptr; return nullptr; } } m_f = f; return f; } #endif // MPT_COMPILER_MSVC // cppcheck-suppress exceptThrowInDestructor SafeOutputFile::~SafeOutputFile() noexcept(false) { const bool mayThrow = (std::uncaught_exceptions() == 0); if(!stream()) { #if MPT_COMPILER_MSVC if(m_f) { std::fclose(m_f); } #endif // MPT_COMPILER_MSVC if(mayThrow && (stream().exceptions() & (std::ios::badbit | std::ios::failbit))) { // cppcheck-suppress exceptThrowInDestructor throw std::ios_base::failure(std::string("Error before flushing file buffers.")); } return; } if(!stream().rdbuf()) { #if MPT_COMPILER_MSVC if(m_f) { std::fclose(m_f); } #endif // MPT_COMPILER_MSVC if(mayThrow && (stream().exceptions() & (std::ios::badbit | std::ios::failbit))) { // cppcheck-suppress exceptThrowInDestructor throw std::ios_base::failure(std::string("Error before flushing file buffers.")); } return; } #if MPT_COMPILER_MSVC if(!m_f) { return; } #endif // MPT_COMPILER_MSVC bool errorOnFlush = false; if(m_FlushMode != FlushMode::None) { try { if(stream().rdbuf()->pubsync() != 0) { errorOnFlush = true; } } catch(const std::exception &) { errorOnFlush = true; #if MPT_COMPILER_MSVC if(m_FlushMode != FlushMode::None) { if(std::fflush(m_f) != 0) { errorOnFlush = true; } } if(std::fclose(m_f) != 0) { errorOnFlush = true; } #endif // MPT_COMPILER_MSVC if(mayThrow) { // ignore errorOnFlush here, and re-throw the earlier exception // cppcheck-suppress exceptThrowInDestructor throw; } } } #if MPT_COMPILER_MSVC if(m_FlushMode != FlushMode::None) { if(std::fflush(m_f) != 0) { errorOnFlush = true; } } if(std::fclose(m_f) != 0) { errorOnFlush = true; } #endif // MPT_COMPILER_MSVC if(mayThrow && errorOnFlush && (stream().exceptions() & (std::ios::badbit | std::ios::failbit))) { // cppcheck-suppress exceptThrowInDestructor throw std::ios_base::failure(std::string("Error flushing file buffers.")); } } } // namespace mpt #endif // MODPLUG_TRACKER #ifdef MODPLUG_TRACKER namespace mpt { LazyFileRef & LazyFileRef::operator = (const std::vector &data) { mpt::ofstream file(m_Filename, std::ios::binary); file.exceptions(std::ios_base::failbit | std::ios_base::badbit); mpt::IO::WriteRaw(file, mpt::as_span(data)); mpt::IO::Flush(file); return *this; } LazyFileRef & LazyFileRef::operator = (const std::vector &data) { mpt::ofstream file(m_Filename, std::ios::binary); file.exceptions(std::ios_base::failbit | std::ios_base::badbit); mpt::IO::WriteRaw(file, mpt::as_span(data)); mpt::IO::Flush(file); return *this; } LazyFileRef & LazyFileRef::operator = (const std::string &data) { mpt::ofstream file(m_Filename, std::ios::binary); file.exceptions(std::ios_base::failbit | std::ios_base::badbit); mpt::IO::WriteRaw(file, mpt::as_span(data)); mpt::IO::Flush(file); return *this; } LazyFileRef::operator std::vector () const { mpt::ifstream file(m_Filename, std::ios::binary); if(!mpt::IO::IsValid(file)) { return std::vector(); } file.exceptions(std::ios_base::failbit | std::ios_base::badbit); mpt::IO::SeekEnd(file); std::vector buf(mpt::saturate_cast(mpt::IO::TellRead(file))); mpt::IO::SeekBegin(file); mpt::IO::ReadRaw(file, mpt::as_span(buf)); return buf; } LazyFileRef::operator std::vector () const { mpt::ifstream file(m_Filename, std::ios::binary); if(!mpt::IO::IsValid(file)) { return std::vector(); } file.exceptions(std::ios_base::failbit | std::ios_base::badbit); mpt::IO::SeekEnd(file); std::vector buf(mpt::saturate_cast(mpt::IO::TellRead(file))); mpt::IO::SeekBegin(file); mpt::IO::ReadRaw(file, mpt::as_span(buf)); return buf; } LazyFileRef::operator std::string () const { mpt::ifstream file(m_Filename, std::ios::binary); if(!mpt::IO::IsValid(file)) { return std::string(); } file.exceptions(std::ios_base::failbit | std::ios_base::badbit); mpt::IO::SeekEnd(file); std::vector buf(mpt::saturate_cast(mpt::IO::TellRead(file))); mpt::IO::SeekBegin(file); mpt::IO::ReadRaw(file, mpt::as_span(buf)); return mpt::buffer_cast(buf); } } // namespace mpt #endif // MODPLUG_TRACKER InputFile::InputFile(const mpt::PathString &filename, bool allowWholeFileCaching) : m_IsValid(false) , m_IsCached(false) { MPT_ASSERT(!filename.empty()); Open(filename, allowWholeFileCaching); } InputFile::~InputFile() { return; } bool InputFile::Open(const mpt::PathString &filename, bool allowWholeFileCaching) { m_IsCached = false; m_Cache.resize(0); m_Cache.shrink_to_fit(); m_Filename = filename; m_File.open(m_Filename, std::ios::binary | std::ios::in); if(allowWholeFileCaching) { if(mpt::IO::IsReadSeekable(m_File)) { if(!mpt::IO::SeekEnd(m_File)) { m_File.close(); return false; } mpt::IO::Offset filesize = mpt::IO::TellRead(m_File); if(!mpt::IO::SeekBegin(m_File)) { m_File.close(); return false; } if(mpt::in_range(filesize)) { std::size_t buffersize = mpt::saturate_cast(filesize); m_Cache.resize(buffersize); if(mpt::IO::ReadRaw(m_File, mpt::as_span(m_Cache)).size() != mpt::saturate_cast(filesize)) { m_File.close(); return false; } if(!mpt::IO::SeekBegin(m_File)) { m_File.close(); return false; } m_IsCached = true; m_IsValid = true; return true; } } } m_IsValid = true; return m_File.good(); } bool InputFile::IsValid() const { return m_IsValid && m_File.good(); } bool InputFile::IsCached() const { return m_IsCached; } mpt::PathString InputFile::GetFilename() const { return m_Filename; } std::istream& InputFile::GetStream() { MPT_ASSERT(!m_IsCached); return m_File; } mpt::const_byte_span InputFile::GetCache() { MPT_ASSERT(m_IsCached); return mpt::as_span(m_Cache); } #if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS OnDiskFileWrapper::OnDiskFileWrapper(FileCursor &file, const mpt::PathString &fileNameExtension) : m_IsTempFile(false) { try { file.Rewind(); if(!file.GetOptionalFileName()) { const mpt::PathString tempName = mpt::CreateTempFileName(P_("OpenMPT"), fileNameExtension); #if MPT_OS_WINDOWS && MPT_OS_WINDOWS_WINRT #if (_WIN32_WINNT < 0x0602) #define MPT_ONDISKFILEWRAPPER_NO_CREATEFILE #endif #endif #ifdef MPT_ONDISKFILEWRAPPER_NO_CREATEFILE mpt::ofstream f(tempName, std::ios::binary); if(!f) { throw std::runtime_error("Error creating temporary file."); } while(!file.EndOfFile()) { FileCursor::PinnedView view = file.ReadPinnedView(mpt::IO::BUFFERSIZE_NORMAL); std::size_t towrite = view.size(); std::size_t written = 0; do { std::size_t chunkSize = mpt::saturate_cast(towrite); bool chunkOk = false; chunkOk = mpt::IO::WriteRaw(f, mpt::const_byte_span(view.data() + written, chunkSize)); if(!chunkOk) { throw std::runtime_error("Incomplete Write."); } towrite -= chunkSize; written += chunkSize; } while(towrite > 0); } f.close(); #else // !MPT_ONDISKFILEWRAPPER_NO_CREATEFILE HANDLE hFile = NULL; #if MPT_OS_WINDOWS_WINRT hFile = mpt::windows::CheckFileHANDLE(CreateFile2(tempName.AsNative().c_str(), GENERIC_WRITE, FILE_SHARE_READ, CREATE_ALWAYS, NULL)); #else hFile = mpt::windows::CheckFileHANDLE(CreateFile(tempName.AsNative().c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, NULL)); #endif while(!file.EndOfFile()) { FileCursor::PinnedView view = file.ReadPinnedView(mpt::IO::BUFFERSIZE_NORMAL); std::size_t towrite = view.size(); std::size_t written = 0; do { DWORD chunkSize = mpt::saturate_cast(towrite); DWORD chunkDone = 0; try { mpt::windows::CheckBOOL(WriteFile(hFile, view.data() + written, chunkSize, &chunkDone, NULL)); } catch(...) { CloseHandle(hFile); hFile = NULL; throw; } if(chunkDone != chunkSize) { CloseHandle(hFile); hFile = NULL; throw std::runtime_error("Incomplete WriteFile()."); } towrite -= chunkDone; written += chunkDone; } while(towrite > 0); } CloseHandle(hFile); hFile = NULL; #endif // MPT_ONDISKFILEWRAPPER_NO_CREATEFILE m_Filename = tempName; m_IsTempFile = true; } else { m_Filename = file.GetOptionalFileName().value(); } } catch (const std::runtime_error &) { m_IsTempFile = false; m_Filename = mpt::PathString(); } } OnDiskFileWrapper::~OnDiskFileWrapper() { if(m_IsTempFile) { DeleteFile(m_Filename.AsNative().c_str()); m_IsTempFile = false; } m_Filename = mpt::PathString(); } bool OnDiskFileWrapper::IsValid() const { return !m_Filename.empty(); } mpt::PathString OnDiskFileWrapper::GetFilename() const { return m_Filename; } #endif // MODPLUG_TRACKER && MPT_OS_WINDOWS #else // !MPT_ENABLE_FILEIO MPT_MSVC_WORKAROUND_LNK4221(mptFileIO) #endif // MPT_ENABLE_FILEIO OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/common/mptFileIO.h0000644000175000017500000002465714056704427017574 00000000000000/* * mptFileIO.h * ----------- * Purpose: A wrapper around std::fstream, enforcing usage of mpt::PathString. * Notes : You should only ever use these wrappers instead of plain std::fstream classes. * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #if defined(MPT_ENABLE_FILEIO) #include "mpt/io_read/filecursor_memory.hpp" #include "mpt/io_read/filecursor_stdstream.hpp" #include "../common/mptString.h" #include "../common/mptPathString.h" #include "../common/FileReaderFwd.h" #if defined(MPT_COMPILER_QUIRK_WINDOWS_FSTREAM_NO_WCHAR) #if MPT_GCC_AT_LEAST(9,1,0) #include #endif // MPT_GCC_AT_LEAST(9,1,0) #endif // MPT_COMPILER_QUIRK_WINDOWS_FSTREAM_NO_WCHAR #include #include #include #include #include #if MPT_COMPILER_MSVC #include #endif // !MPT_COMPILER_MSVC #ifdef MODPLUG_TRACKER #if MPT_OS_WINDOWS #include #endif // MPT_OS_WINDOWS #endif // MODPLUG_TRACKER #endif // MPT_ENABLE_FILEIO OPENMPT_NAMESPACE_BEGIN #if defined(MPT_ENABLE_FILEIO) // Sets the NTFS compression attribute on the file or directory. // Requires read and write permissions for already opened files. // Returns true if the attribute has been set. // In almost all cases, the return value should be ignored because most filesystems other than NTFS do not support compression. #ifdef MODPLUG_TRACKER #if MPT_OS_WINDOWS bool SetFilesystemCompression(HANDLE hFile); bool SetFilesystemCompression(int fd); bool SetFilesystemCompression(const mpt::PathString &filename); #endif // MPT_OS_WINDOWS #endif // MODPLUG_TRACKER namespace mpt { namespace detail { template inline void fstream_open(Tbase & base, const mpt::PathString & filename, std::ios_base::openmode mode) { #if defined(MPT_COMPILER_QUIRK_WINDOWS_FSTREAM_NO_WCHAR) #if MPT_GCC_AT_LEAST(9,1,0) base.open(static_cast(filename.AsNative()), mode); #else // !MPT_GCC_AT_LEAST(9,1,0) // Warning: MinGW with GCC earlier than 9.1 detected. Standard library does neither provide std::fstream wchar_t overloads nor std::filesystem with wchar_t support. Unicode filename support is thus unavailable. base.open(mpt::ToCharset(mpt::Charset::Locale, filename.AsNative()).c_str(), mode); #endif // MPT_GCC_AT_LEAST(9,1,0) #else // !MPT_COMPILER_QUIRK_WINDOWS_FSTREAM_NO_WCHAR base.open(filename.AsNativePrefixed().c_str(), mode); #endif // MPT_COMPILER_QUIRK_WINDOWS_FSTREAM_NO_WCHAR } } // namespace detail // We cannot rely on implicit conversion of mpt::PathString to std::filesystem::path when constructing std::fstream // because of broken overload implementation in GCC libstdc++ 8, 9, 10. // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95642 // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90704 class fstream : public std::fstream { private: typedef std::fstream Tbase; public: fstream() {} fstream(const mpt::PathString & filename, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) { detail::fstream_open(*this, filename, mode); } #if MPT_COMPILER_MSVC protected: fstream(std::FILE * file) : std::fstream(file) { } #endif // MPT_COMPILER_MSVC public: void open(const mpt::PathString & filename, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) { detail::fstream_open(*this, filename, mode); } void open(const char * filename, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) = delete; void open(const std::string & filename, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) = delete; #if MPT_OS_WINDOWS void open(const wchar_t * filename, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) = delete; void open(const std::wstring & filename, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) = delete; #endif }; class ifstream : public std::ifstream { private: typedef std::ifstream Tbase; public: ifstream() {} ifstream(const mpt::PathString & filename, std::ios_base::openmode mode = std::ios_base::in) { detail::fstream_open(*this, filename, mode); } #if MPT_COMPILER_MSVC protected: ifstream(std::FILE * file) : std::ifstream(file) { } #endif // MPT_COMPILER_MSVC public: void open(const mpt::PathString & filename, std::ios_base::openmode mode = std::ios_base::in) { detail::fstream_open(*this, filename, mode); } void open(const char * filename, std::ios_base::openmode mode = std::ios_base::in) = delete; void open(const std::string & filename, std::ios_base::openmode mode = std::ios_base::in) = delete; #if MPT_OS_WINDOWS void open(const wchar_t * filename, std::ios_base::openmode mode = std::ios_base::in) = delete; void open(const std::wstring & filename, std::ios_base::openmode mode = std::ios_base::in) = delete; #endif }; class ofstream : public std::ofstream { private: typedef std::ofstream Tbase; public: ofstream() {} ofstream(const mpt::PathString & filename, std::ios_base::openmode mode = std::ios_base::out) { detail::fstream_open(*this, filename, mode); } #if MPT_COMPILER_MSVC protected: ofstream(std::FILE * file) : std::ofstream(file) { } #endif // MPT_COMPILER_MSVC public: void open(const mpt::PathString & filename, std::ios_base::openmode mode = std::ios_base::out) { detail::fstream_open(*this, filename, mode); } void open(const char * filename, std::ios_base::openmode mode = std::ios_base::out) = delete; void open(const std::string & filename, std::ios_base::openmode mode = std::ios_base::out) = delete; #if MPT_OS_WINDOWS void open(const wchar_t * filename, std::ios_base::openmode mode = std::ios_base::out) = delete; void open(const std::wstring & filename, std::ios_base::openmode mode = std::ios_base::out) = delete; #endif }; enum class FlushMode { None = 0, // no explicit flushes at all Single = 1, // explicitly flush higher-leverl API layers Full = 2, // explicitly flush *all* layers, up to and including disk write caches }; inline FlushMode FlushModeFromBool(bool flush) { return flush ? FlushMode::Full : FlushMode::None; } #ifdef MODPLUG_TRACKER class SafeOutputFile { private: FlushMode m_FlushMode; #if MPT_COMPILER_MSVC std::FILE *m_f = nullptr; #else // !MPT_COMPILER_MSVC mpt::ofstream m_s; #endif // MPT_COMPILER_MSVC #if MPT_COMPILER_MSVC class FILEostream : public mpt::ofstream { public: FILEostream(std::FILE * file) : mpt::ofstream(file) { return; } }; FILEostream m_s; static mpt::tstring convert_mode(std::ios_base::openmode mode, FlushMode flushMode); std::FILE * internal_fopen(const mpt::PathString &filename, std::ios_base::openmode mode, FlushMode flushMode); #endif // MPT_COMPILER_MSVC public: SafeOutputFile() = delete; explicit SafeOutputFile(const mpt::PathString &filename, std::ios_base::openmode mode = std::ios_base::out, FlushMode flushMode = FlushMode::Full) : m_FlushMode(flushMode) #if MPT_COMPILER_MSVC , m_s(internal_fopen(filename, mode | std::ios_base::out, flushMode)) #else // !MPT_COMPILER_MSVC , m_s(filename, mode) #endif // MPT_COMPILER_MSVC { if(!stream().is_open()) { stream().setstate(mpt::ofstream::failbit); } } mpt::ofstream& stream() { return m_s; } operator mpt::ofstream& () { return stream(); } const mpt::ofstream& stream() const { return m_s; } operator const mpt::ofstream& () const { return stream(); } operator bool() const { return stream() ? true : false; } bool operator!() const { return stream().operator!(); } ~SafeOutputFile() noexcept(false); }; #endif // MODPLUG_TRACKER #ifdef MODPLUG_TRACKER // LazyFileRef is a simple reference to an on-disk file by the means of a // filename which allows easy assignment of the whole file contents to and from // byte buffers. class LazyFileRef { private: const mpt::PathString m_Filename; public: LazyFileRef(const mpt::PathString &filename) : m_Filename(filename) { return; } public: LazyFileRef & operator = (const std::vector &data); LazyFileRef & operator = (const std::vector &data); LazyFileRef & operator = (const std::string &data); operator std::vector () const; operator std::vector () const; operator std::string () const; }; #endif // MODPLUG_TRACKER } // namespace mpt class InputFile { private: mpt::PathString m_Filename; mpt::ifstream m_File; bool m_IsValid; bool m_IsCached; std::vector m_Cache; public: InputFile(const mpt::PathString &filename, bool allowWholeFileCaching = false); ~InputFile(); bool IsValid() const; bool IsCached() const; mpt::PathString GetFilename() const; std::istream& GetStream(); mpt::const_byte_span GetCache(); private: bool Open(const mpt::PathString &filename, bool allowWholeFileCaching = false); }; template inline FileCursor make_FileCursor(Targ1 &&arg1) { return mpt::IO::make_FileCursor(std::forward(arg1)); } template inline FileCursor make_FileCursor(Targ1 &&arg1, Targ2 &&arg2) { return mpt::IO::make_FileCursor(std::forward(arg1), std::forward(arg2)); } // templated in order to reduce header inter-dependencies class InputFile; template ::value, bool> = true> inline FileCursor make_FileCursor(TInputFile &file) { if(!file.IsValid()) { return FileCursor(); } if(file.IsCached()) { return mpt::IO::make_FileCursor(file.GetCache(), std::make_shared(file.GetFilename())); } else { return mpt::IO::make_FileCursor(file.GetStream(), std::make_shared(file.GetFilename())); } } template inline FileCursor GetFileReader(Targ1 &&arg1) { return make_FileCursor(std::forward(arg1)); } template inline FileCursor GetFileReader(Targ1 &&arg1, Targ2 &&arg2) { return make_FileCursor(std::forward(arg1), std::forward(arg2)); } #if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS class OnDiskFileWrapper { private: mpt::PathString m_Filename; bool m_IsTempFile; public: OnDiskFileWrapper(FileCursor& file, const mpt::PathString& fileNameExtension = P_("tmp")); ~OnDiskFileWrapper(); public: bool IsValid() const; mpt::PathString GetFilename() const; }; // class OnDiskFileWrapper #endif // MODPLUG_TRACKER && MPT_OS_WINDOWS #endif // MPT_ENABLE_FILEIO OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/common/mptPathString.cpp0000644000175000017500000004534514142754023021071 00000000000000/* * mptPathString.cpp * ----------------- * Purpose: Wrapper class around the platform-native representation of path names. Should be the only type that is used to store path names. * Notes : Currently none. * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "mptPathString.h" #include "mpt/uuid/uuid.hpp" #include "misc_util.h" #include "mptRandom.h" #if MPT_OS_WINDOWS #include #if defined(MODPLUG_TRACKER) #include #endif #include #endif OPENMPT_NAMESPACE_BEGIN #if MPT_OS_WINDOWS namespace mpt { RawPathString PathString::AsNativePrefixed() const { #if MPT_OS_WINDOWS_WINRT && (_WIN32_WINNT < 0x0a00) // For WinRT on Windows 8, there is no official wy to determine an absolute path. return path; #else if(path.length() < MAX_PATH || path.substr(0, 4) == PL_("\\\\?\\")) { // Path is short enough or already in prefixed form return path; } const RawPathString absPath = mpt::GetAbsolutePath(*this).AsNative(); if(absPath.substr(0, 2) == PL_("\\\\")) { // Path is a network share: \\server\foo.bar -> \\?\UNC\server\foo.bar return PL_("\\\\?\\UNC") + absPath.substr(1); } else { // Regular file: C:\foo.bar -> \\?\C:\foo.bar return PL_("\\\\?\\") + absPath; } #endif } #if !MPT_OS_WINDOWS_WINRT int PathString::CompareNoCase(const PathString & a, const PathString & b) { return lstrcmpi(a.path.c_str(), b.path.c_str()); } #endif // !MPT_OS_WINDOWS_WINRT // Convert a path to its simplified form, i.e. remove ".\" and "..\" entries // Note: We use our own implementation as PathCanonicalize is limited to MAX_PATH // and unlimited versions are only available on Windows 8 and later. // Furthermore, we also convert forward-slashes to backslashes and always remove trailing slashes. PathString PathString::Simplify() const { if(path.empty()) return PathString(); std::vector components; RawPathString root; RawPathString::size_type startPos = 0; if(path.size() >= 2 && path[1] == PC_(':')) { // Drive letter root = path.substr(0, 2) + PC_('\\'); startPos = 2; } else if(path.substr(0, 2) == PL_("\\\\")) { // Network share root = PL_("\\\\"); startPos = 2; } else if(path.substr(0, 2) == PL_(".\\") || path.substr(0, 2) == PL_("./")) { // Special case for relative paths root = PL_(".\\"); startPos = 2; } else if(path.size() >= 1 && (path[0] == PC_('\\') || path[0] == PC_('/'))) { // Special case for relative paths root = PL_("\\"); startPos = 1; } while(startPos < path.size()) { auto pos = path.find_first_of(PL_("\\/"), startPos); if(pos == RawPathString::npos) pos = path.size(); mpt::RawPathString dir = path.substr(startPos, pos - startPos); if(dir == PL_("..")) { // Go back one directory if(!components.empty()) { components.pop_back(); } } else if(dir == PL_(".")) { // nop } else if(!dir.empty()) { components.push_back(std::move(dir)); } startPos = pos + 1; } RawPathString result = root; result.reserve(path.size()); for(const auto &component : components) { result += component + PL_("\\"); } if(!components.empty()) result.pop_back(); return mpt::PathString(result); } } // namespace mpt #endif // MPT_OS_WINDOWS namespace mpt { #if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS void PathString::SplitPath(PathString *drive, PathString *dir, PathString *fname, PathString *ext) const { // We cannot use CRT splitpath here, because: // * limited to _MAX_PATH or similar // * no support for UNC paths // * no support for \\?\ prefixed paths if(drive) *drive = mpt::PathString(); if(dir) *dir = mpt::PathString(); if(fname) *fname = mpt::PathString(); if(ext) *ext = mpt::PathString(); mpt::RawPathString p = path; // remove \\?\\ prefix if(p.substr(0, 8) == PL_("\\\\?\\UNC\\")) { p = PL_("\\\\") + p.substr(8); } else if(p.substr(0, 4) == PL_("\\\\?\\")) { p = p.substr(4); } if (p.length() >= 2 && ( p.substr(0, 2) == PL_("\\\\") || p.substr(0, 2) == PL_("\\/") || p.substr(0, 2) == PL_("/\\") || p.substr(0, 2) == PL_("//") )) { // UNC mpt::RawPathString::size_type first_slash = p.substr(2).find_first_of(PL_("\\/")); if(first_slash != mpt::RawPathString::npos) { mpt::RawPathString::size_type second_slash = p.substr(2 + first_slash + 1).find_first_of(PL_("\\/")); if(second_slash != mpt::RawPathString::npos) { if(drive) *drive = mpt::PathString::FromNative(p.substr(0, 2 + first_slash + 1 + second_slash)); p = p.substr(2 + first_slash + 1 + second_slash); } else { if(drive) *drive = mpt::PathString::FromNative(p); p = mpt::RawPathString(); } } else { if(drive) *drive = mpt::PathString::FromNative(p); p = mpt::RawPathString(); } } else { // local if(p.length() >= 2 && (p[1] == PC_(':'))) { if(drive) *drive = mpt::PathString::FromNative(p.substr(0, 2)); p = p.substr(2); } else { if(drive) *drive = mpt::PathString(); } } mpt::RawPathString::size_type last_slash = p.find_last_of(PL_("\\/")); if(last_slash != mpt::RawPathString::npos) { if(dir) *dir = mpt::PathString::FromNative(p.substr(0, last_slash + 1)); p = p.substr(last_slash + 1); } else { if(dir) *dir = mpt::PathString(); } mpt::RawPathString::size_type last_dot = p.find_last_of(PL_(".")); if(last_dot == mpt::RawPathString::npos) { if(fname) *fname = mpt::PathString::FromNative(p); if(ext) *ext = mpt::PathString(); } else if(last_dot == 0) { if(fname) *fname = mpt::PathString::FromNative(p); if(ext) *ext = mpt::PathString(); } else if(p == PL_(".") || p == PL_("..")) { if(fname) *fname = mpt::PathString::FromNative(p); if(ext) *ext = mpt::PathString(); } else { if(fname) *fname = mpt::PathString::FromNative(p.substr(0, last_dot)); if(ext) *ext = mpt::PathString::FromNative(p.substr(last_dot)); } } PathString PathString::GetDrive() const { PathString drive; SplitPath(&drive, nullptr, nullptr, nullptr); return drive; } PathString PathString::GetDir() const { PathString dir; SplitPath(nullptr, &dir, nullptr, nullptr); return dir; } PathString PathString::GetPath() const { PathString drive, dir; SplitPath(&drive, &dir, nullptr, nullptr); return drive + dir; } PathString PathString::GetFileName() const { PathString fname; SplitPath(nullptr, nullptr, &fname, nullptr); return fname; } PathString PathString::GetFileExt() const { PathString ext; SplitPath(nullptr, nullptr, nullptr, &ext); return ext; } PathString PathString::GetFullFileName() const { PathString name, ext; SplitPath(nullptr, nullptr, &name, &ext); return name + ext; } bool PathString::IsDirectory() const { // Using PathIsDirectoryW here instead would increase libopenmpt dependencies by shlwapi.dll. // GetFileAttributesW also does the job just fine. #if MPT_OS_WINDOWS_WINRT WIN32_FILE_ATTRIBUTE_DATA data = {}; if(::GetFileAttributesExW(path.c_str(), GetFileExInfoStandard, &data) == 0) { return false; } DWORD dwAttrib = data.dwFileAttributes; #else // !MPT_OS_WINDOWS_WINRT DWORD dwAttrib = ::GetFileAttributes(path.c_str()); #endif // MPT_OS_WINDOWS_WINRT return ((dwAttrib != INVALID_FILE_ATTRIBUTES) && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); } bool PathString::IsFile() const { #if MPT_OS_WINDOWS_WINRT WIN32_FILE_ATTRIBUTE_DATA data = {}; if (::GetFileAttributesExW(path.c_str(), GetFileExInfoStandard, &data) == 0) { return false; } DWORD dwAttrib = data.dwFileAttributes; #else // !MPT_OS_WINDOWS_WINRT DWORD dwAttrib = ::GetFileAttributes(path.c_str()); #endif // MPT_OS_WINDOWS_WINRT return ((dwAttrib != INVALID_FILE_ATTRIBUTES) && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); } #endif // MODPLUG_TRACKER && MPT_OS_WINDOWS #if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS bool PathString::FileOrDirectoryExists() const { return ::PathFileExists(path.c_str()) != FALSE; } #endif // MODPLUG_TRACKER && MPT_OS_WINDOWS #if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS PathString PathString::ReplaceExt(const mpt::PathString &newExt) const { return GetDrive() + GetDir() + GetFileName() + newExt; } PathString PathString::SanitizeComponent() const { PathString result = *this; SanitizeFilename(result); return result; } // Convert an absolute path to a path that's relative to "&relativeTo". PathString PathString::AbsolutePathToRelative(const PathString &relativeTo) const { mpt::PathString result = *this; if(path.empty()) { return result; } if(!_tcsncicmp(relativeTo.AsNative().c_str(), AsNative().c_str(), relativeTo.AsNative().length())) { // Path is OpenMPT's directory or a sub directory ("C:\OpenMPT\Somepath" => ".\Somepath") result = P_(".\\"); // ".\" result += mpt::PathString::FromNative(AsNative().substr(relativeTo.AsNative().length())); } else if(!_tcsncicmp(relativeTo.AsNative().c_str(), AsNative().c_str(), 2)) { // Path is on the same drive as OpenMPT ("C:\Somepath" => "\Somepath") result = mpt::PathString::FromNative(AsNative().substr(2)); } return result; } // Convert a path that is relative to "&relativeTo" to an absolute path. PathString PathString::RelativePathToAbsolute(const PathString &relativeTo) const { mpt::PathString result = *this; if(path.empty()) { return result; } if(path.length() >= 2 && path[0] == PC_('\\') && path[1] != PC_('\\')) { // Path is on the same drive as OpenMPT ("\Somepath\" => "C:\Somepath\"), but ignore network paths starting with "\\" result = mpt::PathString::FromNative(relativeTo.AsNative().substr(0, 2)); result += mpt::PathString(path); } else if(path.length() >= 2 && path.substr(0, 2) == PL_(".\\")) { // Path is OpenMPT's directory or a sub directory (".\Somepath\" => "C:\OpenMPT\Somepath\") result = relativeTo; // "C:\OpenMPT\" result += mpt::PathString::FromNative(AsNative().substr(2)); } return result; } #endif // MODPLUG_TRACKER && MPT_OS_WINDOWS bool PathString::IsPathSeparator(RawPathString::value_type c) { #if MPT_OS_WINDOWS return (c == PC_('\\')) || (c == PC_('/')); #else return c == PC_('/'); #endif } RawPathString::value_type PathString::GetDefaultPathSeparator() { #if MPT_OS_WINDOWS return PC_('\\'); #else return PC_('/'); #endif } } // namespace mpt namespace mpt { bool PathIsAbsolute(const mpt::PathString &path) { mpt::RawPathString rawpath = path.AsNative(); #if MPT_OS_WINDOWS if(rawpath.substr(0, 8) == PL_("\\\\?\\UNC\\")) { return true; } if(rawpath.substr(0, 4) == PL_("\\\\?\\")) { return true; } if(rawpath.substr(0, 2) == PL_("\\\\")) { return true; // UNC } if(rawpath.substr(0, 2) == PL_("//")) { return true; // UNC } return (rawpath.length()) >= 3 && (rawpath[1] == ':') && mpt::PathString::IsPathSeparator(rawpath[2]); #else return (rawpath.length() >= 1) && mpt::PathString::IsPathSeparator(rawpath[0]); #endif } #if MPT_OS_WINDOWS #if !(MPT_OS_WINDOWS_WINRT && (_WIN32_WINNT < 0x0a00)) mpt::PathString GetAbsolutePath(const mpt::PathString &path) { DWORD size = GetFullPathName(path.AsNative().c_str(), 0, nullptr, nullptr); if(size == 0) { return path; } std::vector fullPathName(size, TEXT('\0')); if(GetFullPathName(path.AsNative().c_str(), size, fullPathName.data(), nullptr) == 0) { return path; } return mpt::PathString::FromNative(fullPathName.data()); } #endif #ifdef MODPLUG_TRACKER bool DeleteWholeDirectoryTree(mpt::PathString path) { if(path.AsNative().empty()) { return false; } if(PathIsRelative(path.AsNative().c_str()) == TRUE) { return false; } if(!path.FileOrDirectoryExists()) { return true; } if(!path.IsDirectory()) { return false; } path.EnsureTrailingSlash(); HANDLE hFind = NULL; WIN32_FIND_DATA wfd = {}; hFind = FindFirstFile((path + P_("*.*")).AsNative().c_str(), &wfd); if(hFind != NULL && hFind != INVALID_HANDLE_VALUE) { do { mpt::PathString filename = mpt::PathString::FromNative(wfd.cFileName); if(filename != P_(".") && filename != P_("..")) { filename = path + filename; if(filename.IsDirectory()) { if(!DeleteWholeDirectoryTree(filename)) { return false; } } else if(filename.IsFile()) { if(DeleteFile(filename.AsNative().c_str()) == 0) { return false; } } } } while(FindNextFile(hFind, &wfd)); FindClose(hFind); } if(RemoveDirectory(path.AsNative().c_str()) == 0) { return false; } return true; } #endif // MODPLUG_TRACKER #endif // MPT_OS_WINDOWS #if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS mpt::PathString GetExecutablePath() { std::vector exeFileName(MAX_PATH); while(GetModuleFileName(0, exeFileName.data(), mpt::saturate_cast(exeFileName.size())) >= exeFileName.size()) { if(GetLastError() != ERROR_INSUFFICIENT_BUFFER) { return mpt::PathString(); } exeFileName.resize(exeFileName.size() * 2); } return mpt::GetAbsolutePath(mpt::PathString::FromNative(exeFileName.data()).GetPath()); } #if !MPT_OS_WINDOWS_WINRT mpt::PathString GetSystemPath() { DWORD size = GetSystemDirectory(nullptr, 0); std::vector path(size + 1); if(!GetSystemDirectory(path.data(), size + 1)) { return mpt::PathString(); } return mpt::PathString::FromNative(path.data()) + P_("\\"); } #endif // !MPT_OS_WINDOWS_WINRT #endif // MODPLUG_TRACKER && MPT_OS_WINDOWS #if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS mpt::PathString GetTempDirectory() { DWORD size = GetTempPath(0, nullptr); if(size) { std::vector tempPath(size + 1); if(GetTempPath(size + 1, tempPath.data())) { return mpt::PathString::FromNative(tempPath.data()); } } // use exe directory as fallback return mpt::GetExecutablePath(); } mpt::PathString CreateTempFileName(const mpt::PathString &fileNamePrefix, const mpt::PathString &fileNameExtension) { mpt::PathString filename = mpt::GetTempDirectory(); filename += (!fileNamePrefix.empty() ? fileNamePrefix + P_("_") : mpt::PathString()); filename += mpt::PathString::FromUnicode(mpt::UUID::GenerateLocalUseOnly(mpt::global_prng()).ToUString()); filename += (!fileNameExtension.empty() ? P_(".") + fileNameExtension : mpt::PathString()); return filename; } TempFileGuard::TempFileGuard(const mpt::PathString &filename) : filename(filename) { return; } mpt::PathString TempFileGuard::GetFilename() const { return filename; } TempFileGuard::~TempFileGuard() { if(!filename.empty()) { DeleteFile(filename.AsNative().c_str()); } } TempDirGuard::TempDirGuard(const mpt::PathString &dirname_) : dirname(dirname_.WithTrailingSlash()) { if(dirname.empty()) { return; } if(::CreateDirectory(dirname.AsNative().c_str(), NULL) == 0) { // fail dirname = mpt::PathString(); } } mpt::PathString TempDirGuard::GetDirname() const { return dirname; } TempDirGuard::~TempDirGuard() { if(!dirname.empty()) { DeleteWholeDirectoryTree(dirname); } } #endif // MODPLUG_TRACKER && MPT_OS_WINDOWS } // namespace mpt #if defined(MODPLUG_TRACKER) static inline char SanitizeFilenameChar(char c) { if( c == '\\' || c == '\"' || c == '/' || c == ':' || c == '?' || c == '<' || c == '>' || c == '|' || c == '*') { c = '_'; } return c; } static inline wchar_t SanitizeFilenameChar(wchar_t c) { if( c == L'\\' || c == L'\"' || c == L'/' || c == L':' || c == L'?' || c == L'<' || c == L'>' || c == L'|' || c == L'*') { c = L'_'; } return c; } #if MPT_CXX_AT_LEAST(20) static inline char8_t SanitizeFilenameChar(char8_t c) { if( c == u8'\\' || c == u8'\"' || c == u8'/' || c == u8':' || c == u8'?' || c == u8'<' || c == u8'>' || c == u8'|' || c == u8'*') { c = u8'_'; } return c; } #endif void SanitizeFilename(mpt::PathString &filename) { mpt::RawPathString tmp = filename.AsNative(); for(auto &c : tmp) { c = SanitizeFilenameChar(c); } filename = mpt::PathString::FromNative(tmp); } void SanitizeFilename(char *beg, char *end) { for(char *it = beg; it != end; ++it) { *it = SanitizeFilenameChar(*it); } } void SanitizeFilename(wchar_t *beg, wchar_t *end) { for(wchar_t *it = beg; it != end; ++it) { *it = SanitizeFilenameChar(*it); } } void SanitizeFilename(std::string &str) { for(size_t i = 0; i < str.length(); i++) { str[i] = SanitizeFilenameChar(str[i]); } } void SanitizeFilename(std::wstring &str) { for(size_t i = 0; i < str.length(); i++) { str[i] = SanitizeFilenameChar(str[i]); } } #if MPT_USTRING_MODE_UTF8 void SanitizeFilename(mpt::u8string &str) { for(size_t i = 0; i < str.length(); i++) { str[i] = SanitizeFilenameChar(str[i]); } } #endif // MPT_USTRING_MODE_UTF8 #if defined(MPT_WITH_MFC) void SanitizeFilename(CString &str) { for(int i = 0; i < str.GetLength(); i++) { str.SetAt(i, SanitizeFilenameChar(str.GetAt(i))); } } #endif // MPT_WITH_MFC #endif // MODPLUG_TRACKER #if defined(MODPLUG_TRACKER) mpt::PathString FileType::AsFilterString(FlagSet format) const { mpt::PathString filter; if(GetShortName().empty() || GetExtensions().empty()) { return filter; } if(!GetDescription().empty()) { filter += mpt::PathString::FromUnicode(GetDescription()); } else { filter += mpt::PathString::FromUnicode(GetShortName()); } const auto extensions = GetExtensions(); if(format[FileTypeFormatShowExtensions]) { filter += P_(" ("); bool first = true; for(const auto &ext : extensions) { if(first) { first = false; } else { filter += P_(","); } filter += P_("*."); filter += ext; } filter += P_(")"); } filter += P_("|"); { bool first = true; for(const auto &ext : extensions) { if(first) { first = false; } else { filter += P_(";"); } filter += P_("*."); filter += ext; } } filter += P_("|"); return filter; } mpt::PathString FileType::AsFilterOnlyString() const { mpt::PathString filter; const auto extensions = GetExtensions(); { bool first = true; for(const auto &ext : extensions) { if(first) { first = false; } else { filter += P_(";"); } filter += P_("*."); filter += ext; } } return filter; } mpt::PathString ToFilterString(const FileType &fileType, FlagSet format) { return fileType.AsFilterString(format); } mpt::PathString ToFilterString(const std::vector &fileTypes, FlagSet format) { mpt::PathString filter; for(const auto &type : fileTypes) { filter += type.AsFilterString(format); } return filter; } mpt::PathString ToFilterOnlyString(const FileType &fileType, bool prependSemicolonWhenNotEmpty) { mpt::PathString filter = fileType.AsFilterOnlyString(); return filter.empty() ? filter : (prependSemicolonWhenNotEmpty ? P_(";") : P_("")) + filter; } mpt::PathString ToFilterOnlyString(const std::vector &fileTypes, bool prependSemicolonWhenNotEmpty) { mpt::PathString filter; for(const auto &type : fileTypes) { filter += type.AsFilterOnlyString(); } return filter.empty() ? filter : (prependSemicolonWhenNotEmpty ? P_(";") : P_("")) + filter; } #endif // MODPLUG_TRACKER OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/common/mptPathString.h0000644000175000017500000003773014145000643020530 00000000000000/* * mptPathString.h * --------------- * Purpose: Wrapper class around the platform-native representation of path names. Should be the only type that is used to store path names. * Notes : Currently none. * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "mptString.h" #include "mpt/base/namespace.hpp" #include #include "openmpt/base/FlagSet.hpp" #define MPT_DEPRECATED_PATH //#define MPT_DEPRECATED_PATH [[deprecated]] OPENMPT_NAMESPACE_BEGIN namespace mpt { #if MPT_OS_WINDOWS typedef mpt::winstring RawPathString; #else // !MPT_OS_WINDOWS typedef std::string RawPathString; #endif // if MPT_OS_WINDOWS class PathString { private: RawPathString path; private: explicit PathString(const RawPathString & path_) : path(path_) { return; } public: PathString() { return; } PathString(const PathString & other) : path(other.path) { return; } PathString(PathString && other) noexcept : path(std::move(other.path)) { return; } PathString & assign(const PathString & other) { path = other.path; return *this; } PathString & assign(PathString && other) noexcept { path = std::move(other.path); return *this; } PathString & operator = (const PathString & other) { return assign(other); } PathString &operator = (PathString && other) noexcept { return assign(std::move(other)); } PathString & append(const PathString & other) { path.append(other.path); return *this; } PathString & operator += (const PathString & other) { return append(other); } friend PathString operator + (const PathString & a, const PathString & b) { return PathString(a).append(b); } friend bool operator < (const PathString & a, const PathString & b) { return a.AsNative() < b.AsNative(); } friend bool operator == (const PathString & a, const PathString & b) { return a.AsNative() == b.AsNative(); } friend bool operator != (const PathString & a, const PathString & b) { return a.AsNative() != b.AsNative(); } bool empty() const { return path.empty(); } std::size_t Length() const { return path.size(); } public: #if MPT_OS_WINDOWS #if !MPT_OS_WINDOWS_WINRT static int CompareNoCase(const PathString & a, const PathString & b); #endif // !MPT_OS_WINDOWS_WINRT #endif #if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS void SplitPath(PathString *drive, PathString *dir, PathString *fname, PathString *ext) const; // \\?\ prefixes will be removed and \\?\\UNC prefixes converted to canonical \\ form. PathString GetDrive() const; // Drive letter + colon, e.g. "C:" or \\server\\share PathString GetDir() const; // Directory, e.g. "\OpenMPT\" PathString GetPath() const; // Drive + Dir, e.g. "C:\OpenMPT\" PathString GetFileName() const; // File name without extension, e.g. "OpenMPT" PathString GetFileExt() const; // Extension including dot, e.g. ".exe" PathString GetFullFileName() const; // File name + extension, e.g. "OpenMPT.exe" // Verify if this path represents a valid directory on the file system. bool IsDirectory() const; // Verify if this path exists and is a file on the file system. bool IsFile() const; bool FileOrDirectoryExists() const; #endif // MODPLUG_TRACKER && MPT_OS_WINDOWS static bool IsPathSeparator(RawPathString::value_type c); static RawPathString::value_type GetDefaultPathSeparator(); #if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS // Return the same path string with a different (or appended) extension (including "."), e.g. "foo.bar",".txt" -> "foo.txt" or "C:\OpenMPT\foo",".txt" -> "C:\OpenMPT\foo.txt" PathString ReplaceExt(const mpt::PathString &newExt) const; // Removes special characters from a filename component and replaces them with a safe replacement character ("_" on windows). // Returns the result. // Note that this also removes path component separators, so this should only be used on single-component PathString objects. PathString SanitizeComponent() const; bool HasTrailingSlash() const { if(path.empty()) { return false; } RawPathString::value_type c = path[path.length() - 1]; return IsPathSeparator(c); } mpt::PathString &EnsureTrailingSlash() { if(!path.empty() && !HasTrailingSlash()) { path += GetDefaultPathSeparator(); } return *this; } mpt::PathString WithoutTrailingSlash() const { mpt::PathString result = *this; while(result.HasTrailingSlash()) { if(result.Length() == 1) { return result; } result = mpt::PathString(result.AsNative().substr(0, result.AsNative().length() - 1)); } return result; } mpt::PathString WithTrailingSlash() const { mpt::PathString result = *this; result.EnsureTrailingSlash(); return result; } // Relative / absolute paths conversion mpt::PathString AbsolutePathToRelative(const mpt::PathString &relativeTo) const; mpt::PathString RelativePathToAbsolute(const mpt::PathString &relativeTo) const; #endif // MODPLUG_TRACKER && MPT_OS_WINDOWS public: #if MPT_OS_WINDOWS #if !(MPT_WSTRING_CONVERT) #error "mpt::PathString on Windows depends on MPT_WSTRING_CONVERT)" #endif // conversions #if defined(MPT_ENABLE_CHARSET_LOCALE) MPT_DEPRECATED_PATH std::string ToLocale() const { return mpt::ToCharset(mpt::Charset::Locale, path); } #endif std::string ToUTF8() const { return mpt::ToCharset(mpt::Charset::UTF8, path); } std::wstring ToWide() const { return mpt::ToWide(path); } mpt::ustring ToUnicode() const { return mpt::ToUnicode(path); } #if defined(MPT_ENABLE_CHARSET_LOCALE) MPT_DEPRECATED_PATH static PathString FromLocale(const std::string &path) { return PathString(mpt::ToWin(mpt::Charset::Locale, path)); } static PathString FromLocaleSilent(const std::string &path) { return PathString(mpt::ToWin(mpt::Charset::Locale, path)); } #endif static PathString FromUTF8(const std::string &path) { return PathString(mpt::ToWin(mpt::Charset::UTF8, path)); } static PathString FromWide(const std::wstring &path) { return PathString(mpt::ToWin(path)); } static PathString FromUnicode(const mpt::ustring &path) { return PathString(mpt::ToWin(path)); } RawPathString AsNative() const { return path; } // Return native string, with possible \\?\ prefix if it exceeds MAX_PATH characters. RawPathString AsNativePrefixed() const; static PathString FromNative(const RawPathString &path) { return PathString(path); } #if defined(MPT_WITH_MFC) // CString TCHAR, so this is CHAR or WCHAR, depending on UNICODE CString ToCString() const { return mpt::ToCString(path); } static PathString FromCString(const CString &path) { return PathString(mpt::ToWin(path)); } #endif // MPT_WITH_MFC // Convert a path to its simplified form, i.e. remove ".\" and "..\" entries mpt::PathString Simplify() const; #else // !MPT_OS_WINDOWS // conversions #if defined(MPT_ENABLE_CHARSET_LOCALE) std::string ToLocale() const { return path; } std::string ToUTF8() const { return mpt::ToCharset(mpt::Charset::UTF8, mpt::Charset::Locale, path); } #if MPT_WSTRING_CONVERT std::wstring ToWide() const { return mpt::ToWide(mpt::Charset::Locale, path); } #endif mpt::ustring ToUnicode() const { return mpt::ToUnicode(mpt::Charset::Locale, path); } static PathString FromLocale(const std::string &path) { return PathString(path); } static PathString FromLocaleSilent(const std::string &path) { return PathString(path); } static PathString FromUTF8(const std::string &path) { return PathString(mpt::ToCharset(mpt::Charset::Locale, mpt::Charset::UTF8, path)); } #if MPT_WSTRING_CONVERT static PathString FromWide(const std::wstring &path) { return PathString(mpt::ToCharset(mpt::Charset::Locale, path)); } #endif static PathString FromUnicode(const mpt::ustring &path) { return PathString(mpt::ToCharset(mpt::Charset::Locale, path)); } RawPathString AsNative() const { return path; } RawPathString AsNativePrefixed() const { return path; } static PathString FromNative(const RawPathString &path) { return PathString(path); } #else // !MPT_ENABLE_CHARSET_LOCALE std::string ToUTF8() const { return path; } #if MPT_WSTRING_CONVERT std::wstring ToWide() const { return mpt::ToWide(mpt::Charset::UTF8, path); } #endif mpt::ustring ToUnicode() const { return mpt::ToUnicode(mpt::Charset::UTF8, path); } static PathString FromUTF8(const std::string &path) { return PathString(path); } #if MPT_WSTRING_CONVERT static PathString FromWide(const std::wstring &path) { return PathString(mpt::ToCharset(mpt::Charset::UTF8, path)); } #endif static PathString FromUnicode(const mpt::ustring &path) { return PathString(mpt::ToCharset(mpt::Charset::UTF8, path)); } RawPathString AsNative() const { return path; } RawPathString AsNativePrefixed() const { return path; } static PathString FromNative(const RawPathString &path) { return PathString(path); } #endif // MPT_ENABLE_CHARSET_LOCALE // Convert a path to its simplified form (currently only implemented on Windows) [[deprecated]] mpt::PathString Simplify() const { return PathString(path); } #endif // MPT_OS_WINDOWS }; #if defined(MPT_ENABLE_CHARSET_LOCALE) #if MPT_OS_WINDOWS #ifdef UNICODE [[deprecated]] inline std::string ToAString(const mpt::PathString & x) { return mpt::ToCharset(mpt::Charset::Locale, x.ToUnicode()); } #else MPT_DEPRECATED_PATH inline std::string ToAString(const mpt::PathString & x) { return mpt::ToCharset(mpt::Charset::Locale, x.AsNative()); } #endif #else MPT_DEPRECATED_PATH inline std::string ToAString(const mpt::PathString & x) { return mpt::ToCharset(mpt::Charset::Locale, x.ToUnicode()); } #endif #endif inline mpt::ustring ToUString(const mpt::PathString & x) { return x.ToUnicode(); } #if MPT_WSTRING_FORMAT inline std::wstring ToWString(const mpt::PathString & x) { return x.ToWide(); } #endif } // namespace mpt #if MPT_OS_WINDOWS #ifdef UNICODE #define MPT_PATHSTRING_LITERAL(x) ( L ## x ) #define MPT_PATHSTRING(x) mpt::PathString::FromNative( L ## x ) #else #define MPT_PATHSTRING_LITERAL(x) ( x ) #define MPT_PATHSTRING(x) mpt::PathString::FromNative( x ) #endif #else // !MPT_OS_WINDOWS #define MPT_PATHSTRING_LITERAL(x) ( x ) #define MPT_PATHSTRING(x) mpt::PathString::FromNative( x ) #endif // MPT_OS_WINDOWS #define PC_(x) MPT_PATHSTRING_LITERAL(x) #define PL_(x) MPT_PATHSTRING_LITERAL(x) #define P_(x) MPT_PATHSTRING(x) namespace mpt { bool PathIsAbsolute(const mpt::PathString &path); #if MPT_OS_WINDOWS #if !(MPT_OS_WINDOWS_WINRT && (_WIN32_WINNT < 0x0a00)) // Returns the absolute path for a potentially relative path and removes ".." or "." components. (same as GetFullPathNameW) mpt::PathString GetAbsolutePath(const mpt::PathString &path); #endif #ifdef MODPLUG_TRACKER // Deletes a complete directory tree. Handle with EXTREME care. // Returns false if any file could not be removed and aborts as soon as it // encounters any error. path must be absolute. bool DeleteWholeDirectoryTree(mpt::PathString path); #endif // MODPLUG_TRACKER #endif // MPT_OS_WINDOWS #if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS // Returns the application executable path or an empty string (if unknown), e.g. "C:\mptrack\" mpt::PathString GetExecutablePath(); #if !MPT_OS_WINDOWS_WINRT // Returns the system directory path, e.g. "C:\Windows\System32\" mpt::PathString GetSystemPath(); #endif // !MPT_OS_WINDOWS_WINRT #endif // MODPLUG_TRACKER && MPT_OS_WINDOWS #if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS // Returns temporary directory (with trailing backslash added) (e.g. "C:\TEMP\") mpt::PathString GetTempDirectory(); // Returns a new unique absolute path. mpt::PathString CreateTempFileName(const mpt::PathString &fileNamePrefix = mpt::PathString(), const mpt::PathString &fileNameExtension = P_("tmp")); // Scoped temporary file guard. Deletes the file when going out of scope. // The file itself is not created automatically. class TempFileGuard { private: const mpt::PathString filename; public: TempFileGuard(const mpt::PathString &filename = CreateTempFileName()); mpt::PathString GetFilename() const; ~TempFileGuard(); }; // Scoped temporary directory guard. Deletes the directory when going out of scope. // The directory itself is created automatically. class TempDirGuard { private: mpt::PathString dirname; public: TempDirGuard(const mpt::PathString &dirname_ = CreateTempFileName()); mpt::PathString GetDirname() const; ~TempDirGuard(); }; #endif // MODPLUG_TRACKER && MPT_OS_WINDOWS } // namespace mpt #if defined(MODPLUG_TRACKER) // Sanitize a filename (remove special chars) void SanitizeFilename(mpt::PathString &filename); void SanitizeFilename(char *beg, char *end); void SanitizeFilename(wchar_t *beg, wchar_t *end); void SanitizeFilename(std::string &str); void SanitizeFilename(std::wstring &str); #if MPT_USTRING_MODE_UTF8 void SanitizeFilename(mpt::u8string &str); #endif // MPT_USTRING_MODE_UTF8 template void SanitizeFilename(char (&buffer)[size]) { static_assert(size > 0); SanitizeFilename(buffer, buffer + size); } template void SanitizeFilename(wchar_t (&buffer)[size]) { static_assert(size > 0); SanitizeFilename(buffer, buffer + size); } #if defined(MPT_WITH_MFC) void SanitizeFilename(CString &str); #endif // MPT_WITH_MFC #endif // MODPLUG_TRACKER #if defined(MODPLUG_TRACKER) enum FileTypeFormat { FileTypeFormatNone = 0 , // do not show extensions after description, i.e. "Foo Files" FileTypeFormatShowExtensions = 1<<0, // show extensions after descripten, i.e. "Foo Files (*.foo,*.bar)" }; MPT_DECLARE_ENUM(FileTypeFormat) class FileType { private: mpt::ustring m_ShortName; // "flac", "mod" (lowercase) mpt::ustring m_Description; // "FastTracker 2 Module" std::vector m_MimeTypes; // "audio/ogg" (in ASCII) std::vector m_Extensions; // "mod", "xm" (lowercase) std::vector m_Prefixes; // "mod" for "mod.*" public: FileType() { } FileType(const std::vector &group) { for(const auto &type : group) { mpt::append(m_MimeTypes, type.m_MimeTypes); mpt::append(m_Extensions, type.m_Extensions); mpt::append(m_Prefixes, type.m_Prefixes); } } static FileType Any() { return FileType().ShortName(U_("*")).Description(U_("All Files")).AddExtension(P_("*")); } public: FileType& ShortName(const mpt::ustring &shortName) { m_ShortName = shortName; return *this; } FileType& Description(const mpt::ustring &description) { m_Description = description; return *this; } FileType& MimeTypes(const std::vector &mimeTypes) { m_MimeTypes = mimeTypes; return *this; } FileType& Extensions(const std::vector &extensions) { m_Extensions = extensions; return *this; } FileType& Prefixes(const std::vector &prefixes) { m_Prefixes = prefixes; return *this; } FileType& AddMimeType(const std::string &mimeType) { m_MimeTypes.push_back(mimeType); return *this; } FileType& AddExtension(const mpt::PathString &extension) { m_Extensions.push_back(extension); return *this; } FileType& AddPrefix(const mpt::PathString &prefix) { m_Prefixes.push_back(prefix); return *this; } public: mpt::ustring GetShortName() const { return m_ShortName; } mpt::ustring GetDescription() const { return m_Description; } std::vector GetMimeTypes() const { return m_MimeTypes; } std::vector GetExtensions() const { return m_Extensions; } std::vector GetPrefixes() const { return m_Prefixes; } public: mpt::PathString AsFilterString(FlagSet format = FileTypeFormatNone) const; mpt::PathString AsFilterOnlyString() const; }; // class FileType // "Ogg Vorbis|*.ogg;*.oga|" // FileTypeFormatNone // "Ogg Vorbis (*.ogg,*.oga)|*.ogg;*.oga|" // FileTypeFormatShowExtensions mpt::PathString ToFilterString(const FileType &fileType, FlagSet format = FileTypeFormatNone); mpt::PathString ToFilterString(const std::vector &fileTypes, FlagSet format = FileTypeFormatNone); // "*.ogg;*.oga" / ";*.ogg;*.oga" mpt::PathString ToFilterOnlyString(const FileType &fileType, bool prependSemicolonWhenNotEmpty = false); mpt::PathString ToFilterOnlyString(const std::vector &fileTypes, bool prependSemicolonWhenNotEmpty = false); #endif // MODPLUG_TRACKER OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/common/mptRandom.cpp0000644000175000017500000000226014044173026020212 00000000000000/* * mptRandom.cpp * ------------- * Purpose: PRNG * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "mptRandom.h" OPENMPT_NAMESPACE_BEGIN namespace mpt { #if defined(MODPLUG_TRACKER) && !defined(MPT_BUILD_WINESUPPORT) static mpt::random_device *g_rd = nullptr; static mpt::thread_safe_prng *g_global_prng = nullptr; void set_global_random_device(mpt::random_device *rd) { g_rd = rd; } void set_global_prng(mpt::thread_safe_prng *prng) { g_global_prng = prng; } mpt::random_device & global_random_device() { return *g_rd; } mpt::thread_safe_prng & global_prng() { return *g_global_prng; } #else mpt::random_device & global_random_device() { static mpt::random_device g_rd; return g_rd; } mpt::thread_safe_prng & global_prng() { static mpt::thread_safe_prng g_global_prng(mpt::make_prng(global_random_device())); return g_global_prng; } #endif // MODPLUG_TRACKER && !MPT_BUILD_WINESUPPORT } // namespace mpt OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/common/mptRandom.h0000644000175000017500000000663214053107667017676 00000000000000/* * mptRandom.h * ----------- * Purpose: PRNG * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "mpt/base/bit.hpp" #include "mpt/mutex/mutex.hpp" #ifdef MODPLUG_TRACKER #include "mpt/random/crand.hpp" #endif // MODPLUG_TRACKER #include "mpt/random/default_engines.hpp" #include "mpt/random/device.hpp" #include "mpt/random/engine.hpp" #include "mpt/random/engine_lcg.hpp" #include "mpt/random/seed.hpp" #include #include OPENMPT_NAMESPACE_BEGIN // NOTE: // We implement our own PRNG and distribution functions as the implementations // of std::uniform_int_distribution is either wrong (not uniform in MSVC2010) or // not guaranteed to be livelock-free for bad PRNGs (in GCC, Clang, boost). // We resort to a simpler implementation with only power-of-2 result ranges for // both the underlying PRNG and our interface function. This saves us from // complicated code having to deal with partial bits of entropy. // Our interface still somewhat follows the mindset of C++11 (with the // addition of a simple wrapper function mpt::random which saves the caller from // instantiating distribution objects for the common uniform distribution case. // We are still using std::random_device for initial seeding when avalable and // after working around its set of problems. namespace mpt { template class thread_safe_prng : private Trng { private: mpt::mutex m; public: typedef typename Trng::result_type result_type; public: template explicit thread_safe_prng(Trd & rd) : Trng(mpt::make_prng(rd)) { return; } thread_safe_prng(Trng rng) : Trng(rng) { return; } public: static MPT_CONSTEXPRINLINE typename engine_traits::result_type min() { return Trng::min(); } static MPT_CONSTEXPRINLINE typename engine_traits::result_type max() { return Trng::max(); } static MPT_CONSTEXPRINLINE int result_bits() { return engine_traits::result_bits(); } public: typename engine_traits::result_type operator()() { mpt::lock_guard l(m); return Trng::operator()(); } }; #ifdef MPT_BUILD_FUZZER // Use deterministic seeding using random_device = deterministc_random_device; #else // !MPT_BUILD_FUZZER // mpt::random_device always generates 32 bits of entropy using random_device = mpt::sane_random_device; #endif // MPT_BUILD_FUZZER #ifdef MPT_BUILD_FUZZER // Use fast PRNGs in order to not waste time fuzzing more complex PRNG // implementations. using fast_prng = deterministic_fast_engine; using good_prng = deterministic_good_engine; #else // !MPT_BUILD_FUZZER // We cannot use std::minstd_rand here because it has not a power-of-2 sized // output domain which we rely upon. using fast_prng = fast_engine; // about 3 ALU operations, ~32bit of state, suited for inner loops using good_prng = good_engine; #endif // MPT_BUILD_FUZZER using default_prng = mpt::good_prng; mpt::random_device & global_random_device(); mpt::thread_safe_prng & global_prng(); #if defined(MODPLUG_TRACKER) && !defined(MPT_BUILD_WINESUPPORT) void set_global_random_device(mpt::random_device *rd); void set_global_prng(mpt::thread_safe_prng *rng); #endif // MODPLUG_TRACKER && !MPT_BUILD_WINESUPPORT } // namespace mpt OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/common/mptString.cpp0000644000175000017500000006437214142660753020263 00000000000000/* * mptString.cpp * ------------- * Purpose: Small string-related utilities, number and message formatting. * Notes : Currently none. * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "mptString.h" #include "mpt/string/types.hpp" #include "mpt/string/utility.hpp" #include "mpt/string_transcode/transcode.hpp" #include #include #include #include #if defined(MODPLUG_TRACKER) #include #endif // MODPLUG_TRACKER #if defined(MODPLUG_TRACKER) #include #endif // MODPLUG_TRACKER #if MPT_OS_WINDOWS #include #endif // MPT_OS_WINDOWS OPENMPT_NAMESPACE_BEGIN /* Quick guide to the OpenMPT string type jungle ============================================= This quick guide is only meant as a hint. There may be valid reasons to not honor the recommendations found here. Staying consistent with surrounding and/or related code sections may also be important. List of string types -------------------- * std::string (OpenMPT, libopenmpt) C++ string of unspecifed 8bit encoding. Try to always document the encoding if not clear from context. Do not use unless there is an obvious reason to do so. * std::wstring (OpenMPT) UTF16 (on windows) or UTF32 (otherwise). Do not use unless there is an obvious reason to do so. * mpt::lstring (OpenMPT) OpenMPT locale string type. The encoding is always CP_ACP. Do not use unless there is an obvious reason to do so. * char* (OpenMPT, libopenmpt) C string of unspecified encoding. Use only for static literals or in performance critical inner loops where full control and avoidance of memory allocations is required. * wchar_t* (OpenMPT) C wide string. Use only if Unicode is required for static literals or in performance critical inner loops where full control and avoidance of memory allocation is required. * mpt::winstring (OpenMPT) OpenMPT type-safe string to interface with native WinAPI, either encoded in locale/CP_ACP (if !UNICODE) or UTF16 (if UNICODE). * CString (OpenMPT) MFC string type, either encoded in locale/CP_ACP (if !UNICODE) or UTF16 (if UNICODE). Specify literals with _T(""). Use in MFC GUI code. * CStringA (OpenMPT) MFC ANSI string type. The encoding is always CP_ACP. Do not use. * CStringW (OpenMPT) MFC Unicode string type. Do not use. * mpt::PathString (OpenMPT, libopenmpt) String type representing paths and filenames. Always use for these in order to avoid potentially lossy conversions. Use P_("") macro for literals. * mpt::ustring (OpenMPT, libopenmpt) The default unicode string type. Can be encoded in UTF8 or UTF16 or UTF32, depending on MPT_USTRING_MODE_* and sizeof(wchar_t). Literals can written as U_(""). Use as your default string type if no other string type is a measurably better fit. * MPT_UTF8 (OpenMPT, libopenmpt) Macro that generates a mpt::ustring from string literals containing non-ascii characters. In order to keep the source code in ascii encoding, always express non-ascii characters using explicit \x23 escaping. Note that depending on the underlying type of mpt::ustring, MPT_UTF8 *requires* a runtime conversion. Only use for string literals containing non-ascii characters (use MPT_USTRING otherwise). * MPT_ULITERAL / MPT_UCHAR / mpt::uchar (OpenMPT, libopenmpt) Macros which generate string literals, char literals and the char literal type respectively. These are especially useful in constexpr contexts or global data where MPT_USTRING is either unusable or requires a global contructor to run. Do NOT use as a performance optimization in place of MPT_USTRING however, because MPT_USTRING can be converted to C++11/14 user defined literals eventually, while MPT_ULITERAL cannot because of constexpr requirements. * mpt::RawPathString (OpenMPT, libopenmpt) Internal representation of mpt::PathString. Only use for parsing path fragments. * mpt::u8string (OpenMPT, libopenmpt) Internal representation of mpt::ustring. Do not use directly. Ever. * std::basic_string (OpenMPT) Same as std::string. Do not use std::basic_string in the templated form. * std::basic_string (OpenMPT) Same as std::wstring. Do not use std::basic_string in the templated form. The following string types are available in order to avoid the need to overload functions on a huge variety of string types. Use only ever as function argument types. Note that the locale charset is not available on all libopenmpt builds (in which case the option is ignored or a sensible fallback is used; these types are always available). All these types publicly inherit from mpt::ustring and do not contain any additional state. This means that they work the same way as mpt::ustring does and do support type-slicing for both, read and write accesses. These types only add conversion constructors for all string types that have a defined encoding and for all 8bit string types using the specified encoding heuristic. * AnyUnicodeString (OpenMPT, libopenmpt) Is constructible from any Unicode string. * AnyString (OpenMPT, libopenmpt) Tries to do the smartest auto-magic we can do. * AnyStringLocale (OpenMPT, libopenmpt) char-based strings are assumed to be in locale encoding. * AnyStringUTF8orLocale (OpenMPT, libopenmpt) char-based strings are tried in UTF8 first, if this fails, locale is used. * AnyStringUTF8 (OpenMPT, libopenmpt) char-based strings are assumed to be in UTF8. Encoding of 8bit strings ------------------------ 8bit strings have an unspecified encoding. When the string is contained within a CSoundFile object, the encoding is most likely CSoundFile::GetCharsetInternal(), otherwise, try to gather the most probable encoding from surrounding or related code sections. Decision tree to help deciding which string type to use ------------------------------------------------------- if in libopenmpt if in libopenmpt c++ interface T = std::string, the encoding is utf8 elif in libopenmpt c interface T = char*, the encoding is utf8 elif performance critical inner loop T = char*, document the encoding if not clear from context elif string literal containing non-ascii characters T = MPT_UTF8 elif path or file if parsing path fragments T = mpt::RawPathString template your function on the concrete underlying string type (std::string and std::wstring) or use preprocessor MPT_OS_WINDOWS else T = mpt::PathString fi else T = mpt::ustring fi else if performance critical inner loop if needs unicode support T = mpt::uchar* / MPT_ULITERAL else T = char*, document the encoding if not clear from context fi elif string literal containing non-ascii characters T = MPT_UTF8 elif path or file if parsing path fragments T = mpt::RawPathString template your function on the concrete underlying string type (std::string and std::wstring) or use preprocessor MPT_OS_WINDOWS else T = mpt::PathString fi elif winapi interfacing code T = mpt::winstring elif mfc/gui code T = CString else if constexpr context or global data T = mpt::uchar* / MPT_ULITERAL else T = mpt::ustring fi fi fi This boils down to: Prefer mpt::PathString and mpt::ustring, and only use any other string type if there is an obvious reason to do so. Character set conversions ------------------------- Character set conversions in OpenMPT are always fuzzy. Behaviour in case of an invalid source encoding and behaviour in case of an unrepresentable destination encoding can be any of the following: * The character is replaced by some replacement character ('?' or L'\ufffd' in most cases). * The character is replaced by a similar character (either semantically similiar or visually similar). * The character is transcribed with some ASCII text. * The character is discarded. * Conversion stops at this very character. Additionally. conversion may stop or continue on \0 characters in the middle of the string. Behaviour can vary from one conversion tuple to any other. If you need to ensure lossless conversion, do a roundtrip conversion and check for equality. Unicode handling ---------------- OpenMPT is generally not aware of and does not handle different Unicode normalization forms. You should be aware of the following possibilities: * Conversion between UTF8, UTF16, UTF32 may or may not change between NFC and NFD. * Conversion from any non-Unicode 8bit encoding can result in both, NFC or NFD forms. * Conversion to any non-Unicode 8bit encoding may or may not involve conversion to NFC, NFD, NFKC or NFKD during the conversion. This in particular means that conversion of decomposed german umlauts to ISO8859-1 may fail. * Changing the normalization form of path strings may render the file inaccessible. Unicode BOM may or may not be preserved and/or discarded during conversion. Invalid Unicode code points may be treated as invalid or as valid characters when converting between different Unicode encodings. Interfacing with WinAPI ----------------------- When in MFC code, use CString. When in non MFC code, either use std::wstring when directly interfacing with APIs only available in WCHAR variants, or use mpt::winstring and mpt::WinStringBuf helpers otherwise. Specify TCHAR string literals with _T("foo") in mptrack/, and with TEXT("foo") in common/ or sounddev/. _T() requires which is specific to the MSVC runtime and not portable across compilers. TEXT() is from . We use _T() in mptrack/ only because it is shorter. */ namespace mpt { namespace String { #define C(x) (mpt::char_value((x))) // AMS1 actually only supports ASCII plus the modified control characters and no high chars at all. // Just default to CP437 for those to keep things simple. static constexpr char32_t CharsetTableCP437AMS[256] = { C(' '),0x0001,0x0002,0x0003,0x00e4,0x0005,0x00e5,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x00c4,0x00c5, // differs from CP437 0x0010,0x0011,0x0012,0x0013,0x00f6,0x0015,0x0016,0x0017,0x0018,0x00d6,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f, // differs from CP437 0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f, 0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f, 0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f, 0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f, 0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f, 0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x2302, 0x00c7,0x00fc,0x00e9,0x00e2,0x00e4,0x00e0,0x00e5,0x00e7,0x00ea,0x00eb,0x00e8,0x00ef,0x00ee,0x00ec,0x00c4,0x00c5, 0x00c9,0x00e6,0x00c6,0x00f4,0x00f6,0x00f2,0x00fb,0x00f9,0x00ff,0x00d6,0x00dc,0x00a2,0x00a3,0x00a5,0x20a7,0x0192, 0x00e1,0x00ed,0x00f3,0x00fa,0x00f1,0x00d1,0x00aa,0x00ba,0x00bf,0x2310,0x00ac,0x00bd,0x00bc,0x00a1,0x00ab,0x00bb, 0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,0x2555,0x2563,0x2551,0x2557,0x255d,0x255c,0x255b,0x2510, 0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x255e,0x255f,0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x2567, 0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256b,0x256a,0x2518,0x250c,0x2588,0x2584,0x258c,0x2590,0x2580, 0x03b1,0x00df,0x0393,0x03c0,0x03a3,0x03c3,0x00b5,0x03c4,0x03a6,0x0398,0x03a9,0x03b4,0x221e,0x03c6,0x03b5,0x2229, 0x2261,0x00b1,0x2265,0x2264,0x2320,0x2321,0x00f7,0x2248,0x00b0,0x2219,0x00b7,0x221a,0x207f,0x00b2,0x25a0,0x00a0 }; // AMS2: Looking at Velvet Studio's bitmap font (TPIC32.PCX), these appear to be the only supported non-ASCII chars. static constexpr char32_t CharsetTableCP437AMS2[256] = { C(' '),0x00a9,0x221a,0x00b7,C('0'),C('1'),C('2'),C('3'),C('4'),C('5'),C('6'),C('7'),C('8'),C('9'),C('A'),C('B'), // differs from CP437 C('C'),C('D'),C('E'),C('F'),C(' '),0x00a7,C(' '),C(' '),C(' '),C(' '),C(' '),C(' '),C(' '),C(' '),C(' '),C(' '), // differs from CP437 0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f, 0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f, 0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f, 0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f, 0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f, 0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x2302, 0x00c7,0x00fc,0x00e9,0x00e2,0x00e4,0x00e0,0x00e5,0x00e7,0x00ea,0x00eb,0x00e8,0x00ef,0x00ee,0x00ec,0x00c4,0x00c5, 0x00c9,0x00e6,0x00c6,0x00f4,0x00f6,0x00f2,0x00fb,0x00f9,0x00ff,0x00d6,0x00dc,0x00a2,0x00a3,0x00a5,0x20a7,0x0192, 0x00e1,0x00ed,0x00f3,0x00fa,0x00f1,0x00d1,0x00aa,0x00ba,0x00bf,0x2310,0x00ac,0x00bd,0x00bc,0x00a1,0x00ab,0x00bb, 0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,0x2555,0x2563,0x2551,0x2557,0x255d,0x255c,0x255b,0x2510, 0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x255e,0x255f,0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x2567, 0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256b,0x256a,0x2518,0x250c,0x2588,0x2584,0x258c,0x2590,0x2580, 0x03b1,0x00df,0x0393,0x03c0,0x03a3,0x03c3,0x00b5,0x03c4,0x03a6,0x0398,0x03a9,0x03b4,0x221e,0x03c6,0x03b5,0x2229, 0x2261,0x00b1,0x2265,0x2264,0x2320,0x2321,0x00f7,0x2248,0x00b0,0x2219,0x00b7,0x221a,0x207f,0x00b2,0x25a0,0x00a0 }; #undef C // templated on 8bit strings because of type-safe variants template static Tdststring EncodeImpl(Charset charset, const mpt::widestring &src) { static_assert(sizeof(typename Tdststring::value_type) == sizeof(char)); static_assert(mpt::is_character::value); switch(charset) { #if defined(MPT_ENABLE_CHARSET_LOCALE) case Charset::Locale: return mpt::encode(mpt::logical_encoding::locale, src); break; #endif case Charset::UTF8: return mpt::encode(mpt::common_encoding::utf8, src); break; case Charset::ASCII: return mpt::encode(mpt::common_encoding::ascii, src); break; case Charset::ISO8859_1: return mpt::encode(mpt::common_encoding::iso8859_1, src); break; case Charset::ISO8859_15: return mpt::encode(mpt::common_encoding::iso8859_15, src); break; case Charset::CP850: return mpt::encode(mpt::common_encoding::cp850, src); break; case Charset::CP437: return mpt::encode(mpt::common_encoding::cp437, src); break; case Charset::CP437AMS: return mpt::encode(CharsetTableCP437AMS, src); break; case Charset::CP437AMS2: return mpt::encode(CharsetTableCP437AMS2, src); break; case Charset::Windows1252: return mpt::encode(mpt::common_encoding::windows1252, src); break; } return Tdststring(); } // templated on 8bit strings because of type-safe variants template static mpt::widestring DecodeImpl(Charset charset, const Tsrcstring &src) { static_assert(sizeof(typename Tsrcstring::value_type) == sizeof(char)); static_assert(mpt::is_character::value); switch(charset) { #if defined(MPT_ENABLE_CHARSET_LOCALE) case Charset::Locale: return mpt::decode(mpt::logical_encoding::locale, src); break; #endif case Charset::UTF8: return mpt::decode(mpt::common_encoding::utf8, src); break; case Charset::ASCII: return mpt::decode(mpt::common_encoding::ascii, src); break; case Charset::ISO8859_1: return mpt::decode(mpt::common_encoding::iso8859_1, src); break; case Charset::ISO8859_15: return mpt::decode(mpt::common_encoding::iso8859_15, src); break; case Charset::CP850: return mpt::decode(mpt::common_encoding::cp850, src); break; case Charset::CP437: return mpt::decode(mpt::common_encoding::cp437, src); break; case Charset::CP437AMS: return mpt::decode(CharsetTableCP437AMS, src); break; case Charset::CP437AMS2: return mpt::decode(CharsetTableCP437AMS2, src); break; case Charset::Windows1252: return mpt::decode(mpt::common_encoding::windows1252, src); break; } return mpt::widestring(); } // templated on 8bit strings because of type-safe variants template static Tdststring ConvertImpl(Charset to, Charset from, const Tsrcstring &src) { static_assert(sizeof(typename Tdststring::value_type) == sizeof(char)); static_assert(sizeof(typename Tsrcstring::value_type) == sizeof(char)); if(to == from) { const typename Tsrcstring::value_type * src_beg = src.data(); const typename Tsrcstring::value_type * src_end = src_beg + src.size(); return Tdststring(reinterpret_cast(src_beg), reinterpret_cast(src_end)); } return EncodeImpl(to, DecodeImpl(from, src)); } } // namespace String bool IsUTF8(const std::string &str) { return mpt::is_utf8(str); } #if MPT_WSTRING_CONVERT std::wstring ToWide(Charset from, const std::string &str) { return String::DecodeImpl(from, str); } #if defined(MPT_ENABLE_CHARSET_LOCALE) std::wstring ToWide(const mpt::lstring &str) { return String::DecodeImpl(Charset::Locale, str); } #endif // MPT_ENABLE_CHARSET_LOCALE #endif #if MPT_WSTRING_CONVERT std::string ToCharset(Charset to, const std::wstring &str) { return String::EncodeImpl(to, str); } #endif std::string ToCharset(Charset to, Charset from, const std::string &str) { return String::ConvertImpl(to, from, str); } #if defined(MPT_ENABLE_CHARSET_LOCALE) std::string ToCharset(Charset to, const mpt::lstring &str) { return String::ConvertImpl(to, Charset::Locale, str); } #endif // MPT_ENABLE_CHARSET_LOCALE #if defined(MPT_ENABLE_CHARSET_LOCALE) #if MPT_WSTRING_CONVERT mpt::lstring ToLocale(const std::wstring &str) { return String::EncodeImpl(Charset::Locale, str); } #endif mpt::lstring ToLocale(Charset from, const std::string &str) { return String::ConvertImpl(Charset::Locale, from, str); } #endif // MPT_ENABLE_CHARSET_LOCALE #if MPT_OS_WINDOWS #if MPT_WSTRING_CONVERT mpt::winstring ToWin(const std::wstring &str) { #ifdef UNICODE return str; #else return ToLocale(str); #endif } #endif mpt::winstring ToWin(Charset from, const std::string &str) { #ifdef UNICODE return ToWide(from, str); #else return ToLocale(from, str); #endif } #if defined(MPT_ENABLE_CHARSET_LOCALE) mpt::winstring ToWin(const mpt::lstring &str) { #ifdef UNICODE return ToWide(str); #else return str; #endif } #endif // MPT_ENABLE_CHARSET_LOCALE #endif // MPT_OS_WINDOWS #if defined(MPT_WITH_MFC) CString ToCString(const std::wstring &str) { #ifdef UNICODE return str.c_str(); #else return ToCharset(Charset::Locale, str).c_str(); #endif } CString ToCString(Charset from, const std::string &str) { #ifdef UNICODE return ToWide(from, str).c_str(); #else return ToCharset(Charset::Locale, from, str).c_str(); #endif } std::wstring ToWide(const CString &str) { #ifdef UNICODE return str.GetString(); #else return ToWide(Charset::Locale, str.GetString()); #endif } std::string ToCharset(Charset to, const CString &str) { #ifdef UNICODE return ToCharset(to, str.GetString()); #else return ToCharset(to, Charset::Locale, str.GetString()); #endif } #if defined(MPT_ENABLE_CHARSET_LOCALE) CString ToCString(const mpt::lstring &str) { #ifdef UNICODE return ToWide(str).c_str(); #else return str.c_str(); #endif } mpt::lstring ToLocale(const CString &str) { #ifdef UNICODE return String::EncodeImpl(Charset::Locale, str.GetString()); #else return str.GetString(); #endif } #endif // MPT_ENABLE_CHARSET_LOCALE #if MPT_OS_WINDOWS mpt::winstring ToWin(const CString &str) { return str.GetString(); } #endif // MPT_OS_WINDOWS #endif // MPT_WITH_MFC #if MPT_USTRING_MODE_WIDE // inline #else // !MPT_USTRING_MODE_WIDE #if MPT_WSTRING_CONVERT mpt::ustring ToUnicode(const std::wstring &str) { return String::EncodeImpl(mpt::Charset::UTF8, str); } #endif mpt::ustring ToUnicode(Charset from, const std::string &str) { return String::ConvertImpl(mpt::Charset::UTF8, from, str); } #if defined(MPT_ENABLE_CHARSET_LOCALE) mpt::ustring ToUnicode(const mpt::lstring &str) { return String::ConvertImpl(mpt::Charset::UTF8, mpt::Charset::Locale, str); } #endif // MPT_ENABLE_CHARSET_LOCALE #if defined(MPT_WITH_MFC) mpt::ustring ToUnicode(const CString &str) { #ifdef UNICODE return String::EncodeImpl(mpt::Charset::UTF8, str.GetString()); #else // !UNICODE return String::ConvertImpl(mpt::Charset::UTF8, mpt::Charset::Locale, str.GetString()); #endif // UNICODE } #endif // MPT_WITH_MFC #endif // MPT_USTRING_MODE_WIDE #if MPT_USTRING_MODE_WIDE // nothing, std::wstring overloads will catch all stuff #else // !MPT_USTRING_MODE_WIDE #if MPT_WSTRING_CONVERT std::wstring ToWide(const mpt::ustring &str) { return String::DecodeImpl(mpt::Charset::UTF8, str); } #endif std::string ToCharset(Charset to, const mpt::ustring &str) { return String::ConvertImpl(to, mpt::Charset::UTF8, str); } #if defined(MPT_ENABLE_CHARSET_LOCALE) mpt::lstring ToLocale(const mpt::ustring &str) { return String::ConvertImpl(mpt::Charset::Locale, mpt::Charset::UTF8, str); } #endif // MPT_ENABLE_CHARSET_LOCALE #if MPT_OS_WINDOWS mpt::winstring ToWin(const mpt::ustring &str) { #ifdef UNICODE return String::DecodeImpl(mpt::Charset::UTF8, str); #else return String::ConvertImpl(mpt::Charset::Locale, mpt::Charset::UTF8, str); #endif } #endif // MPT_OS_WINDOWS #if defined(MPT_WITH_MFC) CString ToCString(const mpt::ustring &str) { #ifdef UNICODE return String::DecodeImpl(mpt::Charset::UTF8, str).c_str(); #else // !UNICODE return String::ConvertImpl(mpt::Charset::Locale, mpt::Charset::UTF8, str).c_str(); #endif // UNICODE } #endif // MPT_WITH_MFC #endif // MPT_USTRING_MODE_WIDE static mpt::Charset CharsetFromCodePage(uint16 codepage, mpt::Charset fallback, bool * isFallback = nullptr) { mpt::Charset result = fallback; switch(codepage) { case 65001: result = mpt::Charset::UTF8; if(isFallback) *isFallback = false; break; case 20127: result = mpt::Charset::ASCII; if(isFallback) *isFallback = false; break; case 28591: result = mpt::Charset::ISO8859_1; if(isFallback) *isFallback = false; break; case 28605: result = mpt::Charset::ISO8859_15; if(isFallback) *isFallback = false; break; case 437: result = mpt::Charset::CP437; if(isFallback) *isFallback = false; break; case 1252: result = mpt::Charset::Windows1252; if(isFallback) *isFallback = false; break; default: result = fallback; if(isFallback) *isFallback = true; break; } return result; } mpt::ustring ToUnicode(uint16 codepage, mpt::Charset fallback, const std::string &str) { #if MPT_OS_WINDOWS mpt::ustring result; bool noCharsetMatch = true; mpt::Charset charset = mpt::CharsetFromCodePage(codepage, fallback, &noCharsetMatch); if(noCharsetMatch && mpt::has_codepage(codepage)) { result = mpt::ToUnicode(mpt::decode(codepage, str)); } else { result = mpt::ToUnicode(charset, str); } return result; #else // !MPT_OS_WINDOWS return mpt::ToUnicode(mpt::CharsetFromCodePage(codepage, fallback), str); #endif // MPT_OS_WINDOWS } char ToLowerCaseAscii(char c) { return mpt::to_lower_ascii(c); } char ToUpperCaseAscii(char c) { return mpt::to_upper_ascii(c); } std::string ToLowerCaseAscii(std::string s) { std::transform(s.begin(), s.end(), s.begin(), static_cast(&mpt::ToLowerCaseAscii)); return s; } std::string ToUpperCaseAscii(std::string s) { std::transform(s.begin(), s.end(), s.begin(), static_cast(&mpt::ToUpperCaseAscii)); return s; } int CompareNoCaseAscii(const char *a, const char *b, std::size_t n) { while(n--) { unsigned char ac = mpt::char_value(mpt::ToLowerCaseAscii(*a)); unsigned char bc = mpt::char_value(mpt::ToLowerCaseAscii(*b)); if(ac != bc) { return ac < bc ? -1 : 1; } else if(!ac && !bc) { return 0; } ++a; ++b; } return 0; } int CompareNoCaseAscii(std::string_view a, std::string_view b) { for(std::size_t i = 0; i < std::min(a.length(), b.length()); ++i) { unsigned char ac = mpt::char_value(mpt::ToLowerCaseAscii(a[i])); unsigned char bc = mpt::char_value(mpt::ToLowerCaseAscii(b[i])); if(ac != bc) { return ac < bc ? -1 : 1; } else if(!ac && !bc) { return 0; } } if(a.length() == b.length()) { return 0; } return a.length() < b.length() ? -1 : 1; } int CompareNoCaseAscii(const std::string &a, const std::string &b) { return CompareNoCaseAscii(std::string_view(a), std::string_view(b)); } #if defined(MODPLUG_TRACKER) mpt::ustring ToLowerCase(const mpt::ustring &s) { #if defined(MPT_WITH_MFC) #if defined(UNICODE) CString tmp = mpt::ToCString(s); tmp.MakeLower(); return mpt::ToUnicode(tmp); #else // !UNICODE CStringW tmp = mpt::ToWide(s).c_str(); tmp.MakeLower(); return mpt::ToUnicode(tmp.GetString()); #endif // UNICODE #else // !MPT_WITH_MFC std::wstring ws = mpt::ToWide(s); std::transform(ws.begin(), ws.end(), ws.begin(), &std::towlower); return mpt::ToUnicode(ws); #endif // MPT_WITH_MFC } mpt::ustring ToUpperCase(const mpt::ustring &s) { #if defined(MPT_WITH_MFC) #if defined(UNICODE) CString tmp = mpt::ToCString(s); tmp.MakeUpper(); return mpt::ToUnicode(tmp); #else // !UNICODE CStringW tmp = mpt::ToWide(s).c_str(); tmp.MakeUpper(); return mpt::ToUnicode(tmp.GetString()); #endif // UNICODE #else // !MPT_WITH_MFC std::wstring ws = mpt::ToWide(s); std::transform(ws.begin(), ws.end(), ws.begin(), &std::towlower); return mpt::ToUnicode(ws); #endif // MPT_WITH_MFC } #endif // MODPLUG_TRACKER } // namespace mpt OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/common/mptString.h0000644000175000017500000003373714056674306017734 00000000000000/* * mptString.h * ---------- * Purpose: Small string-related utilities, number and message formatting. * Notes : Currently none. * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "mpt/base/alloc.hpp" #include "mpt/base/span.hpp" #include "mpt/string/types.hpp" #include "mpt/string/utility.hpp" #include "mptBaseTypes.h" #include #include #include #include #include OPENMPT_NAMESPACE_BEGIN namespace mpt { namespace String { template inline Tstring Replace(Tstring str, const Tstring2 &oldStr, const Tstring3 &newStr) { return mpt::replace(str, oldStr, newStr); } } // namespace String enum class Charset { UTF8, ASCII, // strictly 7-bit ASCII ISO8859_1, ISO8859_15, CP850, CP437, CP437AMS, CP437AMS2, Windows1252, #if defined(MPT_ENABLE_CHARSET_LOCALE) Locale, // CP_ACP on windows, current C locale otherwise #endif // MPT_ENABLE_CHARSET_LOCALE }; // source code / preprocessor (i.e. # token) inline constexpr Charset CharsetSource = Charset::ASCII; // debug log files inline constexpr Charset CharsetLogfile = Charset::UTF8; // std::clog / std::cout / std::cerr #if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS && defined(MPT_ENABLE_CHARSET_LOCALE) inline constexpr Charset CharsetStdIO = Charset::Locale; #else inline constexpr Charset CharsetStdIO = Charset::UTF8; #endif // getenv #if defined(MPT_ENABLE_CHARSET_LOCALE) inline constexpr Charset CharsetEnvironment = Charset::Locale; #else inline constexpr Charset CharsetEnvironment = Charset::UTF8; #endif // std::exception::what() #if defined(MPT_ENABLE_CHARSET_LOCALE) inline constexpr Charset CharsetException = Charset::Locale; #else inline constexpr Charset CharsetException = Charset::UTF8; #endif // Checks if the std::string represents an UTF8 string. // This is currently implemented as converting to std::wstring and back assuming UTF8 both ways, // and comparing the result to the original string. // Caveats: // - can give false negatives because of possible unicode normalization during conversion // - can give false positives if the 8bit encoding contains high-ascii only in valid utf8 groups // - slow because of double conversion bool IsUTF8(const std::string &str); #if MPT_WSTRING_CONVERT // Convert to a wide character string. // The wide encoding is UTF-16 or UTF-32, based on sizeof(wchar_t). // If str does not contain any invalid characters, this conversion is lossless. // Invalid source bytes will be replaced by some replacement character or string. inline std::wstring ToWide(const std::wstring &str) { return str; } inline std::wstring ToWide(const wchar_t * str) { return (str ? std::wstring(str) : std::wstring()); } std::wstring ToWide(Charset from, const std::string &str); inline std::wstring ToWide(Charset from, const char * str) { return ToWide(from, str ? std::string(str) : std::string()); } #if defined(MPT_ENABLE_CHARSET_LOCALE) std::wstring ToWide(const mpt::lstring &str); #endif // MPT_ENABLE_CHARSET_LOCALE #endif // Convert to a string encoded in the 'to'-specified character set. // If str does not contain any invalid characters, // this conversion will be lossless iff, and only iff, // 'to' is UTF8. // Invalid source bytes or characters that are not representable in the // destination charset will be replaced by some replacement character or string. #if MPT_WSTRING_CONVERT std::string ToCharset(Charset to, const std::wstring &str); inline std::string ToCharset(Charset to, const wchar_t * str) { return ToCharset(to, str ? std::wstring(str) : std::wstring()); } #endif std::string ToCharset(Charset to, Charset from, const std::string &str); inline std::string ToCharset(Charset to, Charset from, const char * str) { return ToCharset(to, from, str ? std::string(str) : std::string()); } #if defined(MPT_ENABLE_CHARSET_LOCALE) std::string ToCharset(Charset to, const mpt::lstring &str); #endif // MPT_ENABLE_CHARSET_LOCALE #if defined(MPT_ENABLE_CHARSET_LOCALE) #if MPT_WSTRING_CONVERT mpt::lstring ToLocale(const std::wstring &str); inline mpt::lstring ToLocale(const wchar_t * str) { return ToLocale(str ? std::wstring(str): std::wstring()); } #endif mpt::lstring ToLocale(Charset from, const std::string &str); inline mpt::lstring ToLocale(Charset from, const char * str) { return ToLocale(from, str ? std::string(str): std::string()); } inline mpt::lstring ToLocale(const mpt::lstring &str) { return str; } #endif // MPT_ENABLE_CHARSET_LOCALE #if MPT_OS_WINDOWS #if MPT_WSTRING_CONVERT mpt::winstring ToWin(const std::wstring &str); inline mpt::winstring ToWin(const wchar_t * str) { return ToWin(str ? std::wstring(str): std::wstring()); } #endif mpt::winstring ToWin(Charset from, const std::string &str); inline mpt::winstring ToWin(Charset from, const char * str) { return ToWin(from, str ? std::string(str): std::string()); } #if defined(MPT_ENABLE_CHARSET_LOCALE) mpt::winstring ToWin(const mpt::lstring &str); #endif // MPT_ENABLE_CHARSET_LOCALE #endif // MPT_OS_WINDOWS #if defined(MPT_WITH_MFC) #if !(MPT_WSTRING_CONVERT) #error "MFC depends on MPT_WSTRING_CONVERT)" #endif // Convert to a MFC CString. The CString encoding depends on UNICODE. // This should also be used when converting to TCHAR strings. // If UNICODE is defined, this is a completely lossless operation. inline CString ToCString(const CString &str) { return str; } CString ToCString(const std::wstring &str); inline CString ToCString(const wchar_t * str) { return ToCString(str ? std::wstring(str) : std::wstring()); } CString ToCString(Charset from, const std::string &str); inline CString ToCString(Charset from, const char * str) { return ToCString(from, str ? std::string(str) : std::string()); } #if defined(MPT_ENABLE_CHARSET_LOCALE) CString ToCString(const mpt::lstring &str); mpt::lstring ToLocale(const CString &str); #endif // MPT_ENABLE_CHARSET_LOCALE #if MPT_OS_WINDOWS mpt::winstring ToWin(const CString &str); #endif // MPT_OS_WINDOWS // Convert from a MFC CString. The CString encoding depends on UNICODE. // This should also be used when converting from TCHAR strings. // If UNICODE is defined, this is a completely lossless operation. std::wstring ToWide(const CString &str); std::string ToCharset(Charset to, const CString &str); #endif // MPT_WITH_MFC #define UC_(x) MPT_UCHAR(x) #define UL_(x) MPT_ULITERAL(x) #define U_(x) MPT_USTRING(x) #if MPT_USTRING_MODE_WIDE #if !(MPT_WSTRING_CONVERT) #error "MPT_USTRING_MODE_WIDE depends on MPT_WSTRING_CONVERT)" #endif inline mpt::ustring ToUnicode(const std::wstring &str) { return str; } inline mpt::ustring ToUnicode(const wchar_t * str) { return (str ? std::wstring(str) : std::wstring()); } inline mpt::ustring ToUnicode(Charset from, const std::string &str) { return ToWide(from, str); } inline mpt::ustring ToUnicode(Charset from, const char * str) { return ToUnicode(from, str ? std::string(str) : std::string()); } #if defined(MPT_ENABLE_CHARSET_LOCALE) inline mpt::ustring ToUnicode(const mpt::lstring &str) { return ToWide(str); } #endif // MPT_ENABLE_CHARSET_LOCALE #if defined(MPT_WITH_MFC) inline mpt::ustring ToUnicode(const CString &str) { return ToWide(str); } #endif // MFC #else // !MPT_USTRING_MODE_WIDE inline mpt::ustring ToUnicode(const mpt::ustring &str) { return str; } #if MPT_WSTRING_CONVERT mpt::ustring ToUnicode(const std::wstring &str); inline mpt::ustring ToUnicode(const wchar_t * str) { return ToUnicode(str ? std::wstring(str) : std::wstring()); } #endif mpt::ustring ToUnicode(Charset from, const std::string &str); inline mpt::ustring ToUnicode(Charset from, const char * str) { return ToUnicode(from, str ? std::string(str) : std::string()); } #if defined(MPT_ENABLE_CHARSET_LOCALE) mpt::ustring ToUnicode(const mpt::lstring &str); #endif // MPT_ENABLE_CHARSET_LOCALE #if defined(MPT_WITH_MFC) mpt::ustring ToUnicode(const CString &str); #endif // MPT_WITH_MFC #endif // MPT_USTRING_MODE_WIDE #if MPT_USTRING_MODE_WIDE #if !(MPT_WSTRING_CONVERT) #error "MPT_USTRING_MODE_WIDE depends on MPT_WSTRING_CONVERT)" #endif // nothing, std::wstring overloads will catch all stuff #else // !MPT_USTRING_MODE_WIDE #if MPT_WSTRING_CONVERT std::wstring ToWide(const mpt::ustring &str); #endif std::string ToCharset(Charset to, const mpt::ustring &str); #if defined(MPT_ENABLE_CHARSET_LOCALE) mpt::lstring ToLocale(const mpt::ustring &str); #endif // MPT_ENABLE_CHARSET_LOCALE #if MPT_OS_WINDOWS mpt::winstring ToWin(const mpt::ustring &str); #endif // MPT_OS_WINDOWS #if defined(MPT_WITH_MFC) CString ToCString(const mpt::ustring &str); #endif // MPT_WITH_MFC #endif // MPT_USTRING_MODE_WIDE // The MPT_UTF8 allows specifying UTF8 char arrays. // The resulting type is mpt::ustring and the construction might require runtime translation, // i.e. it is NOT generally available at compile time. // Use explicit UTF8 encoding, // i.e. U+00FC (LATIN SMALL LETTER U WITH DIAERESIS) would be written as "\xC3\xBC". #define MPT_UTF8(x) mpt::ToUnicode(mpt::Charset::UTF8, x) mpt::ustring ToUnicode(uint16 codepage, mpt::Charset fallback, const std::string &str); char ToLowerCaseAscii(char c); char ToUpperCaseAscii(char c); std::string ToLowerCaseAscii(std::string s); std::string ToUpperCaseAscii(std::string s); int CompareNoCaseAscii(const char *a, const char *b, std::size_t n); int CompareNoCaseAscii(std::string_view a, std::string_view b); int CompareNoCaseAscii(const std::string &a, const std::string &b); #if defined(MODPLUG_TRACKER) mpt::ustring ToLowerCase(const mpt::ustring &s); mpt::ustring ToUpperCase(const mpt::ustring &s); #endif // MODPLUG_TRACKER } // namespace mpt // The AnyString types are meant to be used as function argument types only, // and only during the transition phase to all-unicode strings in the whole codebase. // Using an AnyString type as function argument avoids the need to overload a function for all the // different string types that we currently have. // Warning: These types will silently do charset conversions. Only use them when this can be tolerated. // BasicAnyString is convertable to mpt::ustring and constructable from any string at all. template class BasicAnyString : public mpt::ustring { private: static mpt::ustring From8bit(const std::string &str) { if constexpr(charset == mpt::Charset::UTF8) { return mpt::ToUnicode(mpt::Charset::UTF8, str); } else { // auto utf8 detection if constexpr(tryUTF8) { if(mpt::IsUTF8(str)) { return mpt::ToUnicode(mpt::Charset::UTF8, str); } else { return mpt::ToUnicode(charset, str); } } else { return mpt::ToUnicode(charset, str); } } } public: // 8 bit BasicAnyString(const char *str) : mpt::ustring(From8bit(str ? str : std::string())) { } BasicAnyString(const std::string str) : mpt::ustring(From8bit(str)) { } // locale #if defined(MPT_ENABLE_CHARSET_LOCALE) BasicAnyString(const mpt::lstring str) : mpt::ustring(mpt::ToUnicode(str)) { } #endif // MPT_ENABLE_CHARSET_LOCALE // unicode BasicAnyString(const mpt::ustring &str) : mpt::ustring(str) { } BasicAnyString(mpt::ustring &&str) : mpt::ustring(std::move(str)) { } #if MPT_USTRING_MODE_UTF8 && MPT_WSTRING_CONVERT BasicAnyString(const std::wstring &str) : mpt::ustring(mpt::ToUnicode(str)) { } #endif #if MPT_WSTRING_CONVERT BasicAnyString(const wchar_t *str) : mpt::ustring(str ? mpt::ToUnicode(str) : mpt::ustring()) { } #endif // mfc #if defined(MPT_WITH_MFC) BasicAnyString(const CString &str) : mpt::ustring(mpt::ToUnicode(str)) { } #endif // MPT_WITH_MFC // fallback for custom string types template BasicAnyString(const Tstring &str) : mpt::ustring(mpt::ToUnicode(str)) { } template BasicAnyString(Tstring &&str) : mpt::ustring(mpt::ToUnicode(std::forward(str))) { } }; // AnyUnicodeString is convertable to mpt::ustring and constructable from any unicode string, class AnyUnicodeString : public mpt::ustring { public: // locale #if defined(MPT_ENABLE_CHARSET_LOCALE) AnyUnicodeString(const mpt::lstring &str) : mpt::ustring(mpt::ToUnicode(str)) { } #endif // MPT_ENABLE_CHARSET_LOCALE // unicode AnyUnicodeString(const mpt::ustring &str) : mpt::ustring(str) { } AnyUnicodeString(mpt::ustring &&str) : mpt::ustring(std::move(str)) { } #if MPT_USTRING_MODE_UTF8 && MPT_WSTRING_CONVERT AnyUnicodeString(const std::wstring &str) : mpt::ustring(mpt::ToUnicode(str)) { } #endif #if MPT_WSTRING_CONVERT AnyUnicodeString(const wchar_t *str) : mpt::ustring(str ? mpt::ToUnicode(str) : mpt::ustring()) { } #endif // mfc #if defined(MPT_WITH_MFC) AnyUnicodeString(const CString &str) : mpt::ustring(mpt::ToUnicode(str)) { } #endif // MPT_WITH_MFC // fallback for custom string types template AnyUnicodeString(const Tstring &str) : mpt::ustring(mpt::ToUnicode(str)) { } template AnyUnicodeString(Tstring &&str) : mpt::ustring(mpt::ToUnicode(std::forward(str))) { } }; // AnyString // Try to do the smartest auto-magic we can do. #if defined(MPT_ENABLE_CHARSET_LOCALE) using AnyString = BasicAnyString; #elif MPT_OS_WINDOWS using AnyString = BasicAnyString; #else using AnyString = BasicAnyString; #endif // AnyStringLocale // char-based strings are assumed to be in locale encoding. #if defined(MPT_ENABLE_CHARSET_LOCALE) using AnyStringLocale = BasicAnyString; #else using AnyStringLocale = BasicAnyString; #endif // AnyStringUTF8orLocale // char-based strings are tried in UTF8 first, if this fails, locale is used. #if defined(MPT_ENABLE_CHARSET_LOCALE) using AnyStringUTF8orLocale = BasicAnyString; #else using AnyStringUTF8orLocale = BasicAnyString; #endif // AnyStringUTF8 // char-based strings are assumed to be in UTF8. using AnyStringUTF8 = BasicAnyString; OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/common/mptStringBuffer.cpp0000644000175000017500000000456014053531056021400 00000000000000/* * mptStringBuffer.cpp * ------------------- * Purpose: Various functions for "fixing" char array strings for writing to or * reading from module files, or for securing char arrays in general. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "mptStringBuffer.h" OPENMPT_NAMESPACE_BEGIN namespace mpt { namespace String { namespace detail { std::string ReadStringBuffer(String::ReadWriteMode mode, const char *srcBuffer, std::size_t srcSize) { std::string dest; const char *src = srcBuffer; if(mode == nullTerminated || mode == spacePaddedNull) { // We assume that the last character of the source buffer is null. if(srcSize > 0) { srcSize -= 1; } } if(mode == nullTerminated || mode == maybeNullTerminated) { // Copy null-terminated string, stopping at null. dest.assign(src, std::find(src, src + srcSize, '\0')); } else if(mode == spacePadded || mode == spacePaddedNull) { // Copy string over. dest.assign(src, src + srcSize); // Convert null characters to spaces. std::transform(dest.begin(), dest.end(), dest.begin(), [] (char c) -> char { return (c != '\0') ? c : ' '; }); // Trim trailing spaces. dest = mpt::trim_right(dest, std::string(" ")); } return dest; } void WriteStringBuffer(String::ReadWriteMode mode, char *destBuffer, const std::size_t destSize, const char *srcBuffer, const std::size_t srcSize) { MPT_ASSERT(destSize > 0); const size_t maxSize = std::min(destSize, srcSize); char *dst = destBuffer; const char *src = srcBuffer; // First, copy over null-terminated string. size_t pos = maxSize; while(pos > 0) { if((*dst = *src) == '\0') { break; } pos--; dst++; src++; } if(mode == nullTerminated || mode == maybeNullTerminated) { // Fill rest of string with nulls. std::fill(dst, dst + destSize - maxSize + pos, '\0'); } else if(mode == spacePadded || mode == spacePaddedNull) { // Fill the rest of the destination string with spaces. std::fill(dst, dst + destSize - maxSize + pos, ' '); } if(mode == nullTerminated || mode == spacePaddedNull) { // Make sure that destination is really null-terminated. SetNullTerminator(destBuffer, destSize); } } } // namespace detail } // namespace String } // namespace mpt OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/common/mptStringBuffer.h0000644000175000017500000001706614144047352021054 00000000000000/* * mptStringBuffer.h * ----------------- * Purpose: Various functions for "fixing" char array strings for writing to or * reading from module files, or for securing char arrays in general. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "mpt/string/buffer.hpp" #include "mptString.h" #include #include #include OPENMPT_NAMESPACE_BEGIN namespace mpt { namespace String { enum ReadWriteMode : uint8 { // Reading / Writing: Standard null-terminated string handling. nullTerminated = 1, // Reading: Source string is not guaranteed to be null-terminated (if it fills the whole char array). // Writing: Destination string is not guaranteed to be null-terminated (if it fills the whole char array). maybeNullTerminated = 2, // Reading: String may contain null characters anywhere. They should be treated as spaces. // Writing: A space-padded string is written. spacePadded = 3, // Reading: String may contain null characters anywhere. The last character is ignored (it is supposed to be 0). // Writing: A space-padded string with a trailing null is written. spacePaddedNull = 4, }; namespace detail { std::string ReadStringBuffer(String::ReadWriteMode mode, const char *srcBuffer, std::size_t srcSize); void WriteStringBuffer(String::ReadWriteMode mode, char *destBuffer, const std::size_t destSize, const char *srcBuffer, const std::size_t srcSize); } // namespace detail } // namespace String namespace String { using mpt::ReadTypedBuf; using mpt::WriteTypedBuf; } // namespace String namespace String { using mpt::ReadAutoBuf; using mpt::WriteAutoBuf; } // namespace String template class StringModeBufRefImpl { private: Tchar * buf; std::size_t size; String::ReadWriteMode mode; public: // cppcheck false-positive // cppcheck-suppress uninitMemberVar StringModeBufRefImpl(Tchar * buf_, std::size_t size_, String::ReadWriteMode mode_) : buf(buf_) , size(size_) , mode(mode_) { static_assert(sizeof(Tchar) == 1); } StringModeBufRefImpl(const StringModeBufRefImpl &) = delete; StringModeBufRefImpl(StringModeBufRefImpl &&) = default; StringModeBufRefImpl & operator = (const StringModeBufRefImpl &) = delete; StringModeBufRefImpl & operator = (StringModeBufRefImpl &&) = delete; operator std::string () const { return String::detail::ReadStringBuffer(mode, buf, size); } bool empty() const { return String::detail::ReadStringBuffer(mode, buf, size).empty(); } StringModeBufRefImpl & operator = (const std::string & str) { String::detail::WriteStringBuffer(mode, buf, size, str.data(), str.size()); return *this; } }; template class StringModeBufRefImpl { private: const Tchar * buf; std::size_t size; String::ReadWriteMode mode; public: // cppcheck false-positive // cppcheck-suppress uninitMemberVar StringModeBufRefImpl(const Tchar * buf_, std::size_t size_, String::ReadWriteMode mode_) : buf(buf_) , size(size_) , mode(mode_) { static_assert(sizeof(Tchar) == 1); } StringModeBufRefImpl(const StringModeBufRefImpl &) = delete; StringModeBufRefImpl(StringModeBufRefImpl &&) = default; StringModeBufRefImpl & operator = (const StringModeBufRefImpl &) = delete; StringModeBufRefImpl & operator = (StringModeBufRefImpl &&) = delete; operator std::string () const { return String::detail::ReadStringBuffer(mode, buf, size); } bool empty() const { return String::detail::ReadStringBuffer(mode, buf, size).empty(); } }; namespace String { template inline StringModeBufRefImpl::type> ReadBuf(String::ReadWriteMode mode, const std::array &buf) { return StringModeBufRefImpl::type>(buf.data(), size, mode); } template inline StringModeBufRefImpl::type> ReadBuf(String::ReadWriteMode mode, const Tchar (&buf)[size]) { return StringModeBufRefImpl::type>(buf, size, mode); } template inline StringModeBufRefImpl::type> ReadBuf(String::ReadWriteMode mode, const Tchar * buf, std::size_t size) { return StringModeBufRefImpl::type>(buf, size, mode); } template inline StringModeBufRefImpl WriteBuf(String::ReadWriteMode mode, std::array &buf) { return StringModeBufRefImpl(buf.data(), size, mode); } template inline StringModeBufRefImpl WriteBuf(String::ReadWriteMode mode, Tchar (&buf)[size]) { return StringModeBufRefImpl(buf, size, mode); } template inline StringModeBufRefImpl WriteBuf(String::ReadWriteMode mode, Tchar * buf, std::size_t size) { return StringModeBufRefImpl(buf, size, mode); } } // namespace String template struct modecharbuf { public: typedef char Tchar; using char_type = Tchar; using string_type = std::basic_string; public: Tchar buf[len]; public: modecharbuf() = default; modecharbuf(const modecharbuf &) = default; modecharbuf(modecharbuf &&) = default; modecharbuf & operator = (const modecharbuf &) = default; modecharbuf & operator = (modecharbuf &&) = default; operator string_type () const { return mpt::String::ReadBuf(mode, buf); } bool empty() const { return mpt::String::ReadBuf(mode, buf).empty(); } modecharbuf & operator = (const string_type & str) { mpt::String::WriteBuf(mode, buf) = str; return *this; } }; // see MPT_BINARY_STRUCT template constexpr bool declare_binary_safe(const typename mpt::modecharbuf &) { return true; } //struct is_binary_safe> : public std::true_type { }; static_assert(sizeof(mpt::modecharbuf<7, mpt::String::ReadWriteMode::nullTerminated>) == 7); static_assert(alignof(mpt::modecharbuf<7, mpt::String::ReadWriteMode::nullTerminated>) == 1); static_assert(std::is_standard_layout>::value); #ifdef MODPLUG_TRACKER #if MPT_OS_WINDOWS namespace String { using mpt::ReadWinBuf; using mpt::WriteWinBuf; } // namespace String #if defined(MPT_WITH_MFC) namespace String { using mpt::ReadCStringBuf; using mpt::WriteCStringBuf; } // namespace String #endif // MPT_WITH_MFC #endif // MPT_OS_WINDOWS #endif // MODPLUG_TRACKER namespace String { #if MPT_COMPILER_MSVC #pragma warning(push) #pragma warning(disable:4127) // conditional expression is constant #endif // MPT_COMPILER_MSVC // Sets last character to null in given char array. // Size of the array must be known at compile time. template void SetNullTerminator(char (&buffer)[size]) { static_assert(size > 0); buffer[size - 1] = 0; } inline void SetNullTerminator(char *buffer, size_t size) { MPT_ASSERT(size > 0); buffer[size - 1] = 0; } #if !defined(MPT_COMPILER_QUIRK_NO_WCHAR) template void SetNullTerminator(wchar_t (&buffer)[size]) { static_assert(size > 0); buffer[size - 1] = 0; } inline void SetNullTerminator(wchar_t *buffer, size_t size) { MPT_ASSERT(size > 0); buffer[size - 1] = 0; } #endif // !MPT_COMPILER_QUIRK_NO_WCHAR #if MPT_COMPILER_MSVC #pragma warning(pop) #endif // MPT_COMPILER_MSVC } // namespace String } // namespace mpt OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/common/mptStringFormat.cpp0000644000175000017500000002434614174534230021424 00000000000000/* * mptStringFormat.cpp * ------------------- * Purpose: Convert other types to strings. * Notes : Currently none. * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "mptStringFormat.h" #include "mpt/format/default_floatingpoint.hpp" #include "mpt/format/default_integer.hpp" #include "mpt/format/helpers.hpp" #include "mpt/format/simple_floatingpoint.hpp" #include "mpt/format/simple_integer.hpp" OPENMPT_NAMESPACE_BEGIN namespace mpt { std::string ToAString(const bool & x) { return mpt::format_value_default(x); } std::string ToAString(const signed char & x) { return mpt::format_value_default(x); } std::string ToAString(const unsigned char & x) { return mpt::format_value_default(x); } std::string ToAString(const signed short & x) { return mpt::format_value_default(x); } std::string ToAString(const unsigned short & x) { return mpt::format_value_default(x); } std::string ToAString(const signed int & x) { return mpt::format_value_default(x); } std::string ToAString(const unsigned int & x) { return mpt::format_value_default(x); } std::string ToAString(const signed long & x) { return mpt::format_value_default(x); } std::string ToAString(const unsigned long & x) { return mpt::format_value_default(x); } std::string ToAString(const signed long long & x) { return mpt::format_value_default(x); } std::string ToAString(const unsigned long long & x) { return mpt::format_value_default(x); } std::string ToAString(const float & x) { return mpt::format_value_default(x); } std::string ToAString(const double & x) { return mpt::format_value_default(x); } std::string ToAString(const long double & x) { return mpt::format_value_default(x); } #if MPT_WSTRING_FORMAT #if MPT_USTRING_MODE_UTF8 mpt::ustring ToUString(const std::wstring & x) { return mpt::ToUnicode(x); } #endif mpt::ustring ToUString(const wchar_t * const & x) { return mpt::ToUnicode(x); } #endif #if defined(MPT_WITH_MFC) mpt::ustring ToUString(const CString & x) { return mpt::ToUnicode(x); } #endif // MPT_WITH_MFC mpt::ustring ToUString(const bool & x) { return mpt::format_value_default(x); } mpt::ustring ToUString(const signed char & x) { return mpt::format_value_default(x); } mpt::ustring ToUString(const unsigned char & x) { return mpt::format_value_default(x); } mpt::ustring ToUString(const signed short & x) { return mpt::format_value_default(x); } mpt::ustring ToUString(const unsigned short & x) { return mpt::format_value_default(x); } mpt::ustring ToUString(const signed int & x) { return mpt::format_value_default(x); } mpt::ustring ToUString(const unsigned int & x) { return mpt::format_value_default(x); } mpt::ustring ToUString(const signed long & x) { return mpt::format_value_default(x); } mpt::ustring ToUString(const unsigned long & x) { return mpt::format_value_default(x); } mpt::ustring ToUString(const signed long long & x) { return mpt::format_value_default(x); } mpt::ustring ToUString(const unsigned long long & x) { return mpt::format_value_default(x); } mpt::ustring ToUString(const float & x) { return mpt::format_value_default(x); } mpt::ustring ToUString(const double & x) { return mpt::format_value_default(x); } mpt::ustring ToUString(const long double & x) { return mpt::format_value_default(x); } #if MPT_WSTRING_FORMAT #if MPT_USTRING_MODE_UTF8 std::wstring ToWString(const mpt::ustring & x) { return mpt::ToWide(x); } #endif #if defined(MPT_WITH_MFC) std::wstring ToWString(const CString & x) { return mpt::ToWide(x); } #endif // MPT_WITH_MFC std::wstring ToWString(const bool & x) { return mpt::format_value_default(x); } std::wstring ToWString(const signed char & x) { return mpt::format_value_default(x); } std::wstring ToWString(const unsigned char & x) { return mpt::format_value_default(x); } std::wstring ToWString(const signed short & x) { return mpt::format_value_default(x); } std::wstring ToWString(const unsigned short & x) { return mpt::format_value_default(x); } std::wstring ToWString(const signed int & x) { return mpt::format_value_default(x); } std::wstring ToWString(const unsigned int & x) { return mpt::format_value_default(x); } std::wstring ToWString(const signed long & x) { return mpt::format_value_default(x); } std::wstring ToWString(const unsigned long & x) { return mpt::format_value_default(x); } std::wstring ToWString(const signed long long & x) { return mpt::format_value_default(x); } std::wstring ToWString(const unsigned long long & x) { return mpt::format_value_default(x); } std::wstring ToWString(const float & x) { return mpt::format_value_default(x); } std::wstring ToWString(const double & x) { return mpt::format_value_default(x); } std::wstring ToWString(const long double & x) { return mpt::format_value_default(x); } #endif std::string FormatValA(const bool & x, const FormatSpec & f) { return mpt::format_simple(x, f); } std::string FormatValA(const signed char & x, const FormatSpec & f) { return mpt::format_simple(x, f); } std::string FormatValA(const unsigned char & x, const FormatSpec & f) { return mpt::format_simple(x, f); } std::string FormatValA(const signed short & x, const FormatSpec & f) { return mpt::format_simple(x, f); } std::string FormatValA(const unsigned short & x, const FormatSpec & f) { return mpt::format_simple(x, f); } std::string FormatValA(const signed int & x, const FormatSpec & f) { return mpt::format_simple(x, f); } std::string FormatValA(const unsigned int & x, const FormatSpec & f) { return mpt::format_simple(x, f); } std::string FormatValA(const signed long & x, const FormatSpec & f) { return mpt::format_simple(x, f); } std::string FormatValA(const unsigned long & x, const FormatSpec & f) { return mpt::format_simple(x, f); } std::string FormatValA(const signed long long & x, const FormatSpec & f) { return mpt::format_simple(x, f); } std::string FormatValA(const unsigned long long & x, const FormatSpec & f) { return mpt::format_simple(x, f); } std::string FormatValA(const float & x, const FormatSpec & f) { return mpt::format_simple(x, f); } std::string FormatValA(const double & x, const FormatSpec & f) { return mpt::format_simple(x, f); } std::string FormatValA(const long double & x, const FormatSpec & f) { return mpt::format_simple(x, f); } mpt::ustring FormatValU(const bool & x, const FormatSpec & f) { return mpt::format_simple(x, f); } mpt::ustring FormatValU(const signed char & x, const FormatSpec & f) { return mpt::format_simple(x, f); } mpt::ustring FormatValU(const unsigned char & x, const FormatSpec & f) { return mpt::format_simple(x, f); } mpt::ustring FormatValU(const signed short & x, const FormatSpec & f) { return mpt::format_simple(x, f); } mpt::ustring FormatValU(const unsigned short & x, const FormatSpec & f) { return mpt::format_simple(x, f); } mpt::ustring FormatValU(const signed int & x, const FormatSpec & f) { return mpt::format_simple(x, f); } mpt::ustring FormatValU(const unsigned int & x, const FormatSpec & f) { return mpt::format_simple(x, f); } mpt::ustring FormatValU(const signed long & x, const FormatSpec & f) { return mpt::format_simple(x, f); } mpt::ustring FormatValU(const unsigned long & x, const FormatSpec & f) { return mpt::format_simple(x, f); } mpt::ustring FormatValU(const signed long long & x, const FormatSpec & f) { return mpt::format_simple(x, f); } mpt::ustring FormatValU(const unsigned long long & x, const FormatSpec & f) { return mpt::format_simple(x, f); } mpt::ustring FormatValU(const float & x, const FormatSpec & f) { return mpt::format_simple(x, f); } mpt::ustring FormatValU(const double & x, const FormatSpec & f) { return mpt::format_simple(x, f); } mpt::ustring FormatValU(const long double & x, const FormatSpec & f) { return mpt::format_simple(x, f); } #if MPT_WSTRING_FORMAT std::wstring FormatValW(const bool & x, const FormatSpec & f) { return mpt::format_simple(x, f); } std::wstring FormatValW(const signed char & x, const FormatSpec & f) { return mpt::format_simple(x, f); } std::wstring FormatValW(const unsigned char & x, const FormatSpec & f) { return mpt::format_simple(x, f); } std::wstring FormatValW(const signed short & x, const FormatSpec & f) { return mpt::format_simple(x, f); } std::wstring FormatValW(const unsigned short & x, const FormatSpec & f) { return mpt::format_simple(x, f); } std::wstring FormatValW(const signed int & x, const FormatSpec & f) { return mpt::format_simple(x, f); } std::wstring FormatValW(const unsigned int & x, const FormatSpec & f) { return mpt::format_simple(x, f); } std::wstring FormatValW(const signed long & x, const FormatSpec & f) { return mpt::format_simple(x, f); } std::wstring FormatValW(const unsigned long & x, const FormatSpec & f) { return mpt::format_simple(x, f); } std::wstring FormatValW(const signed long long & x, const FormatSpec & f) { return mpt::format_simple(x, f); } std::wstring FormatValW(const unsigned long long & x, const FormatSpec & f) { return mpt::format_simple(x, f); } std::wstring FormatValW(const float & x, const FormatSpec & f) { return mpt::format_simple(x, f); } std::wstring FormatValW(const double & x, const FormatSpec & f) { return mpt::format_simple(x, f); } std::wstring FormatValW(const long double & x, const FormatSpec & f) { return mpt::format_simple(x, f); } #endif } // namespace mpt OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/common/mptStringFormat.h0000644000175000017500000006310014053176735021070 00000000000000/* * mptStringFormat.h * ----------------- * Purpose: Convert other types to strings. * Notes : Currently none. * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "mpt/base/pointer.hpp" #include "mpt/format/message.hpp" #include "mpt/format/simple_spec.hpp" #include "mpt/string/types.hpp" #include #include "mptString.h" #include "openmpt/base/FlagSet.hpp" OPENMPT_NAMESPACE_BEGIN // The following section demands a rationale. // 1. mpt::afmt::val(), mpt::wfmt::val() and mpt::ufmt::val() mimic the semantics of c++11 std::to_string() and std::to_wstring(). // There is an important difference though. The c++11 versions are specified in terms of sprintf formatting which in turn // depends on the current C locale. This renders these functions unusable in a library context because the current // C locale is set by the library-using application and could be anything. There is no way a library can get reliable semantics // out of these functions. It is thus better to just avoid them. // ToAString() and ToWString() are based on iostream internally, but the the locale of the stream is forced to std::locale::classic(), // which results in "C" ASCII locale behavior. // 2. The full suite of printf-like or iostream like number formatting is generally not required. Instead, a sane subset functionality // is provided here. // When formatting integers, it is recommended to use mpt::afmt::dec or mpt::afmt::hex. Appending a template argument '' sets the width, // the same way as '%nd' would do. Appending a '0' to the function name causes zero-filling as print-like '%0nd' would do. Spelling 'HEX' // in upper-case generates upper-case hex digits. If these are not known at compile-time, a more verbose FormatValA(int, format) can be // used. // 3. mpt::format(format)(...) provides simplified and type-safe message and localization string formatting. // The only specifier allowed is '{}' enclosing a number n. It references to n-th parameter after the format string (1-based). // This mimics the behaviour of QString::arg() in QT4/5 or MFC AfxFormatString2(). C printf-like functions offer similar functionality // with a '%n$TYPE' syntax. In .NET, the syntax is '{n}'. This is useful to support localization strings that can change the parameter // ordering. // 4. Every function is available for std::string, std::wstring and mpt::ustring. std::string makes no assumption about the encoding, which // basically means, it should work for any 7-bit or 8-bit encoding, including for example ASCII, UTF8 or the current locale encoding. // std::string std::wstring mpt::ustring mpt::tsrtring CString // mpt::afmt mpt::wfmt mpt::ufmt mpt::tfmt mpt::cfmt // MPT_AFORMAT("{}") MPT_WFORMAT("{}") MPT_UFORMAT("{}") MPT_TFORMAT("{}") MPT_CFORMAT("{}") // 5. All functionality here delegates real work outside of the header file so that and do not need to be included when // using this functionality. // Advantages: // - Avoids binary code bloat when too much of iostream operator << gets inlined at every usage site. // - Faster compile times because and (2 very complex headers) are not included everywhere. // Disadvantages: // - Slightly more c++ code is required for delegating work. // - As the header does not use iostreams, custom types need to overload mpt::UString instead of iostream operator << to allow for custom type // formatting. // - std::string, std::wstring and mpt::ustring are returned from somewhat deep cascades of helper functions. Where possible, code is // written in such a way that return-value-optimization (RVO) or named-return-value-optimization (NRVO) should be able to eliminate // almost all these copies. This should not be a problem for any decent modern compiler (and even less so for a c++11 compiler where // move-semantics will kick in if RVO/NRVO fails). namespace mpt { // ToUString() converts various built-in types to a well-defined, locale-independent string representation. // This is also used as a type-tunnel pattern for mpt::format. // Custom types that need to be converted to strings are encouraged to overload ToUString(). // fallback to member function ToUString() #if MPT_USTRING_MODE_UTF8 template [[deprecated]] auto ToAString(const T & x) -> decltype(mpt::ToCharset(mpt::Charset::UTF8, x.ToUString())) { return mpt::ToCharset(mpt::Charset::UTF8, x.ToUString()); } // unknown encoding #else #if defined(MPT_ENABLE_CHARSET_LOCALE) template [[deprecated]] auto ToAString(const T & x) -> decltype(mpt::ToCharset(mpt::Charset::Locale, x.ToUString())) { return mpt::ToCharset(mpt::Charset::Locale, x.ToUString()); } // unknown encoding #else // !MPT_ENABLE_CHARSET_LOCALE template [[deprecated]] auto ToAString(const T & x) -> decltype(mpt::ToCharset(mpt::Charset::UTF8, x.ToUString())) { return mpt::ToCharset(mpt::Charset::UTF8, x.ToUString()); } // unknown encoding #endif // MPT_ENABLE_CHARSET_LOCALE #endif inline std::string ToAString(const std::string & x) { return x; } inline std::string ToAString(const char * const & x) { return x; } std::string ToAString(const char &x) = delete; // deprecated to catch potential API mis-use, use std::string(1, x) instead #if MPT_WSTRING_FORMAT std::string ToAString(const std::wstring & x) = delete; // Unknown encoding. std::string ToAString(const wchar_t * const & x) = delete; // Unknown encoding. std::string ToAString(const wchar_t &x ) = delete; // deprecated to catch potential API mis-use, use std::wstring(1, x) instead #endif #if MPT_USTRING_MODE_UTF8 std::string ToAString(const mpt::ustring & x) = delete; // Unknown encoding. #endif #if defined(MPT_WITH_MFC) std::string ToAString(const CString & x) = delete; // unknown encoding #endif // MPT_WITH_MFC std::string ToAString(const bool & x); std::string ToAString(const signed char & x); std::string ToAString(const unsigned char & x); std::string ToAString(const signed short & x); std::string ToAString(const unsigned short & x); std::string ToAString(const signed int & x); std::string ToAString(const unsigned int & x); std::string ToAString(const signed long & x); std::string ToAString(const unsigned long & x); std::string ToAString(const signed long long & x); std::string ToAString(const unsigned long long & x); std::string ToAString(const float & x); std::string ToAString(const double & x); std::string ToAString(const long double & x); // fallback to member function ToUString() template auto ToUString(const T & x) -> decltype(x.ToUString()) { return x.ToUString(); } inline mpt::ustring ToUString(const mpt::ustring & x) { return x; } mpt::ustring ToUString(const std::string & x) = delete; // Unknown encoding. mpt::ustring ToUString(const char * const & x) = delete; // Unknown encoding. Note that this also applies to TCHAR in !UNICODE builds as the type is indistinguishable from char. Wrap with CString or FromTcharStr in this case. mpt::ustring ToUString(const char & x) = delete; // deprecated to catch potential API mis-use, use std::string(1, x) instead #if MPT_WSTRING_FORMAT #if MPT_USTRING_MODE_UTF8 mpt::ustring ToUString(const std::wstring & x); #endif mpt::ustring ToUString(const wchar_t * const & x); mpt::ustring ToUString(const wchar_t & x) = delete; // deprecated to catch potential API mis-use, use std::wstring(1, x) instead #endif #if defined(MPT_WITH_MFC) mpt::ustring ToUString(const CString & x); #endif // MPT_WITH_MFC mpt::ustring ToUString(const bool & x); mpt::ustring ToUString(const signed char & x); mpt::ustring ToUString(const unsigned char & x); mpt::ustring ToUString(const signed short & x); mpt::ustring ToUString(const unsigned short & x); mpt::ustring ToUString(const signed int & x); mpt::ustring ToUString(const unsigned int & x); mpt::ustring ToUString(const signed long & x); mpt::ustring ToUString(const unsigned long & x); mpt::ustring ToUString(const signed long long & x); mpt::ustring ToUString(const unsigned long long & x); mpt::ustring ToUString(const float & x); mpt::ustring ToUString(const double & x); mpt::ustring ToUString(const long double & x); #if MPT_WSTRING_FORMAT std::wstring ToWString(const std::string & x) = delete; // Unknown encoding. std::wstring ToWString(const char * const & x) = delete; // Unknown encoding. Note that this also applies to TCHAR in !UNICODE builds as the type is indistinguishable from char. Wrap with CString or FromTcharStr in this case. std::wstring ToWString(const char & x) = delete; // deprecated to catch potential API mis-use, use std::string(1, x) instead inline std::wstring ToWString(const std::wstring & x) { return x; } inline std::wstring ToWString(const wchar_t * const & x) { return x; } std::wstring ToWString(const wchar_t & x) = delete; // deprecated to catch potential API mis-use, use std::wstring(1, x) instead #if MPT_USTRING_MODE_UTF8 std::wstring ToWString(const mpt::ustring & x); #endif #if defined(MPT_WITH_MFC) std::wstring ToWString(const CString & x); #endif // MPT_WITH_MFC std::wstring ToWString(const bool & x); std::wstring ToWString(const signed char & x); std::wstring ToWString(const unsigned char & x); std::wstring ToWString(const signed short & x); std::wstring ToWString(const unsigned short & x); std::wstring ToWString(const signed int & x); std::wstring ToWString(const unsigned int & x); std::wstring ToWString(const signed long & x); std::wstring ToWString(const unsigned long & x); std::wstring ToWString(const signed long long & x); std::wstring ToWString(const unsigned long long & x); std::wstring ToWString(const float & x); std::wstring ToWString(const double & x); std::wstring ToWString(const long double & x); // fallback to member function ToUString() template auto ToWString(const T & x) -> decltype(mpt::ToWide(x.ToUString())) { return mpt::ToWide(x.ToUString()); } #endif #if defined(MPT_ENABLE_CHARSET_LOCALE) template struct ToLocaleHelper { mpt::lstring operator () (const T & v) { return mpt::ToLocale(ToUString(v)); } }; template <> struct ToLocaleHelper { mpt::lstring operator () (const mpt::lstring & v) { return v; } }; #endif // MPT_ENABLE_CHARSET_LOCALE #if defined(MPT_WITH_MFC) template struct ToCStringHelper { CString operator () (const T & v) { return mpt::ToCString(ToUString(v)); } }; template <> struct ToCStringHelper { CString operator () (const CString & v) { return v; } }; #endif // MPT_WITH_MFC template struct ToStringTFunctor {}; template <> struct ToStringTFunctor { template inline std::string operator() (const T & x) { return ToAString(x); } }; template <> struct ToStringTFunctor { template inline mpt::ustring operator() (const T & x) { return ToUString(x); } }; #if MPT_WSTRING_FORMAT && MPT_USTRING_MODE_UTF8 template <> struct ToStringTFunctor { template inline std::wstring operator() (const T & x) { return ToWString(x); } }; #endif #if defined(MPT_ENABLE_CHARSET_LOCALE) template <> struct ToStringTFunctor { template inline mpt::lstring operator() (const T & x) { return mpt::ToLocaleHelper()(x); } }; #endif // MPT_ENABLE_CHARSET_LOCALE #if defined(MPT_WITH_MFC) template <> struct ToStringTFunctor { template inline CString operator() (const T & x) { return mpt::ToCStringHelper()(x); } }; #endif // MPT_WITH_MFC template inline Tstring ToStringT(const T & x) { return ToStringTFunctor()(x); } struct ToStringFormatter { template static inline Tstring format(const T& value) { return ToStringTFunctor()(value); } }; using FormatSpec = mpt::format_simple_spec; using FormatFlags = mpt::format_simple_flags; using fmt_base = mpt::format_simple_base; std::string FormatValA(const char & x, const FormatSpec & f) = delete; // deprecated to catch potential API mis-use, use std::string(1, x) instead #if !defined(MPT_COMPILER_QUIRK_NO_WCHAR) std::string FormatValA(const wchar_t & x, const FormatSpec & f) = delete; // deprecated to catch potential API mis-use, use std::wstring(1, x) instead #endif // !MPT_COMPILER_QUIRK_NO_WCHAR std::string FormatValA(const bool & x, const FormatSpec & f); std::string FormatValA(const signed char & x, const FormatSpec & f); std::string FormatValA(const unsigned char & x, const FormatSpec & f); std::string FormatValA(const signed short & x, const FormatSpec & f); std::string FormatValA(const unsigned short & x, const FormatSpec & f); std::string FormatValA(const signed int & x, const FormatSpec & f); std::string FormatValA(const unsigned int & x, const FormatSpec & f); std::string FormatValA(const signed long & x, const FormatSpec & f); std::string FormatValA(const unsigned long & x, const FormatSpec & f); std::string FormatValA(const signed long long & x, const FormatSpec & f); std::string FormatValA(const unsigned long long & x, const FormatSpec & f); std::string FormatValA(const float & x, const FormatSpec & f); std::string FormatValA(const double & x, const FormatSpec & f); std::string FormatValA(const long double & x, const FormatSpec & f); mpt::ustring FormatValU(const char & x, const FormatSpec & f) = delete; // deprecated to catch potential API mis-use, use std::string(1, x) instead #if !defined(MPT_COMPILER_QUIRK_NO_WCHAR) mpt::ustring FormatValU(const wchar_t & x, const FormatSpec & f) = delete; // deprecated to catch potential API mis-use, use std::wstring(1, x) instead #endif // !MPT_COMPILER_QUIRK_NO_WCHAR mpt::ustring FormatValU(const bool & x, const FormatSpec & f); mpt::ustring FormatValU(const signed char & x, const FormatSpec & f); mpt::ustring FormatValU(const unsigned char & x, const FormatSpec & f); mpt::ustring FormatValU(const signed short & x, const FormatSpec & f); mpt::ustring FormatValU(const unsigned short & x, const FormatSpec & f); mpt::ustring FormatValU(const signed int & x, const FormatSpec & f); mpt::ustring FormatValU(const unsigned int & x, const FormatSpec & f); mpt::ustring FormatValU(const signed long & x, const FormatSpec & f); mpt::ustring FormatValU(const unsigned long & x, const FormatSpec & f); mpt::ustring FormatValU(const signed long long & x, const FormatSpec & f); mpt::ustring FormatValU(const unsigned long long & x, const FormatSpec & f); mpt::ustring FormatValU(const float & x, const FormatSpec & f); mpt::ustring FormatValU(const double & x, const FormatSpec & f); mpt::ustring FormatValU(const long double & x, const FormatSpec & f); #if MPT_WSTRING_FORMAT std::wstring FormatValW(const char & x, const FormatSpec & f) = delete; // deprecated to catch potential API mis-use, use std::string(1, x) instead #if !defined(MPT_COMPILER_QUIRK_NO_WCHAR) std::wstring FormatValW(const wchar_t & x, const FormatSpec & f) = delete; // deprecated to catch potential API mis-use, use std::wstring(1, x) instead #endif // !MPT_COMPILER_QUIRK_NO_WCHAR std::wstring FormatValW(const bool & x, const FormatSpec & f); std::wstring FormatValW(const signed char & x, const FormatSpec & f); std::wstring FormatValW(const unsigned char & x, const FormatSpec & f); std::wstring FormatValW(const signed short & x, const FormatSpec & f); std::wstring FormatValW(const unsigned short & x, const FormatSpec & f); std::wstring FormatValW(const signed int & x, const FormatSpec & f); std::wstring FormatValW(const unsigned int & x, const FormatSpec & f); std::wstring FormatValW(const signed long & x, const FormatSpec & f); std::wstring FormatValW(const unsigned long & x, const FormatSpec & f); std::wstring FormatValW(const signed long long & x, const FormatSpec & f); std::wstring FormatValW(const unsigned long long & x, const FormatSpec & f); std::wstring FormatValW(const float & x, const FormatSpec & f); std::wstring FormatValW(const double & x, const FormatSpec & f); std::wstring FormatValW(const long double & x, const FormatSpec & f); #endif template struct FormatValTFunctor {}; template <> struct FormatValTFunctor { template inline std::string operator() (const T & x, const FormatSpec & f) { return FormatValA(x, f); } }; template <> struct FormatValTFunctor { template inline mpt::ustring operator() (const T & x, const FormatSpec & f) { return FormatValU(x, f); } }; #if MPT_USTRING_MODE_UTF8 && MPT_WSTRING_FORMAT template <> struct FormatValTFunctor { template inline std::wstring operator() (const T & x, const FormatSpec & f) { return FormatValW(x, f); } }; #endif #if defined(MPT_ENABLE_CHARSET_LOCALE) template <> struct FormatValTFunctor { template inline mpt::lstring operator() (const T & x, const FormatSpec & f) { return mpt::ToLocale(mpt::Charset::Locale, FormatValA(x, f)); } }; #endif // MPT_ENABLE_CHARSET_LOCALE #if defined(MPT_WITH_MFC) #ifdef UNICODE template <> struct FormatValTFunctor { template inline CString operator() (const T & x, const FormatSpec & f) { return mpt::ToCString(FormatValW(x, f)); } }; #else // !UNICODE template <> struct FormatValTFunctor { template inline CString operator() (const T & x, const FormatSpec & f) { return mpt::ToCString(mpt::Charset::Locale, FormatValA(x, f)); } }; #endif // UNICODE #endif // MPT_WITH_MFC template struct fmtT : fmt_base { template static inline Tstring val(const T& x) { return ToStringTFunctor()(x); } template static inline Tstring fmt(const T& x, const FormatSpec& f) { return FormatValTFunctor()(x, f); } template static inline Tstring dec(const T& x) { static_assert(std::numeric_limits::is_integer); return FormatValTFunctor()(x, FormatSpec().BaseDec().FillOff()); } template static inline Tstring dec0(const T& x) { static_assert(std::numeric_limits::is_integer); return FormatValTFunctor()(x, FormatSpec().BaseDec().FillNul().Width(width)); } template static inline Tstring dec(unsigned int g, char s, const T& x) { static_assert(std::numeric_limits::is_integer); return FormatValTFunctor()(x, FormatSpec().BaseDec().FillOff().Group(g).GroupSep(s)); } template static inline Tstring dec0(unsigned int g, char s, const T& x) { static_assert(std::numeric_limits::is_integer); return FormatValTFunctor()(x, FormatSpec().BaseDec().FillNul().Width(width).Group(g).GroupSep(s)); } template static inline Tstring hex(const T& x) { static_assert(std::numeric_limits::is_integer); return FormatValTFunctor()(x, FormatSpec().BaseHex().CaseLow().FillOff()); } template static inline Tstring HEX(const T& x) { static_assert(std::numeric_limits::is_integer); return FormatValTFunctor()(x, FormatSpec().BaseHex().CaseUpp().FillOff()); } template static inline Tstring hex0(const T& x) { static_assert(std::numeric_limits::is_integer); return FormatValTFunctor()(x, FormatSpec().BaseHex().CaseLow().FillNul().Width(width)); } template static inline Tstring HEX0(const T& x) { static_assert(std::numeric_limits::is_integer); return FormatValTFunctor()(x, FormatSpec().BaseHex().CaseUpp().FillNul().Width(width)); } template static inline Tstring hex(unsigned int g, char s, const T& x) { static_assert(std::numeric_limits::is_integer); return FormatValTFunctor()(x, FormatSpec().BaseHex().CaseLow().FillOff().Group(g).GroupSep(s)); } template static inline Tstring HEX(unsigned int g, char s, const T& x) { static_assert(std::numeric_limits::is_integer); return FormatValTFunctor()(x, FormatSpec().BaseHex().CaseUpp().FillOff().Group(g).GroupSep(s)); } template static inline Tstring hex0(unsigned int g, char s, const T& x) { static_assert(std::numeric_limits::is_integer); return FormatValTFunctor()(x, FormatSpec().BaseHex().CaseLow().FillNul().Width(width).Group(g).GroupSep(s)); } template static inline Tstring HEX0(unsigned int g, char s, const T& x) { static_assert(std::numeric_limits::is_integer); return FormatValTFunctor()(x, FormatSpec().BaseHex().CaseUpp().FillNul().Width(width).Group(g).GroupSep(s)); } template static inline Tstring flt(const T& x, int precision = -1) { static_assert(std::is_floating_point::value); return FormatValTFunctor()(x, FormatSpec().NotaNrm().FillOff().Precision(precision)); } template static inline Tstring fix(const T& x, int precision = -1) { static_assert(std::is_floating_point::value); return FormatValTFunctor()(x, FormatSpec().NotaFix().FillOff().Precision(precision)); } template static inline Tstring sci(const T& x, int precision = -1) { static_assert(std::is_floating_point::value); return FormatValTFunctor()(x, FormatSpec().NotaSci().FillOff().Precision(precision)); } template static inline Tstring ptr(const T& x) { static_assert(std::is_pointer::value || std::is_same::value || std::is_same::value, ""); return hex0(mpt::pointer_cast(x)); } template static inline Tstring PTR(const T& x) { static_assert(std::is_pointer::value || std::is_same::value || std::is_same::value, ""); return HEX0(mpt::pointer_cast(x)); } static inline Tstring pad_left(std::size_t width_, const Tstring &str) { typedef mpt::string_traits traits; typename traits::size_type width = static_cast(width_); return traits::pad(str, width, 0); } static inline Tstring pad_right(std::size_t width_, const Tstring &str) { typedef mpt::string_traits traits; typename traits::size_type width = static_cast(width_); return traits::pad(str, 0, width); } static inline Tstring left(std::size_t width_, const Tstring &str) { typedef mpt::string_traits traits; typename traits::size_type width = static_cast(width_); return (traits::length(str) < width) ? traits::pad(str, 0, width - traits::length(str)) : str; } static inline Tstring right(std::size_t width_, const Tstring &str) { typedef mpt::string_traits traits; typename traits::size_type width = static_cast(width_); return (traits::length(str) < width) ? traits::pad(str, width - traits::length(str), 0) : str; } static inline Tstring center(std::size_t width_, const Tstring &str) { typedef mpt::string_traits traits; typename traits::size_type width = static_cast(width_); return (traits::length(str) < width) ? traits::pad(str, (width - traits::length(str)) / 2, (width - traits::length(str) + 1) / 2) : str; } }; // struct fmtT typedef fmtT afmt; #if MPT_WSTRING_FORMAT typedef fmtT wfmt; #endif #if MPT_USTRING_MODE_WIDE typedef fmtT ufmt; #else typedef fmtT ufmt; #endif #if defined(MPT_ENABLE_CHARSET_LOCALE) typedef fmtT lfmt; #endif // MPT_ENABLE_CHARSET_LOCALE #if MPT_OS_WINDOWS typedef fmtT tfmt; #endif #if defined(MPT_WITH_MFC) typedef fmtT cfmt; #endif // MPT_WITH_MFC #define MPT_AFORMAT(f) mpt::format_message(f) #if MPT_WSTRING_FORMAT #define MPT_WFORMAT(f) mpt::format_message_typed( L ## f ) #endif #define MPT_UFORMAT(f) mpt::format_message_typed(MPT_ULITERAL(f)) #if defined(MPT_ENABLE_CHARSET_LOCALE) #define MPT_LFORMAT(f) mpt::format_message_typed(f) #endif // MPT_ENABLE_CHARSET_LOCALE #if MPT_OS_WINDOWS #define MPT_TFORMAT(f) mpt::format_message_typed(TEXT(f)) #endif #if defined(MPT_WITH_MFC) #define MPT_CFORMAT(f) mpt::format_message_typed(TEXT(f)) #endif // MPT_WITH_MFC } // namespace mpt namespace mpt { namespace String { // Combine a vector of values into a string, separated with the given separator. // No escaping is performed. template mpt::ustring Combine(const std::vector &vals, const mpt::ustring &sep=U_(",")) { mpt::ustring str; for(std::size_t i = 0; i < vals.size(); ++i) { if(i > 0) { str += sep; } str += mpt::ufmt::val(vals[i]); } return str; } template std::string Combine(const std::vector &vals, const std::string &sep=std::string(",")) { std::string str; for(std::size_t i = 0; i < vals.size(); ++i) { if(i > 0) { str += sep; } str += mpt::afmt::val(vals[i]); } return str; } } } // namespace mpt::String template mpt::ustring ToUString(FlagSet flagset) { mpt::ustring str(flagset.size_bits(), UC_('0')); for(std::size_t x = 0; x < flagset.size_bits(); ++x) { str[flagset.size_bits() - x - 1] = (flagset.value().as_bits() & (static_cast::store_type>(1) << x) ? UC_('1') : UC_('0')); } return str; } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/common/mptStringParse.cpp0000644000175000017500000001246714044173026021245 00000000000000/* * mptStringParse.cpp * ------------------ * Purpose: Convert strings to other types. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "mptStringParse.h" #include "mpt/parse/parse.hpp" OPENMPT_NAMESPACE_BEGIN template inline T ConvertStrToHelper(const std::string &str) { return mpt::ConvertStringTo(str); } template<> inline bool ConvertStrToHelper(const std::string &str) { return ConvertStrToHelper(str)?true:false; } template<> inline signed char ConvertStrToHelper(const std::string &str) { return static_cast(ConvertStrToHelper(str)); } template<> inline unsigned char ConvertStrToHelper(const std::string &str) { return static_cast(ConvertStrToHelper(str)); } #if MPT_WSTRING_FORMAT template inline T ConvertStrToHelper(const std::wstring &str) { return mpt::ConvertStringTo(str); } template<> inline bool ConvertStrToHelper(const std::wstring &str) { return ConvertStrToHelper(str)?true:false; } template<> inline signed char ConvertStrToHelper(const std::wstring &str) { return static_cast(ConvertStrToHelper(str)); } template<> inline unsigned char ConvertStrToHelper(const std::wstring &str) { return static_cast(ConvertStrToHelper(str)); } #endif bool ConvertStrToBool(const std::string &str) { return ConvertStrToHelper(str); } signed char ConvertStrToSignedChar(const std::string &str) { return ConvertStrToHelper(str); } unsigned char ConvertStrToUnsignedChar(const std::string &str) { return ConvertStrToHelper(str); } signed short ConvertStrToSignedShort(const std::string &str) { return ConvertStrToHelper(str); } unsigned short ConvertStrToUnsignedShort(const std::string &str) { return ConvertStrToHelper(str); } signed int ConvertStrToSignedInt(const std::string &str) { return ConvertStrToHelper(str); } unsigned int ConvertStrToUnsignedInt(const std::string &str) { return ConvertStrToHelper(str); } signed long ConvertStrToSignedLong(const std::string &str) { return ConvertStrToHelper(str); } unsigned long ConvertStrToUnsignedLong(const std::string &str) { return ConvertStrToHelper(str); } signed long long ConvertStrToSignedLongLong(const std::string &str) { return ConvertStrToHelper(str); } unsigned long long ConvertStrToUnsignedLongLong(const std::string &str) { return ConvertStrToHelper(str); } float ConvertStrToFloat(const std::string &str) { return ConvertStrToHelper(str); } double ConvertStrToDouble(const std::string &str) { return ConvertStrToHelper(str); } long double ConvertStrToLongDouble(const std::string &str) { return ConvertStrToHelper(str); } #if MPT_WSTRING_FORMAT bool ConvertStrToBool(const std::wstring &str) { return ConvertStrToHelper(str); } signed char ConvertStrToSignedChar(const std::wstring &str) { return ConvertStrToHelper(str); } unsigned char ConvertStrToUnsignedChar(const std::wstring &str) { return ConvertStrToHelper(str); } signed short ConvertStrToSignedShort(const std::wstring &str) { return ConvertStrToHelper(str); } unsigned short ConvertStrToUnsignedShort(const std::wstring &str) { return ConvertStrToHelper(str); } signed int ConvertStrToSignedInt(const std::wstring &str) { return ConvertStrToHelper(str); } unsigned int ConvertStrToUnsignedInt(const std::wstring &str) { return ConvertStrToHelper(str); } signed long ConvertStrToSignedLong(const std::wstring &str) { return ConvertStrToHelper(str); } unsigned long ConvertStrToUnsignedLong(const std::wstring &str) { return ConvertStrToHelper(str); } signed long long ConvertStrToSignedLongLong(const std::wstring &str) { return ConvertStrToHelper(str); } unsigned long long ConvertStrToUnsignedLongLong(const std::wstring &str) { return ConvertStrToHelper(str); } float ConvertStrToFloat(const std::wstring &str) { return ConvertStrToHelper(str); } double ConvertStrToDouble(const std::wstring &str) { return ConvertStrToHelper(str); } long double ConvertStrToLongDouble(const std::wstring &str) { return ConvertStrToHelper(str); } #endif namespace mpt { namespace String { namespace Parse { template T HexToHelper(const std::string &str) { return mpt::ConvertHexStringTo(str); } template<> unsigned char HexToHelper(const std::string &str) { return static_cast(HexToHelper(str)); } unsigned char HexToUnsignedChar(const std::string &str) { return HexToHelper(str); } unsigned short HexToUnsignedShort(const std::string &str) { return HexToHelper(str); } unsigned int HexToUnsignedInt(const std::string &str) { return HexToHelper(str); } unsigned long HexToUnsignedLong(const std::string &str) { return HexToHelper(str); } unsigned long long HexToUnsignedLongLong(const std::string &str) { return HexToHelper(str); } } // namespace Parse } // namespace String } // namespace mpt OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/common/mptStringParse.h0000644000175000017500000002320414052666041020704 00000000000000/* * mptStringParse.h * ---------------- * Purpose: Convert strings to other types. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" OPENMPT_NAMESPACE_BEGIN bool ConvertStrToBool(const std::string &str); signed char ConvertStrToSignedChar(const std::string &str); unsigned char ConvertStrToUnsignedChar(const std::string &str); signed short ConvertStrToSignedShort(const std::string &str); unsigned short ConvertStrToUnsignedShort(const std::string &str); signed int ConvertStrToSignedInt(const std::string &str); unsigned int ConvertStrToUnsignedInt(const std::string &str); signed long ConvertStrToSignedLong(const std::string &str); unsigned long ConvertStrToUnsignedLong(const std::string &str); signed long long ConvertStrToSignedLongLong(const std::string &str); unsigned long long ConvertStrToUnsignedLongLong(const std::string &str); float ConvertStrToFloat(const std::string &str); double ConvertStrToDouble(const std::string &str); long double ConvertStrToLongDouble(const std::string &str); template inline T ConvertStrTo(const std::string &str); // not defined, generates compiler error for non-specialized types template<> inline std::string ConvertStrTo(const std::string &str) { return str; } template<> inline bool ConvertStrTo(const std::string &str) { return ConvertStrToBool(str); } template<> inline signed char ConvertStrTo(const std::string &str) { return ConvertStrToSignedChar(str); } template<> inline unsigned char ConvertStrTo(const std::string &str) { return ConvertStrToUnsignedChar(str); } template<> inline signed short ConvertStrTo(const std::string &str) { return ConvertStrToSignedShort(str); } template<> inline unsigned short ConvertStrTo(const std::string &str) { return ConvertStrToUnsignedShort(str); } template<> inline signed int ConvertStrTo(const std::string &str) { return ConvertStrToSignedInt(str); } template<> inline unsigned int ConvertStrTo(const std::string &str) { return ConvertStrToUnsignedInt(str); } template<> inline signed long ConvertStrTo(const std::string &str) { return ConvertStrToSignedLong(str); } template<> inline unsigned long ConvertStrTo(const std::string &str) { return ConvertStrToUnsignedLong(str); } template<> inline signed long long ConvertStrTo(const std::string &str) { return ConvertStrToSignedLongLong(str); } template<> inline unsigned long long ConvertStrTo(const std::string &str) { return ConvertStrToUnsignedLongLong(str); } template<> inline float ConvertStrTo(const std::string &str) { return ConvertStrToFloat(str); } template<> inline double ConvertStrTo(const std::string &str) { return ConvertStrToDouble(str); } template<> inline long double ConvertStrTo(const std::string &str) { return ConvertStrToLongDouble(str); } #if MPT_WSTRING_FORMAT bool ConvertStrToBool(const std::wstring &str); signed char ConvertStrToSignedChar(const std::wstring &str); unsigned char ConvertStrToUnsignedChar(const std::wstring &str); signed short ConvertStrToSignedShort(const std::wstring &str); unsigned short ConvertStrToUnsignedShort(const std::wstring &str); signed int ConvertStrToSignedInt(const std::wstring &str); unsigned int ConvertStrToUnsignedInt(const std::wstring &str); signed long ConvertStrToSignedLong(const std::wstring &str); unsigned long ConvertStrToUnsignedLong(const std::wstring &str); signed long long ConvertStrToSignedLongLong(const std::wstring &str); unsigned long long ConvertStrToUnsignedLongLong(const std::wstring &str); float ConvertStrToFloat(const std::wstring &str); double ConvertStrToDouble(const std::wstring &str); long double ConvertStrToLongDouble(const std::wstring &str); template inline T ConvertStrTo(const std::wstring &str); // not defined, generates compiler error for non-specialized types template<> inline std::wstring ConvertStrTo(const std::wstring &str) { return str; } template<> inline bool ConvertStrTo(const std::wstring &str) { return ConvertStrToBool(str); } template<> inline signed char ConvertStrTo(const std::wstring &str) { return ConvertStrToSignedChar(str); } template<> inline unsigned char ConvertStrTo(const std::wstring &str) { return ConvertStrToUnsignedChar(str); } template<> inline signed short ConvertStrTo(const std::wstring &str) { return ConvertStrToSignedShort(str); } template<> inline unsigned short ConvertStrTo(const std::wstring &str) { return ConvertStrToUnsignedShort(str); } template<> inline signed int ConvertStrTo(const std::wstring &str) { return ConvertStrToSignedInt(str); } template<> inline unsigned int ConvertStrTo(const std::wstring &str) { return ConvertStrToUnsignedInt(str); } template<> inline signed long ConvertStrTo(const std::wstring &str) { return ConvertStrToSignedLong(str); } template<> inline unsigned long ConvertStrTo(const std::wstring &str) { return ConvertStrToUnsignedLong(str); } template<> inline signed long long ConvertStrTo(const std::wstring &str) { return ConvertStrToSignedLongLong(str); } template<> inline unsigned long long ConvertStrTo(const std::wstring &str) { return ConvertStrToUnsignedLongLong(str); } template<> inline float ConvertStrTo(const std::wstring &str) { return ConvertStrToFloat(str); } template<> inline double ConvertStrTo(const std::wstring &str) { return ConvertStrToDouble(str); } template<> inline long double ConvertStrTo(const std::wstring &str) { return ConvertStrToLongDouble(str); } #endif #if defined(MPT_WITH_MFC) template inline T ConvertStrTo(const CString &str) { #if defined(UNICODE) && MPT_WSTRING_FORMAT return ConvertStrTo(mpt::ToWide(str)); #elif defined(UNICODE) return ConvertStrTo(mpt::ToCharset(mpt::Charset::UTF8, str)); #else // !UNICODE return ConvertStrTo(mpt::ToCharset(mpt::Charset::Locale, str)); #endif // UNICODE } #endif // MPT_WITH_MFC template inline T ConvertStrTo(const char *str) { if(!str) { return T(); } return ConvertStrTo(std::string(str)); } #if MPT_WSTRING_FORMAT #if MPT_USTRING_MODE_UTF8 template<> inline mpt::ustring ConvertStrTo(const std::wstring &str) { return mpt::ToUnicode(str); } #endif template inline T ConvertStrTo(const wchar_t *str) { if(!str) { return T(); } return ConvertStrTo(std::wstring(str)); } #endif #if MPT_USTRING_MODE_UTF8 template inline T ConvertStrTo(const mpt::ustring &str) { return ConvertStrTo(mpt::ToCharset(mpt::Charset::UTF8, str)); } template<> inline mpt::ustring ConvertStrTo(const mpt::ustring &str) { return str; } #if MPT_WSTRING_CONVERT template<> inline std::wstring ConvertStrTo(const mpt::ustring &str) { return mpt::ToWide(str); } #endif #endif #if defined(MPT_ENABLE_CHARSET_LOCALE) template inline T ConvertStrTo(const mpt::lstring &str) { return ConvertStrTo(mpt::ToCharset(mpt::Charset::Locale, str)); } template<> inline mpt::lstring ConvertStrTo(const mpt::lstring &str) { return str; } #endif namespace mpt { namespace String { namespace Parse { unsigned char HexToUnsignedChar(const std::string &str); unsigned short HexToUnsignedShort(const std::string &str); unsigned int HexToUnsignedInt(const std::string &str); unsigned long HexToUnsignedLong(const std::string &str); unsigned long long HexToUnsignedLongLong(const std::string &str); template inline T Hex(const std::string &str); // not defined, generates compiler error for non-specialized types template<> inline unsigned char Hex(const std::string &str) { return HexToUnsignedChar(str); } template<> inline unsigned short Hex(const std::string &str) { return HexToUnsignedShort(str); } template<> inline unsigned int Hex(const std::string &str) { return HexToUnsignedInt(str); } template<> inline unsigned long Hex(const std::string &str) { return HexToUnsignedLong(str); } template<> inline unsigned long long Hex(const std::string &str) { return HexToUnsignedLongLong(str); } template inline T Hex(const char *str) { if(!str) { return T(); } return Hex(std::string(str)); } #if MPT_WSTRING_FORMAT template inline T Hex(const std::wstring &str) { return Hex(mpt::ToCharset(mpt::Charset::UTF8, str)); } template inline T Hex(const wchar_t *str) { if(!str) { return T(); } return Hex(std::wstring(str)); } #endif #if MPT_USTRING_MODE_UTF8 template inline T Hex(const mpt::ustring &str) { return Hex(mpt::ToCharset(mpt::Charset::UTF8, str)); } #endif } // namespace Parse } // namespace String } // namespace mpt namespace mpt { namespace String { // Split the given string at separator positions into individual values returned as a vector. // An empty string results in an empty vector. // Leading or trailing separators result in a default-constructed element being inserted before or after the other elements. template std::vector Split(const mpt::ustring &str, const mpt::ustring &sep=U_(",")) { std::vector vals; std::size_t pos = 0; while(str.find(sep, pos) != std::string::npos) { vals.push_back(ConvertStrTo(str.substr(pos, str.find(sep, pos) - pos))); pos = str.find(sep, pos) + sep.length(); } if(!vals.empty() || (str.substr(pos).length() > 0)) { vals.push_back(ConvertStrTo(str.substr(pos))); } return vals; } template std::vector Split(const std::string &str, const std::string &sep=std::string(",")) { std::vector vals; std::size_t pos = 0; while(str.find(sep, pos) != std::string::npos) { vals.push_back(ConvertStrTo(str.substr(pos, str.find(sep, pos) - pos))); pos = str.find(sep, pos) + sep.length(); } if(!vals.empty() || (str.substr(pos).length() > 0)) { vals.push_back(ConvertStrTo(str.substr(pos))); } return vals; } } } // namespace mpt::String OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/common/mptTime.cpp0000644000175000017500000001340114050176023017664 00000000000000/* * mptTime.cpp * ----------- * Purpose: Various time utility functions. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "mptTime.h" #include "mptStringBuffer.h" #include #if MPT_OS_WINDOWS #include #if defined(MODPLUG_TRACKER) #include #endif #endif OPENMPT_NAMESPACE_BEGIN namespace mpt { namespace Date { #if defined(MODPLUG_TRACKER) #if MPT_OS_WINDOWS namespace ANSI { uint64 Now() { FILETIME filetime; GetSystemTimeAsFileTime(&filetime); return ((uint64)filetime.dwHighDateTime << 32 | filetime.dwLowDateTime); } mpt::ustring ToUString(uint64 time100ns) { constexpr std::size_t bufsize = 256; mpt::ustring result; FILETIME filetime; SYSTEMTIME systime; filetime.dwHighDateTime = (DWORD)(((uint64)time100ns) >> 32); filetime.dwLowDateTime = (DWORD)((uint64)time100ns); FileTimeToSystemTime(&filetime, &systime); TCHAR buf[bufsize]; GetDateFormat(LOCALE_SYSTEM_DEFAULT, 0, &systime, TEXT("yyyy-MM-dd"), buf, bufsize); result.append(mpt::ToUnicode(mpt::String::ReadWinBuf(buf))); result.append(U_(" ")); GetTimeFormat(LOCALE_SYSTEM_DEFAULT, TIME_FORCE24HOURFORMAT, &systime, TEXT("HH:mm:ss"), buf, bufsize); result.append(mpt::ToUnicode(mpt::String::ReadWinBuf(buf))); result.append(U_(".")); result.append(mpt::ufmt::dec0<3>((unsigned)systime.wMilliseconds)); return result; } } // namespace ANSI #endif // MPT_OS_WINDOWS #endif // MODPLUG_TRACKER Unix::Unix() : Value(0) { return; } Unix::Unix(int64 unixtime) : Value(unixtime) { return; } Unix::operator int64 () const { return Value; } static int32 ToDaynum(int32 year, int32 month, int32 day) { month = (month + 9) % 12; year = year - (month / 10); int32 daynum = year*365 + year/4 - year/100 + year/400 + (month*306 + 5)/10 + (day - 1); return daynum; } static void FromDaynum(int32 d, int32 & year, int32 & month, int32 & day) { int64 g = d; int64 y,ddd,mi,mm,dd; y = (10000*g + 14780)/3652425; ddd = g - (365*y + y/4 - y/100 + y/400); if(ddd < 0) { y = y - 1; ddd = g - (365*y + y/4 - y/100 + y/400); } mi = (100*ddd + 52)/3060; mm = (mi + 2)%12 + 1; y = y + (mi + 2)/12; dd = ddd - (mi*306 + 5)/10 + 1; year = static_cast(y); month = static_cast(mm); day = static_cast(dd); } mpt::Date::Unix Unix::FromUTC(tm timeUtc) { int32 daynum = ToDaynum(timeUtc.tm_year+1900, timeUtc.tm_mon+1, timeUtc.tm_mday); int64 seconds = static_cast(daynum - ToDaynum(1970,1,1))*24*60*60 + timeUtc.tm_hour*60*60 + timeUtc.tm_min*60 + timeUtc.tm_sec; return mpt::Date::Unix(seconds); } tm Unix::AsUTC() const { int64 tmp = Value; int64 seconds = tmp % 60; tmp /= 60; int64 minutes = tmp % 60; tmp /= 60; int64 hours = tmp % 24; tmp /= 24; int32 year = 0, month = 0, day = 0; FromDaynum(static_cast(tmp) + ToDaynum(1970,1,1), year, month, day); tm result = {}; result.tm_year = year - 1900; result.tm_mon = month - 1; result.tm_mday = day; result.tm_hour = static_cast(hours); result.tm_min = static_cast(minutes); result.tm_sec = static_cast(seconds); return result; } mpt::ustring ToShortenedISO8601(tm date) { // We assume date in UTC here. // There are too many differences in supported format specifiers in strftime() // and strftime does not support reduced precision ISO8601 at all. // Just do the formatting ourselves. mpt::ustring result; mpt::ustring tz = U_("Z"); if(date.tm_year == 0) { return result; } result += mpt::ufmt::dec0<4>(date.tm_year + 1900); if(date.tm_mon < 0 || date.tm_mon > 11) { return result; } result += U_("-") + mpt::ufmt::dec0<2>(date.tm_mon + 1); if(date.tm_mday < 1 || date.tm_mday > 31) { return result; } result += U_("-") + mpt::ufmt::dec0<2>(date.tm_mday); if(date.tm_hour == 0 && date.tm_min == 0 && date.tm_sec == 0) { return result; } if(date.tm_hour < 0 || date.tm_hour > 23) { return result; } if(date.tm_min < 0 || date.tm_min > 59) { return result; } result += U_("T"); if(date.tm_isdst > 0) { tz = U_("+01:00"); } result += mpt::ufmt::dec0<2>(date.tm_hour) + U_(":") + mpt::ufmt::dec0<2>(date.tm_min); if(date.tm_sec < 0 || date.tm_sec > 61) { return result + tz; } result += U_(":") + mpt::ufmt::dec0<2>(date.tm_sec); result += tz; return result; } } // namespace Date } // namespace mpt #ifdef MODPLUG_TRACKER namespace Util { #if MPT_OS_WINDOWS void MultimediaClock::Init() { m_CurrentPeriod = 0; } void MultimediaClock::SetPeriod(uint32 ms) { TIMECAPS caps = {}; if(timeGetDevCaps(&caps, sizeof(caps)) != MMSYSERR_NOERROR) { return; } if((caps.wPeriodMax == 0) || (caps.wPeriodMin > caps.wPeriodMax)) { return; } ms = std::clamp(mpt::saturate_cast(ms), caps.wPeriodMin, caps.wPeriodMax); if(timeBeginPeriod(ms) != MMSYSERR_NOERROR) { return; } m_CurrentPeriod = ms; } void MultimediaClock::Cleanup() { if(m_CurrentPeriod > 0) { if(timeEndPeriod(m_CurrentPeriod) != MMSYSERR_NOERROR) { // should not happen MPT_ASSERT_NOTREACHED(); } m_CurrentPeriod = 0; } } MultimediaClock::MultimediaClock() { Init(); } MultimediaClock::MultimediaClock(uint32 ms) { Init(); SetResolution(ms); } MultimediaClock::~MultimediaClock() { Cleanup(); } uint32 MultimediaClock::SetResolution(uint32 ms) { if(m_CurrentPeriod == ms) { return m_CurrentPeriod; } Cleanup(); if(ms != 0) { SetPeriod(ms); } return GetResolution(); } uint32 MultimediaClock::GetResolution() const { return m_CurrentPeriod; } uint32 MultimediaClock::Now() const { return timeGetTime(); } uint64 MultimediaClock::NowNanoseconds() const { return (uint64)timeGetTime() * (uint64)1000000; } #endif // MPT_OS_WINDOWS } // namespace Util #endif // MODPLUG_TRACKER OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/common/mptTime.h0000644000175000017500000000502614052666041017343 00000000000000/* * mptTime.h * --------- * Purpose: Various time utility functions. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include #include OPENMPT_NAMESPACE_BEGIN namespace mpt { namespace Date { #if defined(MODPLUG_TRACKER) #if MPT_OS_WINDOWS namespace ANSI { // uint64 counts 100ns since 1601-01-01T00:00Z uint64 Now(); mpt::ustring ToUString(uint64 time100ns); // i.e. 2015-01-15 18:32:01.718 } // namespacee ANSI #endif // MPT_OS_WINDOWS #endif // MODPLUG_TRACKER class Unix { // int64 counts 1s since 1970-01-01T00:00Z private: int64 Value; public: Unix(); explicit Unix(int64 unixtime); operator int64 () const; public: static mpt::Date::Unix FromUTC(tm timeUtc); tm AsUTC() const; }; mpt::ustring ToShortenedISO8601(tm date); // i.e. 2015-01-15T18:32:01Z } // namespace Date } // namespace mpt #ifdef MODPLUG_TRACKER namespace Util { #if MPT_OS_WINDOWS // RAII wrapper around timeBeginPeriod/timeEndPeriod/timeGetTime (on Windows). // This clock is monotonic, even across changing its resolution. // This is needed to synchronize time in Steinberg APIs (ASIO and VST). class MultimediaClock { private: uint32 m_CurrentPeriod; private: void Init(); void SetPeriod(uint32 ms); void Cleanup(); public: MultimediaClock(); MultimediaClock(uint32 ms); ~MultimediaClock(); public: // Sets the desired resolution in milliseconds, returns the obtained resolution in milliseconds. // A parameter of 0 causes the resolution to be reset to system defaults. // A return value of 0 means the resolution is unknown, but timestamps will still be valid. uint32 SetResolution(uint32 ms); // Returns obtained resolution in milliseconds. // A return value of 0 means the resolution is unknown, but timestamps will still be valid. uint32 GetResolution() const; // Returns current instantaneous timestamp in milliseconds. // The epoch (offset) of the timestamps is undefined but constant until the next system reboot. // The resolution is the value returned from GetResolution(). uint32 Now() const; // Returns current instantaneous timestamp in nanoseconds. // The epoch (offset) of the timestamps is undefined but constant until the next system reboot. // The resolution is the value returned from GetResolution() in milliseconds. uint64 NowNanoseconds() const; }; #endif // MPT_OS_WINDOWS } // namespace Util #endif // MODPLUG_TRACKER OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/common/Profiler.cpp0000644000175000017500000000707414053176735020055 00000000000000/* * Profiler.cpp * ------------ * Purpose: Performance measuring * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Profiler.h" OPENMPT_NAMESPACE_BEGIN #ifdef USE_PROFILER class Statistics { public: Profile &profile; Profile::Data data; double usage; Statistics(Profile &p) : profile(p) { usage = 0.0; Update(); } void Update() { data = profile.GetAndResetData(); uint64 now = profile.GetTime(); uint64 timewindow = now - data.Start; if(data.Calls > 0 && timewindow > 0) { usage = (double)data.Sum / (double)timewindow; } else { usage = 0.0; } } }; struct ProfileBlock { class Profile * profile; const char * name; class Statistics * stats; }; static constexpr std::size_t MAX_PROFILES = 1024; static ProfileBlock Profiles[ MAX_PROFILES ]; static std::size_t NextProfile = 0; static void RegisterProfile(Profile *newprofile) { if(NextProfile < MAX_PROFILES) { Profiles[NextProfile].profile = newprofile; Profiles[NextProfile].stats = 0; NextProfile++; } } static void UnregisterProfile(Profile *oldprofile) { for(std::size_t i=0; iUpdate(); } } } std::string Profiler::DumpProfiles() { std::string ret; for(std::size_t i=0; i Profiler::DumpCategories() { std::vector ret; ret.resize(Profiler::CategoriesCount); for(std::size_t i=0; iCategory] += Profiles[i].stats->usage; } } return ret; } uint64 Profile::GetTime() const { LARGE_INTEGER ret; ret.QuadPart = 0; QueryPerformanceCounter(&ret); return ret.QuadPart; } uint64 Profile::GetFrequency() const { LARGE_INTEGER ret; ret.QuadPart = 0; QueryPerformanceFrequency(&ret); return ret.QuadPart; } Profile::Profile(Profiler::Category category, const char *name) : Category(category), Name(name) { data.Calls = 0; data.Sum = 0; data.Overhead = 0; data.Start = GetTime(); EnterTime = 0; RegisterProfile(this); } Profile::~Profile() { UnregisterProfile(this); } Profile::Data Profile::GetAndResetData() { Profile::Data ret; datamutex.lock(); ret = data; data.Calls = 0; data.Sum = 0; data.Overhead = 0; data.Start = GetTime(); datamutex.unlock(); return ret; } void Profile::Reset() { datamutex.lock(); data.Calls = 0; data.Sum = 0; data.Overhead = 0; data.Start = GetTime(); datamutex.unlock(); } void Profile::Enter() { EnterTime = GetTime(); } void Profile::Leave() { uint64 LeaveTime = GetTime(); datamutex.lock(); data.Calls += 1; data.Sum += LeaveTime - EnterTime; datamutex.unlock(); } #else // !USE_PROFILER MPT_MSVC_WORKAROUND_LNK4221(Profiler) #endif // USE_PROFILER OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/common/Profiler.h0000644000175000017500000000424214052666041017505 00000000000000/* * Profiler.h * ---------- * Purpose: Performance measuring * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "mpt/mutex/mutex.hpp" #include #include OPENMPT_NAMESPACE_BEGIN #if defined(MODPLUG_TRACKER) //#define USE_PROFILER #endif #ifdef USE_PROFILER class Profiler { public: enum Category { GUI, Audio, Notify, CategoriesCount }; static std::vector GetCategoryNames() { std::vector ret; ret.push_back("GUI"); ret.push_back("Audio"); ret.push_back("Notify"); return ret; } public: static void Update(); static std::string DumpProfiles(); static std::vector DumpCategories(); }; class Profile { private: mutable mpt::mutex datamutex; public: struct Data { uint64 Calls; uint64 Sum; int64 Overhead; uint64 Start; }; public: Data data; uint64 EnterTime; Profiler::Category Category; const char * const Name; uint64 GetTime() const; uint64 GetFrequency() const; public: Profile(Profiler::Category category, const char *name); ~Profile(); void Reset(); void Enter(); void Leave(); class Scope { private: Profile &profile; public: Scope(Profile &p) : profile(p) { profile.Enter(); } ~Scope() { profile.Leave(); } }; public: Data GetAndResetData(); }; #define OPENMPT_PROFILE_SCOPE(cat, name) \ static Profile OPENMPT_PROFILE_VAR(cat, name);\ Profile::Scope OPENMPT_PROFILE_SCOPE_VAR(OPENMPT_PROFILE_VAR); \ /**/ #define OPENMPT_PROFILE_FUNCTION(cat) OPENMPT_PROFILE_SCOPE(cat, __func__) #else // !USE_PROFILER class Profiler { public: enum Category { CategoriesCount }; static std::vector GetCategoryNames() { return std::vector(); } public: static void Update() { } static std::string DumpProfiles() { return std::string(); } static std::vector DumpCategories() { return std::vector(); } }; #define OPENMPT_PROFILE_SCOPE(cat, name) do { } while(0) #define OPENMPT_PROFILE_FUNCTION(cat) do { } while(0) #endif // USE_PROFILER OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/common/serialization_utils.cpp0000644000175000017500000004620014056354456022363 00000000000000/* * serialization_utils.cpp * ----------------------- * Purpose: Serializing data to and from MPTM files. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "serialization_utils.h" #include "mpt/io/io.hpp" #include "mpt/io/io_stdstream.hpp" #include #include #include #include #include "misc_util.h" OPENMPT_NAMESPACE_BEGIN namespace srlztn { #ifdef MPT_ALL_LOGGING #define SSB_LOGGING #endif #ifdef SSB_LOGGING #define SSB_LOG(x) MPT_LOG_GLOBAL(LogDebug, "serialization", x) #else #define SSB_LOG(x) do { } while(0) #endif static const uint8 HeaderId_FlagByte = 0; // Indexing starts from 0. static inline bool Testbit(uint8 val, uint8 bitindex) {return ((val & (1 << bitindex)) != 0);} static inline void Setbit(uint8& val, uint8 bitindex, bool newval) { if(newval) val |= (1 << bitindex); else val &= ~(1 << bitindex); } bool ID::IsPrintable() const { for(std::size_t i = 0; i < m_ID.length(); ++i) { if(m_ID[i] <= 0 || isprint(static_cast(m_ID[i])) == 0) { return false; } } return true; } //Format: First bit tells whether the size indicator is 1 or 2 bytes. static void WriteAdaptive12String(std::ostream& oStrm, const std::string& str) { uint16 s = static_cast(str.size()); LimitMax(s, uint16(std::numeric_limits::max() / 2)); mpt::IO::WriteAdaptiveInt16LE(oStrm, s); oStrm.write(str.c_str(), s); } void WriteItemString(std::ostream& oStrm, const std::string &str) { uint32 id = static_cast(std::min(str.size(), static_cast((std::numeric_limits::max() >> 4)))) << 4; id |= 12; // 12 == 1100b Binarywrite(oStrm, id); id >>= 4; if(id > 0) oStrm.write(str.data(), id); } void ReadItemString(std::istream& iStrm, std::string& str, const DataSize) { // bits 0,1: Bytes per char type: 1,2,3,4. // bits 2,3: Bytes in size indicator, 1,2,3,4 uint32 id = 0; Binaryread(iStrm, id, 1); const uint8 nSizeBytes = (id & 12) >> 2; // 12 == 1100b if (nSizeBytes > 0) { uint8 bytes = std::min(uint8(3), nSizeBytes); uint8 v2 = 0; uint8 v3 = 0; uint8 v4 = 0; if(bytes >= 1) Binaryread(iStrm, v2); if(bytes >= 2) Binaryread(iStrm, v3); if(bytes >= 3) Binaryread(iStrm, v4); id &= 0xff; id |= (v2 << 8) | (v3 << 16) | (v4 << 24); } // Limit to 1 MB. str.resize(std::min(id >> 4, uint32(1000000))); for(size_t i = 0; i < str.size(); i++) iStrm.read(&str[i], 1); id = (id >> 4) - static_cast(str.size()); if(id > 0) iStrm.ignore(id); } mpt::ustring ID::AsString() const { if(IsPrintable()) { return mpt::ToUnicode(mpt::Charset::ISO8859_1, m_ID); } if(m_ID.length() > 8) { return mpt::ustring(); } uint64le val; val.set(0); std::memcpy(&val, m_ID.data(), m_ID.length()); return mpt::ufmt::val(val); } const char Ssb::s_EntryID[3] = {'2','2','8'}; Ssb::Ssb() : m_Status(SNT_NONE) , m_nFixedEntrySize(0) , m_posStart(0) , m_nIdbytes(IdSizeVariable) , m_nCounter(0) , m_Flags((1 << RwfWMapStartPosEntry) + (1 << RwfWMapSizeEntry) + (1 << RwfWVersionNum)) { return; } SsbWrite::SsbWrite(std::ostream& os) : oStrm(os) , m_posEntrycount(0) , m_posMapPosField(0) { return; } SsbRead::SsbRead(std::istream& is) : iStrm(is) , m_nReadVersion(0) , m_rposMapBegin(0) , m_posMapEnd(0) , m_posDataBegin(0) , m_rposEndofHdrData(0) , m_nReadEntrycount(0) , m_nNextReadHint(0) { return; } void SsbWrite::AddWriteNote(const SsbStatus s) { m_Status |= s; SSB_LOG(MPT_UFORMAT("{}: 0x{}")(U_("Write note: "), mpt::ufmt::hex(s))); } void SsbRead::AddReadNote(const SsbStatus s) { m_Status |= s; SSB_LOG(MPT_UFORMAT("{}: 0x{}")(U_("Read note: "), mpt::ufmt::hex(s))); } void SsbRead::AddReadNote(const ReadEntry* const pRe, const NumType nNum) { m_Status |= SNT_PROGRESS; SSB_LOG(MPT_UFORMAT("Read entry: {{num, id, rpos, size, desc}} = {{{}, {}, {}, {}, {}}}")( nNum, (pRe && pRe->nIdLength < 30 && m_Idarray.size() > 0) ? ID(&m_Idarray[pRe->nIdpos], pRe->nIdLength).AsString() : U_(""), (pRe) ? pRe->rposStart : 0, (pRe && pRe->nSize != invalidDatasize) ? mpt::ufmt::val(pRe->nSize) : U_(""), U_(""))); #ifndef SSB_LOGGING MPT_UNREFERENCED_PARAMETER(pRe); MPT_UNREFERENCED_PARAMETER(nNum); #endif } // Called after writing an entry. void SsbWrite::AddWriteNote(const ID &id, const NumType nEntryNum, const DataSize nBytecount, const RposType rposStart) { m_Status |= SNT_PROGRESS; SSB_LOG(MPT_UFORMAT("Wrote entry: {{num, id, rpos, size}} = {{{}, {}, {}, {}}}")(nEntryNum, id.AsString(), rposStart, nBytecount)); #ifndef SSB_LOGGING MPT_UNREFERENCED_PARAMETER(id); MPT_UNREFERENCED_PARAMETER(nEntryNum); MPT_UNREFERENCED_PARAMETER(nBytecount); MPT_UNREFERENCED_PARAMETER(rposStart); #endif } void SsbRead::ResetReadstatus() { m_Status = SNT_NONE; m_Idarray.reserve(32); m_Idarray.push_back(0); } void SsbWrite::WriteMapItem(const ID &id, const RposType& rposDataStart, const DataSize& nDatasize, const char* pszDesc) { SSB_LOG(MPT_UFORMAT("Writing map entry: id={}, rpos={}, size={}")( (id.GetSize() > 0) ? id.AsString() : U_(""), rposDataStart, nDatasize)); std::ostringstream mapStream; if(m_nIdbytes > 0) { if (m_nIdbytes != IdSizeVariable && id.GetSize() != m_nIdbytes) { AddWriteNote(SNW_CHANGING_IDSIZE_WITH_FIXED_IDSIZESETTING); return; } if (m_nIdbytes == IdSizeVariable) //Variablesize ID? mpt::IO::WriteAdaptiveInt16LE(mapStream, static_cast(id.GetSize())); if(id.GetSize() > 0) mapStream.write(id.GetBytes(), id.GetSize()); } if (GetFlag(RwfWMapStartPosEntry)) //Startpos mpt::IO::WriteAdaptiveInt64LE(mapStream, rposDataStart); if (GetFlag(RwfWMapSizeEntry)) //Entrysize mpt::IO::WriteAdaptiveInt64LE(mapStream, nDatasize); if (GetFlag(RwfWMapDescEntry)) //Entry descriptions WriteAdaptive12String(mapStream, std::string(pszDesc)); m_MapStreamString.append(mapStream.str()); } void SsbWrite::IncrementWriteCounter() { m_nCounter++; if(m_nCounter >= static_cast(std::numeric_limits::max() >> 2)) { FinishWrite(); AddWriteNote(SNW_MAX_WRITE_COUNT_REACHED); } } void SsbWrite::BeginWrite(const ID &id, const uint64& nVersion) { SSB_LOG(MPT_UFORMAT("Write header with ID = {}")(id.AsString())); ResetWritestatus(); if(!oStrm.good()) { AddWriteNote(SNRW_BADGIVEN_STREAM); return; } // Start bytes. oStrm.write(s_EntryID, sizeof(s_EntryID)); m_posStart = oStrm.tellp() - Offtype(sizeof(s_EntryID)); // Object ID. { uint8 idsize = static_cast(id.GetSize()); Binarywrite(oStrm, idsize); if(idsize > 0) oStrm.write(id.GetBytes(), id.GetSize()); } // Form header. uint8 header = 0; SetFlag(RwfWMapStartPosEntry, GetFlag(RwfWMapStartPosEntry) && m_nFixedEntrySize == 0); SetFlag(RwfWMapSizeEntry, GetFlag(RwfWMapSizeEntry) && m_nFixedEntrySize == 0); header = (m_nIdbytes != 4) ? (m_nIdbytes & 3) : 3; //0,1 : Bytes per IDtype, 0,1,2,4 Setbit(header, 2, GetFlag(RwfWMapStartPosEntry)); //2 : Startpos in map? Setbit(header, 3, GetFlag(RwfWMapSizeEntry)); //3 : Datasize in map? Setbit(header, 4, GetFlag(RwfWVersionNum)); //4 : Version numeric field? Setbit(header, 7, GetFlag(RwfWMapDescEntry)); //7 : Entrydescriptions in map? // Write header Binarywrite(oStrm, header); // Additional options. uint8 tempU8 = 0; Setbit(tempU8, 0, (m_nIdbytes == IdSizeVariable) || (m_nIdbytes == 3) || (m_nIdbytes > 4)); Setbit(tempU8, 1, m_nFixedEntrySize != 0); const uint8 flags = tempU8; if(flags != s_DefaultFlagbyte) { mpt::IO::WriteAdaptiveInt32LE(oStrm, 2); //Headersize - now it is 2. Binarywrite(oStrm, HeaderId_FlagByte); Binarywrite(oStrm, flags); } else mpt::IO::WriteAdaptiveInt32LE(oStrm, 0); if(Testbit(header, 4)) // Version(numeric)? mpt::IO::WriteAdaptiveInt64LE(oStrm, nVersion); if(Testbit(flags, 0)) // Custom IDbytecount? { uint8 n = (m_nIdbytes == IdSizeVariable) ? 1 : static_cast((m_nIdbytes << 1)); Binarywrite(oStrm, n); } if(Testbit(flags, 1)) // Fixedsize entries? mpt::IO::WriteAdaptiveInt32LE(oStrm, m_nFixedEntrySize); //Entrycount. Reserve two bytes(max uint16_max / 4 entries), actual value is written after writing data. m_posEntrycount = oStrm.tellp(); Binarywrite(oStrm, 0); SetFlag(RwfRwHasMap, (m_nIdbytes != 0 || GetFlag(RwfWMapStartPosEntry) || GetFlag(RwfWMapSizeEntry) || GetFlag(RwfWMapDescEntry))); m_posMapPosField = oStrm.tellp(); if (GetFlag(RwfRwHasMap)) //Mapping begin pos(reserve space - actual value is written after writing data) Binarywrite(oStrm, 0); } SsbRead::ReadRv SsbRead::OnReadEntry(const ReadEntry* pE, const ID &id, const Postype& posReadBegin) { if (pE != nullptr) AddReadNote(pE, m_nCounter); else if (GetFlag(RwfRMapHasId) == false) // Not ID's in map. { ReadEntry e; e.rposStart = static_cast(posReadBegin - m_posStart); e.nSize = static_cast(iStrm.tellg() - posReadBegin); AddReadNote(&e, m_nCounter); } else // Entry not found. { SSB_LOG(MPT_UFORMAT("No entry with id {} found.")(id.AsString())); #ifndef SSB_LOGGING MPT_UNREFERENCED_PARAMETER(id); #endif return EntryNotFound; } m_nCounter++; return EntryRead; } void SsbWrite::OnWroteItem(const ID &id, const Postype& posBeforeWrite) { const Offtype nRawEntrySize = oStrm.tellp() - posBeforeWrite; MPT_MAYBE_CONSTANT_IF(nRawEntrySize < 0 || static_cast(nRawEntrySize) > std::numeric_limits::max()) { AddWriteNote(SNW_INSUFFICIENT_DATASIZETYPE); return; } if(GetFlag(RwfRMapHasSize) && (nRawEntrySize < 0 || static_cast(nRawEntrySize) > (std::numeric_limits::max() >> 2))) { AddWriteNote(SNW_DATASIZETYPE_OVERFLOW); return; } DataSize nEntrySize = static_cast(nRawEntrySize); // Handle fixed size entries: if (m_nFixedEntrySize > 0) { if(nEntrySize <= m_nFixedEntrySize) { for(uint32 i = 0; i(posBeforeWrite - m_posStart), nEntrySize, ""); AddWriteNote(id, m_nCounter, nEntrySize, static_cast(posBeforeWrite - m_posStart)); IncrementWriteCounter(); } void SsbRead::BeginRead(const ID &id, const uint64& nVersion) { SSB_LOG(MPT_UFORMAT("Read header with expected ID = {}")(id.AsString())); ResetReadstatus(); if (!iStrm.good()) { AddReadNote(SNRW_BADGIVEN_STREAM); return; } m_posStart = iStrm.tellg(); // Start bytes. { char temp[sizeof(s_EntryID)]; ArrayReader(sizeof(s_EntryID))(iStrm, temp, sizeof(s_EntryID)); if(std::memcmp(temp, s_EntryID, sizeof(s_EntryID))) { AddReadNote(SNR_STARTBYTE_MISMATCH); return; } } // Compare IDs. uint8 storedIdLen = 0; Binaryread(iStrm, storedIdLen); std::array storedIdBuf; storedIdBuf = {}; if(storedIdLen > 0) { iStrm.read(storedIdBuf.data(), storedIdLen); } if(!(id == ID(storedIdBuf.data(), storedIdLen))) { AddReadNote(SNR_OBJECTCLASS_IDMISMATCH); } if ((m_Status & SNT_FAILURE) != 0) { SSB_LOG(U_("ID mismatch, terminating read.")); return; } SSB_LOG(U_("ID match, continuing reading.")); // Header uint8 tempU8; Binaryread(iStrm, tempU8); const uint8 header = tempU8; m_nIdbytes = ((header & 3) == 3) ? 4 : (header & 3); if (Testbit(header, 6)) SetFlag(RwfRTwoBytesDescChar, true); // Read headerdata size uint32 tempU32 = 0; mpt::IO::ReadAdaptiveInt32LE(iStrm, tempU32); const uint32 headerdatasize = tempU32; // If headerdatasize != 0, read known headerdata and ignore rest. uint8 flagbyte = s_DefaultFlagbyte; if(headerdatasize >= 2) { Binaryread(iStrm, tempU8); if(tempU8 == HeaderId_FlagByte) Binaryread(iStrm, flagbyte); iStrm.ignore( (tempU8 == HeaderId_FlagByte) ? headerdatasize - 2 : headerdatasize - 1); } uint64 tempU64 = 0; // Read version numeric if available. if (Testbit(header, 4)) { mpt::IO::ReadAdaptiveInt64LE(iStrm, tempU64); m_nReadVersion = tempU64; if(tempU64 > nVersion) AddReadNote(SNR_LOADING_OBJECT_WITH_LARGER_VERSION); } if (Testbit(header, 5)) { Binaryread(iStrm, tempU8); iStrm.ignore(tempU8); } if(Testbit(flagbyte, 0)) // Custom ID? { Binaryread(iStrm, tempU8); if ((tempU8 & 1) != 0) m_nIdbytes = IdSizeVariable; else m_nIdbytes = (tempU8 >> 1); if(m_nIdbytes == 0) AddReadNote(SNR_NO_ENTRYIDS_WITH_CUSTOMID_DEFINED); } m_nFixedEntrySize = 0; if(Testbit(flagbyte, 1)) // Fixedsize entries? mpt::IO::ReadAdaptiveInt32LE(iStrm, m_nFixedEntrySize); SetFlag(RwfRMapHasStartpos, Testbit(header, 2)); SetFlag(RwfRMapHasSize, Testbit(header, 3)); SetFlag(RwfRMapHasId, (m_nIdbytes > 0)); SetFlag(RwfRMapHasDesc, Testbit(header, 7)); SetFlag(RwfRwHasMap, GetFlag(RwfRMapHasId) || GetFlag(RwfRMapHasStartpos) || GetFlag(RwfRMapHasSize) || GetFlag(RwfRMapHasDesc)); if (GetFlag(RwfRwHasMap) == false) { SSB_LOG(U_("No map in the file.")); } if (Testbit(flagbyte, 2)) // Object description? { uint16 size = 0; mpt::IO::ReadAdaptiveInt16LE(iStrm, size); iStrm.ignore(size * (GetFlag(RwfRTwoBytesDescChar) ? 2 : 1)); } if(Testbit(flagbyte, 3)) iStrm.ignore(5); // Read entrycount mpt::IO::ReadAdaptiveInt64LE(iStrm, tempU64); if(tempU64 > 16000) // The current code can only write 16383 entries because it uses a Adaptive64LE with a fixed size=2 // Additionally, 16000 is an arbitrary limit to avoid an out-of-memory DoS when caching the map. { AddReadNote(SNR_TOO_MANY_ENTRIES_TO_READ); return; } m_nReadEntrycount = static_cast(tempU64); if(m_nReadEntrycount == 0) AddReadNote(SNR_ZEROENTRYCOUNT); // Read map rpos if map exists. if (GetFlag(RwfRwHasMap)) { mpt::IO::ReadAdaptiveInt64LE(iStrm, tempU64); if(tempU64 > static_cast(std::numeric_limits::max())) { AddReadNote(SNR_INSUFFICIENT_STREAM_OFFTYPE); return; } } const Offtype rawEndOfHdrData = iStrm.tellg() - m_posStart; MPT_MAYBE_CONSTANT_IF(rawEndOfHdrData < 0 || static_cast(rawEndOfHdrData) > std::numeric_limits::max()) { AddReadNote(SNR_INSUFFICIENT_RPOSTYPE); return; } m_rposEndofHdrData = static_cast(rawEndOfHdrData); m_rposMapBegin = (GetFlag(RwfRwHasMap)) ? static_cast(tempU64) : m_rposEndofHdrData; if (GetFlag(RwfRwHasMap) == false) m_posMapEnd = m_posStart + m_rposEndofHdrData; SetFlag(RwfRHeaderIsRead, true); } void SsbRead::CacheMap() { if(GetFlag(RwfRwHasMap) || m_nFixedEntrySize > 0) { iStrm.seekg(m_posStart + m_rposMapBegin); if(iStrm.fail()) { AddReadNote(SNR_BADSTREAM_AFTER_MAPHEADERSEEK); return; } SSB_LOG(MPT_UFORMAT("Reading map from rpos: {}")(m_rposMapBegin)); mapData.resize(m_nReadEntrycount); m_Idarray.reserve(m_nReadEntrycount * 4); //Read map for(NumType i = 0; i 0 && (Util::MaxValueOfType(nOldEnd) - nOldEnd >= nIdsize)) { m_Idarray.resize(nOldEnd + nIdsize); iStrm.read(&m_Idarray[nOldEnd], nIdsize); } mapData[i].nIdLength = nIdsize; mapData[i].nIdpos = nOldEnd; // Read position. if(GetFlag(RwfRMapHasStartpos)) { uint64 tempU64; mpt::IO::ReadAdaptiveInt64LE(iStrm, tempU64); if(tempU64 > static_cast(std::numeric_limits::max())) { AddReadNote(SNR_INSUFFICIENT_STREAM_OFFTYPE); return; } mapData[i].rposStart = static_cast(tempU64); } // Read entry size. if (m_nFixedEntrySize > 0) mapData[i].nSize = m_nFixedEntrySize; else if(GetFlag(RwfRMapHasSize)) // Map has datasize field. { uint64 tempU64; mpt::IO::ReadAdaptiveInt64LE(iStrm, tempU64); if(tempU64 > static_cast(std::numeric_limits::max())) { AddReadNote(SNR_INSUFFICIENT_STREAM_OFFTYPE); return; } mapData[i].nSize = static_cast(tempU64); } // If there's no entry startpos in map, count start pos from datasizes. // Here readentry.rposStart is set to relative position from databegin. if (mapData[i].nSize != invalidDatasize && GetFlag(RwfRMapHasStartpos) == false) mapData[i].rposStart = (i > 0) ? mapData[i-1].rposStart + mapData[i-1].nSize : 0; if(GetFlag(RwfRMapHasDesc)) //Map has entrydescriptions? { uint16 size = 0; mpt::IO::ReadAdaptiveInt16LE(iStrm, size); if(GetFlag(RwfRTwoBytesDescChar)) iStrm.ignore(size * 2); else iStrm.ignore(size); } } m_posMapEnd = iStrm.tellg(); SSB_LOG(MPT_UFORMAT("End of map(rpos): {}")(m_posMapEnd - m_posStart)); } SetFlag(RwfRMapCached, true); m_posDataBegin = (m_rposMapBegin == m_rposEndofHdrData) ? m_posMapEnd : m_posStart + Postype(m_rposEndofHdrData); iStrm.seekg(m_posDataBegin); // If there are no positions in the map but there are entry sizes, rposStart will // be relative to data start. Now that posDataBegin is known, make them relative to // startpos. if (GetFlag(RwfRMapHasStartpos) == false && (GetFlag(RwfRMapHasSize) || m_nFixedEntrySize > 0)) { const RposType offset = static_cast(m_posDataBegin - m_posStart); for(size_t i = 0; i < m_nReadEntrycount; i++) mapData[i].rposStart += offset; } } const ReadEntry* SsbRead::Find(const ID &id) { iStrm.clear(); if (GetFlag(RwfRMapCached) == false) CacheMap(); if (m_nFixedEntrySize > 0 && GetFlag(RwfRMapHasStartpos) == false && GetFlag(RwfRMapHasSize) == false) iStrm.seekg(m_posDataBegin + Postype(m_nFixedEntrySize * m_nCounter)); if (GetFlag(RwfRMapHasId) == true) { const size_t nEntries = mapData.size(); for(size_t i0 = 0; i0 < nEntries; i0++) { const size_t i = (i0 + m_nNextReadHint) % nEntries; if(mapData[i].nIdpos < m_Idarray.size() && id == ID(&m_Idarray[mapData[i].nIdpos], mapData[i].nIdLength)) { m_nNextReadHint = (i + 1) % nEntries; if (mapData[i].rposStart != 0) iStrm.seekg(m_posStart + Postype(mapData[i].rposStart)); return &mapData[i]; } } } return nullptr; } void SsbWrite::FinishWrite() { const Postype posDataEnd = oStrm.tellp(); Postype posMapStart = oStrm.tellp(); SSB_LOG(MPT_UFORMAT("Writing map to rpos: {}")(posMapStart - m_posStart)); if (GetFlag(RwfRwHasMap)) //Write map { oStrm.write(m_MapStreamString.c_str(), m_MapStreamString.length()); } const Postype posMapEnd = oStrm.tellp(); // Write entry count. oStrm.seekp(m_posEntrycount); // Write a fixed size=2 Adaptive64LE because space for this value has already been reserved berforehand. mpt::IO::WriteAdaptiveInt64LE(oStrm, m_nCounter, 2); if (GetFlag(RwfRwHasMap)) { // Write map start position. oStrm.seekp(m_posMapPosField); const uint64 rposMap = posMapStart - m_posStart; // Write a fixed size=8 Adaptive64LE because space for this value has already been reserved berforehand. mpt::IO::WriteAdaptiveInt64LE(oStrm, rposMap, 8); } // Seek to end. oStrm.seekp(std::max(posMapEnd, posDataEnd)); SSB_LOG(MPT_UFORMAT("End of stream(rpos): {}")(oStrm.tellp() - m_posStart)); } } // namespace srlztn OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/common/serialization_utils.h0000644000175000017500000003517214056354456022036 00000000000000/* * serialization_utils.h * --------------------- * Purpose: Serializing data to and from MPTM files. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "mpt/io/io.hpp" #include "mpt/io/io_stdstream.hpp" #include "openmpt/base/Endian.hpp" #include "../common/mptBaseTypes.h" #include #include #include #include #include #include #include #include #include #include OPENMPT_NAMESPACE_BEGIN namespace srlztn //SeRiaLiZaTioN { typedef std::ios::off_type Offtype; typedef Offtype Postype; typedef uintptr_t DataSize; // Data size type. typedef uintptr_t RposType; // Relative position type. typedef uintptr_t NumType; // Entry count type. const DataSize invalidDatasize = DataSize(-1); enum { SNT_PROGRESS = 0x08000000, // = 1 << 27 SNT_FAILURE = 0x40000000, // = 1 << 30 SNT_NOTE = 0x20000000, // = 1 << 29 SNT_WARNING = 0x10000000, // = 1 << 28 SNT_NONE = 0, SNRW_BADGIVEN_STREAM = 1 | SNT_FAILURE, // Read failures. SNR_BADSTREAM_AFTER_MAPHEADERSEEK = 2 | SNT_FAILURE, SNR_STARTBYTE_MISMATCH = 3 | SNT_FAILURE, SNR_BADSTREAM_AT_MAP_READ = 4 | SNT_FAILURE, SNR_INSUFFICIENT_STREAM_OFFTYPE = 5 | SNT_FAILURE, SNR_OBJECTCLASS_IDMISMATCH = 6 | SNT_FAILURE, SNR_TOO_MANY_ENTRIES_TO_READ = 7 | SNT_FAILURE, SNR_INSUFFICIENT_RPOSTYPE = 8 | SNT_FAILURE, // Read notes and warnings. SNR_ZEROENTRYCOUNT = 0x80 | SNT_NOTE, // 0x80 == 1 << 7 SNR_NO_ENTRYIDS_WITH_CUSTOMID_DEFINED = 0x100 | SNT_NOTE, SNR_LOADING_OBJECT_WITH_LARGER_VERSION = 0x200 | SNT_NOTE, // Write failures. SNW_INSUFFICIENT_FIXEDSIZE = (0x10) | SNT_FAILURE, SNW_CHANGING_IDSIZE_WITH_FIXED_IDSIZESETTING = (0x11) | SNT_FAILURE, SNW_DATASIZETYPE_OVERFLOW = (0x13) | SNT_FAILURE, SNW_MAX_WRITE_COUNT_REACHED = (0x14) | SNT_FAILURE, SNW_INSUFFICIENT_DATASIZETYPE = (0x16) | SNT_FAILURE }; enum { IdSizeVariable = std::numeric_limits::max(), IdSizeMaxFixedSize = (std::numeric_limits::max() >> 1) }; typedef int32 SsbStatus; struct ReadEntry { ReadEntry() : nIdpos(0), rposStart(0), nSize(invalidDatasize), nIdLength(0) {} uintptr_t nIdpos; // Index of id start in ID array. RposType rposStart; // Entry start position. DataSize nSize; // Entry size. uint16 nIdLength; // Length of id. }; enum Rwf { RwfWMapStartPosEntry, // Write. True to include data start pos entry to map. RwfWMapSizeEntry, // Write. True to include data size entry to map. RwfWMapDescEntry, // Write. True to include description entry to map. RwfWVersionNum, // Write. True to include version numeric. RwfRMapCached, // Read. True if map has been cached. RwfRMapHasId, // Read. True if map has IDs RwfRMapHasStartpos, // Read. True if map data start pos. RwfRMapHasSize, // Read. True if map has entry size. RwfRMapHasDesc, // Read. True if map has entry description. RwfRTwoBytesDescChar, // Read. True if map description characters are two bytes. RwfRHeaderIsRead, // Read. True when header is read. RwfRwHasMap, // Read/write. True if map exists. RwfNumFlags }; template inline void Binarywrite(std::ostream& oStrm, const T& data) { mpt::IO::WriteIntLE(oStrm, data); } template<> inline void Binarywrite(std::ostream& oStrm, const float& data) { IEEE754binary32LE tmp = IEEE754binary32LE(data); mpt::IO::Write(oStrm, tmp); } template<> inline void Binarywrite(std::ostream& oStrm, const double& data) { IEEE754binary64LE tmp = IEEE754binary64LE(data); mpt::IO::Write(oStrm, tmp); } template inline void WriteItem(std::ostream& oStrm, const T& data) { static_assert(std::is_trivial::value == true, ""); Binarywrite(oStrm, data); } void WriteItemString(std::ostream& oStrm, const std::string &str); template <> inline void WriteItem(std::ostream& oStrm, const std::string& str) {WriteItemString(oStrm, str);} template inline void Binaryread(std::istream& iStrm, T& data) { mpt::IO::ReadIntLE(iStrm, data); } template<> inline void Binaryread(std::istream& iStrm, float& data) { IEEE754binary32LE tmp = IEEE754binary32LE(0.0f); mpt::IO::Read(iStrm, tmp); data = tmp; } template<> inline void Binaryread(std::istream& iStrm, double& data) { IEEE754binary64LE tmp = IEEE754binary64LE(0.0); mpt::IO::Read(iStrm, tmp); data = tmp; } //Read only given number of bytes to the beginning of data; data bytes are memset to 0 before reading. template inline void Binaryread(std::istream& iStrm, T& data, const Offtype bytecount) { mpt::IO::ReadBinaryTruncatedLE(iStrm, data, static_cast(bytecount)); } template <> inline void Binaryread(std::istream& iStrm, float& data, const Offtype bytecount) { typedef IEEE754binary32LE T; std::byte bytes[sizeof(T)]; std::memset(bytes, 0, sizeof(T)); mpt::IO::ReadRaw(iStrm, bytes, std::min(static_cast(bytecount), sizeof(T))); // There is not much we can sanely do for truncated floats, // thus we ignore what we just read and return 0. data = 0.0f; } template <> inline void Binaryread(std::istream& iStrm, double& data, const Offtype bytecount) { typedef IEEE754binary64LE T; std::byte bytes[sizeof(T)]; std::memset(bytes, 0, sizeof(T)); mpt::IO::ReadRaw(iStrm, bytes, std::min(static_cast(bytecount), sizeof(T))); // There is not much we can sanely do for truncated floats, // thus we ignore what we just read and return 0. data = 0.0; } template inline void ReadItem(std::istream& iStrm, T& data, const DataSize nSize) { static_assert(std::is_trivial::value == true, ""); if (nSize == sizeof(T) || nSize == invalidDatasize) Binaryread(iStrm, data); else Binaryread(iStrm, data, nSize); } void ReadItemString(std::istream& iStrm, std::string& str, const DataSize); template <> inline void ReadItem(std::istream& iStrm, std::string& str, const DataSize nSize) { ReadItemString(iStrm, str, nSize); } class ID { private: std::string m_ID; // NOTE: can contain null characters ('\0') public: ID() { } ID(const std::string &id) : m_ID(id) { } ID(const char *beg, const char *end) : m_ID(beg, end) { } ID(const char *id) : m_ID(id?id:"") { } ID(const char * str, std::size_t len) : m_ID(str, str + len) { } template static ID FromInt(const T &val) { static_assert(std::numeric_limits::is_integer); typename mpt::make_le::type valle; valle = val; return ID(std::string(mpt::byte_cast(mpt::as_raw_memory(valle).data()), mpt::byte_cast(mpt::as_raw_memory(valle).data() + sizeof(valle)))); } bool IsPrintable() const; mpt::ustring AsString() const; const char *GetBytes() const { return m_ID.c_str(); } std::size_t GetSize() const { return m_ID.length(); } bool operator == (const ID &other) const { return m_ID == other.m_ID; } bool operator != (const ID &other) const { return m_ID != other.m_ID; } }; class Ssb { protected: Ssb(); public: SsbStatus GetStatus() const { return m_Status; } protected: // When writing, returns the number of entries written. // When reading, returns the number of entries read not including unrecognized entries. NumType GetCounter() const {return m_nCounter;} void SetFlag(Rwf flag, bool val) {m_Flags.set(flag, val);} bool GetFlag(Rwf flag) const {return m_Flags[flag];} protected: SsbStatus m_Status; uint32 m_nFixedEntrySize; // Read/write: If > 0, data entries have given fixed size. Postype m_posStart; // Read/write: Stream position at the beginning of object. uint16 m_nIdbytes; // Read/Write: Tells map ID entry size in bytes. If size is variable, value is IdSizeVariable. NumType m_nCounter; // Read/write: Keeps count of entries written/read. std::bitset m_Flags; // Read/write: Various flags. protected: enum : uint8 { s_DefaultFlagbyte = 0 }; static const char s_EntryID[3]; }; class SsbRead : public Ssb { public: enum ReadRv // Read return value. { EntryRead, EntryNotFound }; enum IdMatchStatus { IdMatch, IdMismatch }; typedef std::vector::const_iterator ReadIterator; SsbRead(std::istream& iStrm); // Call this to begin reading: must be called before other read functions. void BeginRead(const ID &id, const uint64& nVersion); // After calling BeginRead(), this returns number of entries in the file. NumType GetNumEntries() const {return m_nReadEntrycount;} // Returns read iterator to the beginning of entries. // The behaviour of read iterators is undefined if map doesn't // contain entry ids or data begin positions. ReadIterator GetReadBegin(); // Returns read iterator to the end(one past last) of entries. ReadIterator GetReadEnd(); // Compares given id with read entry id IdMatchStatus CompareId(const ReadIterator& iter, const ID &id); uint64 GetReadVersion() {return m_nReadVersion;} // Read item using default read implementation. template ReadRv ReadItem(T& obj, const ID &id) {return ReadItem(obj, id, srlztn::ReadItem);} // Read item using given function. template ReadRv ReadItem(T& obj, const ID &id, FuncObj); // Read item using read iterator. template ReadRv ReadIterItem(const ReadIterator& iter, T& obj) {return ReadIterItem(iter, obj, srlztn::ReadItem);} template ReadRv ReadIterItem(const ReadIterator& iter, T& obj, FuncObj func); private: // Reads map to cache. void CacheMap(); // Searches for entry with given ID. If found, returns pointer to corresponding entry, else // returns nullptr. const ReadEntry* Find(const ID &id); // Called after reading an object. ReadRv OnReadEntry(const ReadEntry* pE, const ID &id, const Postype& posReadBegin); void AddReadNote(const SsbStatus s); // Called after reading entry. pRe is a pointer to associated map entry if exists. void AddReadNote(const ReadEntry* const pRe, const NumType nNum); void ResetReadstatus(); private: // mapData is a cache that facilitates faster access to the stored data // without having to reparse on every access. // Iterator invalidation in CacheMap() is not a problem because every code // path that ever returns an iterator into mapData does CacheMap exactly once // beforehand. Following calls use this already cached map. As the data is // immutable when reading, there is no need to ever invalidate the cache and // redo CacheMap(). std::istream& iStrm; std::vector m_Idarray; // Read: Holds entry ids. std::vector mapData; // Read: Contains map information. uint64 m_nReadVersion; // Read: Version is placed here when reading. RposType m_rposMapBegin; // Read: If map exists, rpos of map begin, else m_rposEndofHdrData. Postype m_posMapEnd; // Read: If map exists, map end position, else pos of end of hdrData. Postype m_posDataBegin; // Read: Data begin position. RposType m_rposEndofHdrData; // Read: rpos of end of header data. NumType m_nReadEntrycount; // Read: Number of entries. NumType m_nNextReadHint; // Read: Hint where to start looking for the next read entry. }; class SsbWrite : public Ssb { public: SsbWrite(std::ostream& oStrm); // Write header void BeginWrite(const ID &id, const uint64& nVersion); // Write item using default write implementation. template void WriteItem(const T& obj, const ID &id) {WriteItem(obj, id, &srlztn::WriteItem);} // Write item using given function. template void WriteItem(const T& obj, const ID &id, FuncObj); // Writes mapping. void FinishWrite(); private: // Called after writing an item. void OnWroteItem(const ID &id, const Postype& posBeforeWrite); void AddWriteNote(const SsbStatus s); void AddWriteNote(const ID &id, const NumType nEntryNum, const DataSize nBytecount, const RposType rposStart); // Writes mapping item to mapstream. void WriteMapItem(const ID &id, const RposType& rposDataStart, const DataSize& nDatasize, const char* pszDesc); void ResetWritestatus() {m_Status = SNT_NONE;} void IncrementWriteCounter(); private: std::ostream& oStrm; Postype m_posEntrycount; // Write: Pos of entrycount field. Postype m_posMapPosField; // Write: Pos of map position field. std::string m_MapStreamString; // Write: Map stream string. }; template void SsbWrite::WriteItem(const T& obj, const ID &id, FuncObj Func) { const Postype pos = oStrm.tellp(); Func(oStrm, obj); OnWroteItem(id, pos); } template SsbRead::ReadRv SsbRead::ReadItem(T& obj, const ID &id, FuncObj Func) { const ReadEntry* pE = Find(id); const Postype pos = iStrm.tellg(); if (pE != nullptr || GetFlag(RwfRMapHasId) == false) Func(iStrm, obj, (pE) ? (pE->nSize) : invalidDatasize); return OnReadEntry(pE, id, pos); } template SsbRead::ReadRv SsbRead::ReadIterItem(const ReadIterator& iter, T& obj, FuncObj func) { iStrm.clear(); if (iter->rposStart != 0) iStrm.seekg(m_posStart + Postype(iter->rposStart)); const Postype pos = iStrm.tellg(); func(iStrm, obj, iter->nSize); return OnReadEntry(&(*iter), ID(&m_Idarray[iter->nIdpos], iter->nIdLength), pos); } inline SsbRead::IdMatchStatus SsbRead::CompareId(const ReadIterator& iter, const ID &id) { if(iter->nIdpos >= m_Idarray.size()) return IdMismatch; return (id == ID(&m_Idarray[iter->nIdpos], iter->nIdLength)) ? IdMatch : IdMismatch; } inline SsbRead::ReadIterator SsbRead::GetReadBegin() { MPT_ASSERT(GetFlag(RwfRMapHasId) && (GetFlag(RwfRMapHasStartpos) || GetFlag(RwfRMapHasSize) || m_nFixedEntrySize > 0)); if (GetFlag(RwfRMapCached) == false) CacheMap(); return mapData.begin(); } inline SsbRead::ReadIterator SsbRead::GetReadEnd() { if (GetFlag(RwfRMapCached) == false) CacheMap(); return mapData.end(); } template struct VectorWriter { VectorWriter(size_t nCount) : m_nCount(nCount) {} void operator()(std::ostream &oStrm, const std::vector &vec) { for(size_t i = 0; i < m_nCount; i++) { Binarywrite(oStrm, vec[i]); } } size_t m_nCount; }; template struct VectorReader { VectorReader(size_t nCount) : m_nCount(nCount) {} void operator()(std::istream& iStrm, std::vector &vec, const size_t) { vec.resize(m_nCount); for(std::size_t i = 0; i < m_nCount; ++i) { Binaryread(iStrm, vec[i]); } } size_t m_nCount; }; template struct ArrayReader { ArrayReader(size_t nCount) : m_nCount(nCount) {} void operator()(std::istream& iStrm, T* pData, const size_t) { for(std::size_t i=0; i // MFC core // cppcheck-suppress missingInclude #include // MFC standard components // cppcheck-suppress missingInclude #include // MFC extensions // cppcheck-suppress missingInclude #include // MFC support for Windows Common Controls // cppcheck-suppress missingInclude #include // cppcheck-suppress missingInclude #include #ifdef MPT_MFC_FULL // cppcheck-suppress missingInclude #include #endif // MPT_MFC_FULL // cppcheck-suppress missingInclude #include #endif // MPT_WITH_MFC #if MPT_OS_WINDOWS #include #include #include #include #endif // MPT_OS_WINDOWS #endif // MODPLUG_TRACKER #if MPT_COMPILER_MSVC #include #endif #include "mpt/base/span.hpp" #include "mpt/check/libc.hpp" #if defined(MPT_WITH_MFC) #include "mpt/check/mfc.hpp" #endif #if MPT_OS_WINDOWS #include "mpt/check/windows.hpp" #endif #include "mpt/exception_text/exception_text.hpp" #include "mpt/out_of_memory/out_of_memory.hpp" #include "mpt/system_error/system_error.hpp" #include "openmpt/base/Types.hpp" #include "openmpt/logging/Logger.hpp" #include #include // this will be available everywhere #include "../common/mptBaseMacros.h" // // // // // // #include "../common/mptBaseTypes.h" // "openmpt/base/Types.hpp" // "mptBaseMacros.h" // // // // #include "../common/mptAssert.h" // "mptBaseMacros.h" #include "../common/mptBaseUtils.h" // // // // // #include "../common/mptString.h" // // // // // // #include "../common/mptStringBuffer.h" #include "../common/mptStringFormat.h" // #include "../common/mptPathString.h" #include "../common/Logging.h" // "openmpt/logging/Logger.hpp" // #include "../common/misc_util.h" // // // // for std::abs #include #include #include #include //{{AFX_INSERT_LOCATION}} // Microsoft Developer Studio will insert additional declarations immediately before the previous line. libopenmpt-0.6.1+release.autotools/common/version.cpp0000644000175000017500000005164414164006754017756 00000000000000/* * version.cpp * ----------- * Purpose: OpenMPT version handling. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "version.h" #include "mptString.h" #include "mptStringFormat.h" #include "mptStringParse.h" #include "versionNumber.h" #include "svn_version.h" OPENMPT_NAMESPACE_BEGIN #define MPT_MAKE_VERSION_NUMERIC_HELPER(prefix,v0,v1,v2,v3) Version( prefix ## v0 , prefix ## v1 , prefix ## v2 , prefix ## v3 ) #define MPT_MAKE_VERSION_NUMERIC(v0,v1,v2,v3) MPT_MAKE_VERSION_NUMERIC_HELPER(0x, v0, v1, v2, v3) #define MPT_VERSION_CURRENT MPT_MAKE_VERSION_NUMERIC(VER_MAJORMAJOR,VER_MAJOR,VER_MINOR,VER_MINORMINOR) static_assert((MPT_VERSION_CURRENT.GetRawVersion() & 0xffffu) != 0x0000u, "Version numbers ending in .00.00 shall never exist again, as they make interpreting the version number ambiguous for file formats which can only store the two major parts of the version number (e.g. IT and S3M)."); Version Version::Current() noexcept { return MPT_VERSION_CURRENT; } mpt::ustring Version::GetOpenMPTVersionString() const { return U_("OpenMPT ") + ToUString(); } Version Version::Parse(const mpt::ustring &s) { uint32 result = 0; std::vector numbers = mpt::String::Split(s, U_(".")); for (std::size_t i = 0; i < numbers.size() && i < 4; ++i) { result |= (mpt::String::Parse::Hex(numbers[i]) & 0xff) << ((3 - i) * 8); } return Version(result); } mpt::ustring Version::ToUString() const { uint32 v = m_Version; if(v == 0) { // Unknown version return U_("Unknown"); } else if((v & 0xFFFF) == 0) { // Only parts of the version number are known (e.g. when reading the version from the IT or S3M file header) return MPT_UFORMAT("{}.{}")(mpt::ufmt::HEX((v >> 24) & 0xFF), mpt::ufmt::HEX0<2>((v >> 16) & 0xFF)); } else { // Full version info available return MPT_UFORMAT("{}.{}.{}.{}")(mpt::ufmt::HEX((v >> 24) & 0xFF), mpt::ufmt::HEX0<2>((v >> 16) & 0xFF), mpt::ufmt::HEX0<2>((v >> 8) & 0xFF), mpt::ufmt::HEX0<2>((v) & 0xFF)); } } Version Version::WithoutTestNumber() const noexcept { return Version(m_Version & 0xFFFFFF00u); } Version Version::WithoutPatchOrTestNumbers() const noexcept { return Version(m_Version & 0xFFFF0000u); } bool Version::IsTestVersion() const noexcept { return ( // Legacy (*this > MPT_V("1.17.02.54") && *this < MPT_V("1.18.02.00") && *this != MPT_V("1.18.00.00")) || // Test builds have non-zero VER_MINORMINOR (*this > MPT_V("1.18.02.00") && ((m_Version & 0xFFFFFF00u) != m_Version)) ); } namespace Source { static mpt::ustring GetUrl() { #ifdef OPENMPT_VERSION_URL return mpt::ToUnicode(mpt::Charset::ASCII, OPENMPT_VERSION_URL); #else return mpt::ustring(); #endif } static int GetRevision() { #if defined(OPENMPT_VERSION_REVISION) return OPENMPT_VERSION_REVISION; #elif defined(OPENMPT_VERSION_SVNVERSION) std::string svnversion = OPENMPT_VERSION_SVNVERSION; if(svnversion.length() == 0) { return 0; } if(svnversion.find(":") != std::string::npos) { svnversion = svnversion.substr(svnversion.find(":") + 1); } if(svnversion.find("-") != std::string::npos) { svnversion = svnversion.substr(svnversion.find("-") + 1); } if(svnversion.find("M") != std::string::npos) { svnversion = svnversion.substr(0, svnversion.find("M")); } if(svnversion.find("S") != std::string::npos) { svnversion = svnversion.substr(0, svnversion.find("S")); } if(svnversion.find("P") != std::string::npos) { svnversion = svnversion.substr(0, svnversion.find("P")); } return ConvertStrTo(svnversion); #else MPT_WARNING_STATEMENT("SVN revision unknown. Please check your build system."); return 0; #endif } static bool IsDirty() { #if defined(OPENMPT_VERSION_DIRTY) return OPENMPT_VERSION_DIRTY != 0; #elif defined(OPENMPT_VERSION_SVNVERSION) std::string svnversion = OPENMPT_VERSION_SVNVERSION; if(svnversion.length() == 0) { return false; } if(svnversion.find("M") != std::string::npos) { return true; } return false; #else return false; #endif } static bool HasMixedRevisions() { #if defined(OPENMPT_VERSION_MIXEDREVISIONS) return OPENMPT_VERSION_MIXEDREVISIONS != 0; #elif defined(OPENMPT_VERSION_SVNVERSION) std::string svnversion = OPENMPT_VERSION_SVNVERSION; if(svnversion.length() == 0) { return false; } if(svnversion.find(":") != std::string::npos) { return true; } if(svnversion.find("-") != std::string::npos) { return true; } if(svnversion.find("S") != std::string::npos) { return true; } if(svnversion.find("P") != std::string::npos) { return true; } return false; #else return false; #endif } static bool IsPackage() { #if defined(OPENMPT_VERSION_IS_PACKAGE) return OPENMPT_VERSION_IS_PACKAGE != 0; #else return false; #endif } static mpt::ustring GetSourceDate() { #if defined(OPENMPT_VERSION_DATE) return mpt::ToUnicode(mpt::Charset::ASCII, OPENMPT_VERSION_DATE); #else return mpt::ustring(); #endif } } // namespace Source SourceInfo::SourceInfo() : m_Url(Source::GetUrl()) , m_Revision(Source::GetRevision()) , m_IsDirty(Source::IsDirty()) , m_HasMixedRevisions(Source::HasMixedRevisions()) , m_IsPackage(Source::IsPackage()) , m_Date(Source::GetSourceDate()) { } mpt::ustring SourceInfo::GetUrlWithRevision() const { if(m_Url.empty() || (m_Revision == 0)) { return mpt::ustring(); } return m_Url + UL_("@") + mpt::ufmt::val(m_Revision); } mpt::ustring SourceInfo::GetStateString() const { mpt::ustring retval; if(m_IsDirty) { retval += UL_("+dirty"); } if(m_HasMixedRevisions) { retval += UL_("+mixed"); } if(retval.empty()) { retval += UL_("clean"); } if(m_IsPackage) { retval += UL_("-pkg"); } return retval; } SourceInfo SourceInfo::Current() { return SourceInfo(); } VersionWithRevision VersionWithRevision::Current() { return {Version::Current(), static_cast(SourceInfo::Current().Revision())}; } VersionWithRevision VersionWithRevision::Parse(const mpt::ustring &s) { Version version = Version::Parse(mpt::ustring()); uint64 revision = 0; const auto tokens = mpt::String::Split(s, U_("-")); if(tokens.size() >= 1) { version = Version::Parse(tokens[0]); } if(tokens.size() >= 2) { revision = ConvertStrTo(tokens[1].substr(1)); } return {version, revision}; } mpt::ustring VersionWithRevision::ToUString() const { if(!HasRevision()) { return mpt::ufmt::val(version); } if(!version.IsTestVersion()) { return mpt::ufmt::val(version); } return MPT_UFORMAT("{}-r{}")(version, revision); } namespace Build { bool IsReleasedBuild() { return !(Version::Current().IsTestVersion() || IsDebugBuild() || Source::IsDirty() || Source::HasMixedRevisions()); } bool IsDebugBuild() { #if defined(MPT_BUILD_DEBUG) || defined(DEBUG) || defined(_DEBUG) return true; #else return false; #endif } mpt::ustring GetBuildDateString() { mpt::ustring result; #ifdef MODPLUG_TRACKER #if defined(OPENMPT_BUILD_DATE) result = mpt::ToUnicode(mpt::Charset::ASCII, OPENMPT_BUILD_DATE ); #else result = mpt::ToUnicode(mpt::Charset::ASCII, __DATE__ " " __TIME__ ); #endif #else // !MODPLUG_TRACKER result = SourceInfo::Current().Date(); #endif // MODPLUG_TRACKER return result; } static mpt::ustring GetBuildFlagsString() { mpt::ustring retval; #ifdef MODPLUG_TRACKER #if defined(MPT_BUILD_RETRO) retval += UL_(" RETRO"); #endif // MPT_BUILD_RETRO if(Version::Current().IsTestVersion()) { retval += UL_(" TEST"); } #endif // MODPLUG_TRACKER if(IsDebugBuild()) { retval += UL_(" DEBUG"); } return retval; } mpt::ustring GetBuildFeaturesString() { mpt::ustring retval; #ifdef LIBOPENMPT_BUILD retval = UL_("") #if defined(MPT_WITH_ZLIB) UL_(" +ZLIB") #endif #if defined(MPT_WITH_MINIZ) UL_(" +MINIZ") #endif #if !defined(MPT_WITH_ZLIB) && !defined(MPT_WITH_MINIZ) UL_(" -INFLATE") #endif #if defined(MPT_WITH_MPG123) UL_(" +MPG123") #endif #if defined(MPT_WITH_MINIMP3) UL_(" +MINIMP3") #endif #if defined(MPT_WITH_MEDIAFOUNDATION) UL_(" +MF") #endif #if !defined(MPT_WITH_MPG123) && !defined(MPT_WITH_MINIMP3) && !defined(MPT_WITH_MEDIAFOUNDATION) UL_(" -MP3") #endif #if defined(MPT_WITH_OGG) && defined(MPT_WITH_VORBIS) && defined(MPT_WITH_VORBISFILE) UL_(" +VORBIS") #endif #if defined(MPT_WITH_STBVORBIS) UL_(" +STBVORBIS") #endif #if !(defined(MPT_WITH_OGG) && defined(MPT_WITH_VORBIS) && defined(MPT_WITH_VORBISFILE)) && !defined(MPT_WITH_STBVORBIS) UL_(" -VORBIS") #endif #if !defined(NO_PLUGINS) UL_(" +PLUGINS") #else UL_(" -PLUGINS") #endif #if defined(MPT_WITH_DMO) UL_(" +DMO") #endif ; #endif #ifdef MODPLUG_TRACKER retval += UL_("") #if defined(UNICODE) UL_(" UNICODE") #else UL_(" ANSI") #endif #ifndef MPT_WITH_VST UL_(" NO_VST") #endif #ifndef MPT_WITH_DMO UL_(" NO_DMO") #endif #ifdef NO_PLUGINS UL_(" NO_PLUGINS") #endif ; #endif return retval; } mpt::ustring GetBuildCompilerString() { mpt::ustring retval; #if MPT_COMPILER_GENERIC retval += U_("Generic C++11 Compiler"); #elif MPT_COMPILER_MSVC #if defined(_MSC_FULL_VER) && defined(_MSC_BUILD) && (_MSC_BUILD > 0) retval += MPT_UFORMAT("Microsoft Compiler {}.{}.{}.{}") ( _MSC_FULL_VER / 10000000 , mpt::ufmt::dec0<2>((_MSC_FULL_VER / 100000) % 100) , mpt::ufmt::dec0<5>(_MSC_FULL_VER % 100000) , mpt::ufmt::dec0<2>(_MSC_BUILD) ); #elif defined(_MSC_FULL_VER) retval += MPT_UFORMAT("Microsoft Compiler {}.{}.{}") ( _MSC_FULL_VER / 10000000 , mpt::ufmt::dec0<2>((_MSC_FULL_VER / 100000) % 100) , mpt::ufmt::dec0<5>(_MSC_FULL_VER % 100000) ); #else retval += MPT_UFORMAT("Microsoft Compiler {}.{}")(MPT_COMPILER_MSVC_VERSION / 100, MPT_COMPILER_MSVC_VERSION % 100); #endif #elif MPT_COMPILER_GCC retval += MPT_UFORMAT("GNU Compiler Collection {}.{}.{}")(MPT_COMPILER_GCC_VERSION / 10000, (MPT_COMPILER_GCC_VERSION / 100) % 100, MPT_COMPILER_GCC_VERSION % 100); #elif MPT_COMPILER_CLANG retval += MPT_UFORMAT("Clang {}.{}.{}")(MPT_COMPILER_CLANG_VERSION / 10000, (MPT_COMPILER_CLANG_VERSION / 100) % 100, MPT_COMPILER_CLANG_VERSION % 100); #else retval += U_("unknown"); #endif return retval; } static mpt::ustring GetRevisionString() { mpt::ustring result; if(Source::GetRevision() == 0) { return result; } result = U_("-r") + mpt::ufmt::val(Source::GetRevision()); if(Source::HasMixedRevisions()) { result += UL_("!"); } if(Source::IsDirty()) { result += UL_("+"); } if(Source::IsPackage()) { result += UL_("p"); } return result; } mpt::ustring GetVersionString(FlagSet strings) { std::vector result; if(strings[StringVersion]) { result.push_back(mpt::ufmt::val(Version::Current())); } if(strings[StringRevision]) { if(!IsReleasedBuild()) { result.push_back(GetRevisionString()); } } if(strings[StringSourceInfo]) { const SourceInfo sourceInfo = SourceInfo::Current(); if(!sourceInfo.GetUrlWithRevision().empty()) { result.push_back(MPT_UFORMAT(" {}")(sourceInfo.GetUrlWithRevision())); } if(!sourceInfo.Date().empty()) { result.push_back(MPT_UFORMAT(" ({})")(sourceInfo.Date())); } if(!sourceInfo.GetStateString().empty()) { result.push_back(MPT_UFORMAT(" {}")(sourceInfo.GetStateString())); } } if(strings[StringBuildFlags]) { if(!IsReleasedBuild()) { result.push_back(GetBuildFlagsString()); } } if(strings[StringBuildFeatures]) { result.push_back(GetBuildFeaturesString()); } return mpt::trim(mpt::String::Combine(result, U_(""))); } mpt::ustring GetVersionStringPure() { FlagSet strings; strings |= Build::StringVersion; strings |= Build::StringRevision; return GetVersionString(strings); } mpt::ustring GetVersionStringSimple() { FlagSet strings; strings |= Build::StringVersion; strings |= Build::StringRevision; strings |= Build::StringBuildFlags; return GetVersionString(strings); } mpt::ustring GetVersionStringExtended() { FlagSet strings; strings |= Build::StringVersion; strings |= Build::StringRevision; #ifndef MODPLUG_TRACKER strings |= Build::StringSourceInfo; #endif strings |= Build::StringBuildFlags; #ifdef MODPLUG_TRACKER strings |= Build::StringBuildFeatures; #endif return GetVersionString(strings); } mpt::ustring GetURL(Build::Url key) { mpt::ustring result; switch(key) { case Url::Website: #ifdef LIBOPENMPT_BUILD result = U_("https://lib.openmpt.org/"); #else result = U_("https://openmpt.org/"); #endif break; case Url::Download: #ifdef MODPLUG_TRACKER result = IsReleasedBuild() ? U_("https://openmpt.org/download") : U_("https://builds.openmpt.org/builds/"); #else result = U_("https://lib.openmpt.org/libopenmpt/download/"); #endif break; case Url::Forum: result = U_("https://forum.openmpt.org/"); break; case Url::Bugtracker: result = U_("https://bugs.openmpt.org/"); break; case Url::Updates: result = U_("https://openmpt.org/download"); break; case Url::TopPicks: result = U_("https://openmpt.org/top_picks"); break; } return result; } mpt::ustring GetFullCreditsString() { return mpt::ToUnicode(mpt::Charset::UTF8, #ifdef MODPLUG_TRACKER "OpenMPT / Open ModPlug Tracker\n" #else "libopenmpt (based on OpenMPT / Open ModPlug Tracker)\n" #endif "\n" "Copyright \xC2\xA9 2004-2022 OpenMPT Project Developers and Contributors\n" "Copyright \xC2\xA9 1997-2003 Olivier Lapicque\n" "\n" "Developers:\n" "Johannes Schultz (2008-2022)\n" "J\xC3\xB6rn Heusipp (2012-2022)\n" "Ahti Lepp\xC3\xA4nen (2005-2011)\n" "Robin Fernandes (2004-2007)\n" "Sergiy Pylypenko (2007)\n" "Eric Chavanon (2004-2005)\n" "Trevor Nunes (2004)\n" "Olivier Lapicque (1997-2003)\n" "\n" "Additional contributors:\n" "coda (https://coda.s3m.us/)\n" "Jo\xC3\xA3o Baptista de Paula e Silva (https://joaobapt.com/)\n" "kode54 (https://kode54.net/)\n" "Revenant (https://revenant1.net/)\n" "xaimus (http://xaimus.com/)\n" "\n" "Thanks to:\n" "\n" "Konstanty for the XMMS-ModPlug resampling implementation\n" "http://modplug-xmms.sourceforge.net/\n" "\n" #ifdef MODPLUG_TRACKER "Stephan M. Bernsee for pitch shifting source code\n" "http://www.dspdimension.com/\n" "\n" "Aleksey Vaneev of Voxengo for r8brain sample rate converter\n" "https://github.com/avaneev/r8brain-free-src\n" "\n" "Olli Parviainen for SoundTouch Library (time stretching)\n" "https://www.surina.net/soundtouch/\n" "\n" #endif #ifdef MPT_WITH_VST "Hermann Seib for his example VST Host implementation\n" "http://www.hermannseib.com/english/vsthost.htm\n" "\n" "Benjamin \"BeRo\" Rosseaux for his independent VST header\n" "https://blog.rosseaux.net/\n" "\n" #endif "Storlek for all the IT compatibility hints and testcases\n" "as well as the IMF, MDL, OKT and ULT loaders\n" "http://schismtracker.org/\n" "\n" "Sergei \"x0r\" Kolzun for various hints on Scream Tracker 2 compatibility\n" "https://github.com/viiri/st2play\n" "\n" "Laurent Cl\xc3\xA9vy for unofficial MO3 documentation and decompression code\n" "https://github.com/lclevy/unmo3\n" "\n" "Ben \"GreaseMonkey\" Russell for IT sample compression code\n" "https://github.com/iamgreaser/it2everything/\n" "\n" "Antti S. Lankila for Amiga resampler implementation\n" "https://bel.fi/alankila/modguide/interpolate.txt\n" "\n" "Shayde / Reality Productions for Opal OPL3 emulator\n" "https://www.3eality.com/\n" "\n" "Ryuhei Mori for TinyFFT\n" "https://github.com/ryuhei-mori/tinyfft\n" "\n" #ifdef MPT_WITH_ZLIB "Jean-loup Gailly and Mark Adler for zlib\n" "https://zlib.net/\n" "\n" #endif #ifdef MPT_WITH_MINIZ "Rich Geldreich et al. for miniz\n" "https://github.com/richgel999/miniz\n" "\n" #endif #ifdef MPT_WITH_LHASA "Simon Howard for lhasa\n" "https://fragglet.github.io/lhasa/\n" "\n" #endif #ifdef MPT_WITH_UNRAR "Alexander L. Roshal for UnRAR\n" "https://rarlab.com/\n" "\n" #endif #ifdef MPT_WITH_ANCIENT "Teemu Suutari for ancient\n" "https://github.com/temisu/ancient\n" "\n" #endif #ifdef MPT_WITH_PORTAUDIO "PortAudio contributors\n" "http://www.portaudio.com/\n" "\n" #endif #ifdef MPT_WITH_RTAUDIO "Gary P. Scavone, McGill University for RtAudio\n" "https://www.music.mcgill.ca/~gary/rtaudio/\n" "\n" #endif #ifdef MPT_WITH_FLAC "Josh Coalson / Xiph.Org Foundation for libFLAC\n" "https://xiph.org/flac/\n" "\n" #endif #if defined(MPT_WITH_MPG123) "The mpg123 project for libmpg123\n" "https://mpg123.de/\n" "\n" #endif #ifdef MPT_WITH_MINIMP3 "Lion (github.com/lieff) for minimp3\n" "https://github.com/lieff/minimp3/\n" "\n" #endif #ifdef MPT_WITH_STBVORBIS "Sean Barrett for stb_vorbis\n" "https://github.com/nothings/stb/\n" "\n" #endif #ifdef MPT_WITH_OGG "Xiph.Org Foundation for libogg\n" "https://xiph.org/ogg/\n" "\n" #endif #if defined(MPT_WITH_VORBIS) || defined(MPT_WITH_LIBVORBISFILE) "Xiph.Org Foundation for libvorbis\n" "https://xiph.org/vorbis/\n" "\n" #endif #if defined(MPT_WITH_OPUS) "Xiph.Org, Skype Limited, Octasic, Jean-Marc Valin, Timothy B. Terriberry,\n" "CSIRO, Gregory Maxwell, Mark Borgerding, Erik de Castro Lopo,\n" "Xiph.Org Foundation, Microsoft Corporation, Broadcom Corporation for libopus\n" "https://opus-codec.org/\n" "\n" #endif #if defined(MPT_WITH_OPUSFILE) "Xiph.Org Foundation and contributors for libopusfile\n" "https://opus-codec.org/\n" "\n" #endif #if defined(MPT_WITH_OPUSENC) "Xiph.Org Foundation, Jean-Marc Valin and contributors for libopusenc\n" "https://git.xiph.org/?p=libopusenc.git;a=summary\n" "\n" #endif #if defined(MPT_WITH_LAME) "The LAME project for LAME\n" "https://lame.sourceforge.io/\n" "\n" #endif #if defined(MPT_WITH_NLOHMANNJSON) "Niels Lohmann et al. for nlohmann-json\n" "https://github.com/nlohmann/json\n" "\n" #endif #ifdef MODPLUG_TRACKER "Lennart Poettering and David Henningsson for RealtimeKit\n" "http://git.0pointer.net/rtkit.git/\n" "\n" "Gary P. Scavone for RtMidi\n" "https://www.music.mcgill.ca/~gary/rtmidi/\n" "\n" "Alexander Uckun for decimal input field\n" "https://www.codeproject.com/Articles/21257/_\n" "\n" "\xc3\x9alfur Kolka for application icon, splash and about screen\n" "https://www.behance.net/ulfurkolka\n" "\n" "Nobuyuki for file icon\n" "https://twitter.com/nobuyukinyuu\n" "\n" #endif "Daniel Collin (emoon/TBL) for providing test infrastructure\n" "https://twitter.com/daniel_collin\n" "\n" "The people at ModPlug forums for crucial contribution\n" "in the form of ideas, testing and support;\n" "thanks particularly to:\n" "33, 8bitbubsy, Anboi, BooT-SectoR-ViruZ, Bvanoudtshoorn\n" "christofori, cubaxd, Diamond, Ganja, Georg, Goor00,\n" "Harbinger, jmkz, KrazyKatz, LPChip, Nofold, Rakib, Sam Zen\n" "Skaven, Skilletaudio, Snu, Squirrel Havoc, Teimoso, Waxhead\n" "\n" #ifdef MPT_WITH_VST "VST PlugIn Technology by Steinberg Media Technologies GmbH\n" "\n" #endif #ifdef MPT_WITH_ASIO "ASIO Technology by Steinberg Media Technologies GmbH\n" "\n" #endif ); } mpt::ustring GetLicenseString() { return MPT_UTF8( "Copyright (c) 2004-2022, OpenMPT Project Developers and Contributors" "\n" "Copyright (c) 1997-2003, Olivier Lapicque" "\n" "All rights reserved." "\n" "" "\n" "Redistribution and use in source and binary forms, with or without" "\n" "modification, are permitted provided that the following conditions are met:" "\n" " * Redistributions of source code must retain the above copyright" "\n" " notice, this list of conditions and the following disclaimer." "\n" " * Redistributions in binary form must reproduce the above copyright" "\n" " notice, this list of conditions and the following disclaimer in the" "\n" " documentation and/or other materials provided with the distribution." "\n" " * Neither the name of the OpenMPT project nor the" "\n" " names of its contributors may be used to endorse or promote products" "\n" " derived from this software without specific prior written permission." "\n" "" "\n" "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"" "\n" "AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE" "\n" "IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE" "\n" "DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE" "\n" "FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL" "\n" "DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR" "\n" "SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER" "\n" "CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY," "\n" "OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE" "\n" "OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." "\n" ); } } // namespace Build OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/common/version.h0000644000175000017500000002360614053120664017412 00000000000000/* * version.h * --------- * Purpose: OpenMPT version handling. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "mptString.h" #include "openmpt/base/FlagSet.hpp" #include OPENMPT_NAMESPACE_BEGIN class Version { private: uint32 m_Version; // e.g. 0x01170208 public: enum class Field { Major, Minor, Patch, Test, }; public: static Version Current() noexcept; public: MPT_CONSTEXPRINLINE Version() noexcept : m_Version(0) {} explicit MPT_CONSTEXPRINLINE Version(uint32 version) noexcept : m_Version(version) {} explicit MPT_CONSTEXPRINLINE Version(uint8 v1, uint8 v2, uint8 v3, uint8 v4) noexcept : m_Version((static_cast(v1) << 24) | (static_cast(v2) << 16) | (static_cast(v3) << 8) | (static_cast(v4) << 0)) {} public: mpt::ustring ToUString() const; // e.g "1.17.02.08" // Returns numerical version value from given version string. static Version Parse(const mpt::ustring &s); public: explicit MPT_CONSTEXPRINLINE operator bool () const noexcept { return m_Version != 0; } MPT_CONSTEXPRINLINE bool operator ! () const noexcept { return m_Version == 0; } MPT_CONSTEXPRINLINE uint32 GetRawVersion() const noexcept { return m_Version; } MPT_FORCEINLINE Version Masked(uint32 mask) const noexcept { return Version(m_Version & mask); } MPT_CONSTEXPRINLINE uint8 GetField(Field field) const noexcept { return (field == Field::Major) ? static_cast((m_Version >> 24) & 0xffu) : (field == Field::Minor) ? static_cast((m_Version >> 16) & 0xffu) : (field == Field::Patch) ? static_cast((m_Version >> 8) & 0xffu) : (field == Field::Test ) ? static_cast((m_Version >> 0) & 0xffu) : 0u; } // Return a version without build number (the last number in the version). // The current versioning scheme uses this number only for test builds, and it should be 00 for official builds, // So sometimes it might be wanted to do comparisons without the build number. Version WithoutTestNumber() const noexcept; Version WithoutPatchOrTestNumbers() const noexcept; public: // Return a OpenMPT version string suitable for file format tags mpt::ustring GetOpenMPTVersionString() const; // e.g. "OpenMPT 1.17.02.08" // Returns true if a given version number is from a test build, false if it's a release build. bool IsTestVersion() const noexcept; public: struct LiteralParser { public: // Work-around for GCC 5 which complains about instanciating non-literal type inside a constexpr function when using mpt::constexpr_throw(std::runtime_error("")). struct ParseException {}; private: static MPT_CONSTEXPRINLINE uint8 NibbleFromChar(char x) { return ('0' <= x && x <= '9') ? static_cast(x - '0' + 0) : ('a' <= x && x <= 'z') ? static_cast(x - 'a' + 10) : ('A' <= x && x <= 'Z') ? static_cast(x - 'A' + 10) : mpt::constexpr_throw(std::domain_error("")); } public: static MPT_CONSTEXPRINLINE Version Parse(const char * str, std::size_t len) { // 0123456789 // 1.23.45.67 uint8 v[4] = {0, 0, 0, 0}; std::size_t field = 0; std::size_t fieldlen = 0; for(std::size_t i = 0; i < len; ++i) { char c = str[i]; if(c == '.') { if(field >= 3) { mpt::constexpr_throw(ParseException()); } if(fieldlen == 0) { mpt::constexpr_throw(ParseException()); } field++; fieldlen = 0; } else if(('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')) { fieldlen++; if(fieldlen > 2) { mpt::constexpr_throw(ParseException()); } v[field] <<= 4; v[field] |= NibbleFromChar(c); } else { mpt::constexpr_throw(ParseException()); } } if(fieldlen == 0) { mpt::constexpr_throw(ParseException()); } return Version(v[0], v[1], v[2], v[3]); } }; }; MPT_CONSTEXPRINLINE bool operator == (const Version &a, const Version &b) noexcept { return a.GetRawVersion() == b.GetRawVersion(); } MPT_CONSTEXPRINLINE bool operator != (const Version &a, const Version &b) noexcept { return a.GetRawVersion() != b.GetRawVersion(); } MPT_CONSTEXPRINLINE bool operator <= (const Version &a, const Version &b) noexcept { return a.GetRawVersion() <= b.GetRawVersion(); } MPT_CONSTEXPRINLINE bool operator >= (const Version &a, const Version &b) noexcept { return a.GetRawVersion() >= b.GetRawVersion(); } MPT_CONSTEXPRINLINE bool operator < (const Version &a, const Version &b) noexcept { return a.GetRawVersion() < b.GetRawVersion(); } MPT_CONSTEXPRINLINE bool operator > (const Version &a, const Version &b) noexcept { return a.GetRawVersion() > b.GetRawVersion(); } MPT_CONSTEXPRINLINE Version operator "" _LiteralVersionImpl (const char * str, std::size_t len) { return Version::LiteralParser::Parse(str, len); } // Create Version object from version string and check syntax, all at compile time. // cppcheck false-positive // cppcheck-suppress preprocessorErrorDirective #define MPT_V(strver) MPT_FORCE_CONSTEXPR(Version{( strver ## _LiteralVersionImpl ).GetRawVersion()}) class SourceInfo { private: mpt::ustring m_Url; // svn repository url (or empty string) int m_Revision; // svn revision (or 0) bool m_IsDirty; // svn working copy is dirty (or false) bool m_HasMixedRevisions; // svn working copy has mixed revisions (or false) bool m_IsPackage; // source code originates from a packaged version of the source code mpt::ustring m_Date; // svn date (or empty string) private: SourceInfo(); public: static SourceInfo Current(); public: const mpt::ustring & Url() const { return m_Url; } int Revision() const { return m_Revision; } bool IsDirty() const { return m_IsDirty; } bool HasMixedRevisions() const { return m_HasMixedRevisions; } bool IsPackage() const { return m_IsPackage; } const mpt::ustring & Date() const { return m_Date; } public: mpt::ustring GetUrlWithRevision() const; // i.e. "https://source.openmpt.org/svn/openmpt/trunk/OpenMPT@1234" or empty string mpt::ustring GetStateString() const; // i.e. "+dirty" or "clean" }; struct VersionWithRevision { Version version; uint64 revision; static VersionWithRevision Current(); static VersionWithRevision Parse(const mpt::ustring &s); mpt::ustring ToUString() const; constexpr bool HasRevision() const noexcept { return revision != 0; } constexpr bool IsEqualTo(VersionWithRevision other) const noexcept { return version == other.version && revision == other.revision; } constexpr bool IsEquivalentTo(VersionWithRevision other) const noexcept { if(version == other.version && revision == other.revision) { return true; } if(HasRevision() && other.HasRevision()) { return false; } return version == other.version; } constexpr bool IsNewerThan(VersionWithRevision other) const noexcept { if(version < other.version) { return false; } if(version > other.version) { return true; } if(!HasRevision() && !other.HasRevision()) { return false; } if(HasRevision() && other.HasRevision()) { if(revision < other.revision) { return false; } if(revision > other.revision) { return true; } return false; } return false; } constexpr bool IsOlderThan(VersionWithRevision other) const noexcept { if(version < other.version) { return true; } if(version > other.version) { return false; } if(!HasRevision() && !other.HasRevision()) { return false; } if(HasRevision() && other.HasRevision()) { if(revision < other.revision) { return true; } if(revision > other.revision) { return false; } return false; } return false; } }; namespace Build { // Returns true if all conditions for an official release build are met bool IsReleasedBuild(); // Return true if this is a debug build with no optimizations bool IsDebugBuild(); // Return a string decribing the time of the build process (if built from a svn working copy and tsvn was available during build, otherwise it returns the time version.cpp was last rebuild which could be unreliable as it does not get rebuild every time without tsvn) mpt::ustring GetBuildDateString(); // Return a string decribing some of the build features mpt::ustring GetBuildFeaturesString(); // e.g. " NO_VST NO_DSOUND" // Return a string describing the compiler version used for building. mpt::ustring GetBuildCompilerString(); // e.g. "Microsoft Compiler 15.00.20706.01" enum Strings { StringsNone = 0, StringVersion = 1<<0, // "1.23.35.45" StringRevision = 1<<2, // "-r1234+" StringSourceInfo = 1<<5, // "https://source.openmpt.org/svn/openmpt/trunk/OpenMPT@1234 (2016-01-02) +dirty" StringBuildFlags = 1<<6, // "TEST DEBUG" StringBuildFeatures = 1<<7, // "NO_VST NO_DSOUND" }; MPT_DECLARE_ENUM(Strings) // Returns a versions string with the fields selected via @strings. mpt::ustring GetVersionString(FlagSet strings); // Returns a pure version string mpt::ustring GetVersionStringPure(); // e.g. "1.17.02.08-r1234+" // Returns a simple version string mpt::ustring GetVersionStringSimple(); // e.g. "1.17.02.08-r1234+ TEST" // Returns Version::CurrentAsString() if the build is a clean release build straight from the repository or an extended string otherwise (if built from a svn working copy and tsvn was available during build) mpt::ustring GetVersionStringExtended(); // e.g. "1.17.02.08-r1234+ DEBUG" enum class Url { Website, Download, Forum, Bugtracker, Updates, TopPicks, }; // Returns a URL for the respective key. mpt::ustring GetURL(Build::Url key); // Returns a multi-line string containing the full credits for the code base mpt::ustring GetFullCreditsString(); // Returns the OpenMPT license text mpt::ustring GetLicenseString(); } //namespace Build OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/common/versionNumber.h0000644000175000017500000000102414175527626020567 00000000000000/* * versionNumber.h * --------------- * Purpose: OpenMPT version number. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" OPENMPT_NAMESPACE_BEGIN // Version definitions. The only thing that needs to be changed when changing version number. #define VER_MAJORMAJOR 1 #define VER_MAJOR 30 #define VER_MINOR 02 #define VER_MINORMINOR 00 OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/doc/0000755000175000017500000000000014175541576015120 500000000000000libopenmpt-0.6.1+release.autotools/doc/contributing.md0000644000175000017500000000445213416353102020055 00000000000000 Contributing ============ OpenMPT, libopenmpt, openmpt123, xmp-openmpt and in_openmpt are developed in the Subversion repository at [https://source.openmpt.org/browse/openmpt/trunk/OpenMPT/](https://source.openmpt.org/browse/openmpt/trunk/OpenMPT/). Patches can be provided either against this Subversion repository or against our GitHub mirror at [https://github.com/OpenMPT/openmpt/](https://github.com/OpenMPT/openmpt/). We do not have a developer mailing list. Discussions about new features or problems can happen at: * [Issue Tracker](https://bugs.openmpt.org/), preferred for specific bug reports or bug fixes and feature development discussion * [Forum](https://forum.openmpt.org/), preferred for long-term discussion of new features or specific questions about development * [IRC channel (`EsperNET/#modplug`)](irc://irc.esper.net:5555/#modplug), preferred for shorter questions * [GitHub pull requests](https://github.com/OpenMPT/openmpt/pulls), please only use for rather tiny fixes, see below For patch submissions, please also see [OpenMPT style guide](openmpt_styleguide.md) and [libopenmpt style guide](libopenmpt_styleguide.md). ### Contributing via GitHub As OpenMPT is developed in a Subversion repository and the GitHub repository is just mirrored from that, we cannot directly take pull requests via GitHub. We recognize that, especially for tiny bug fixes, the burden to choose a different way than GitHub for contributing can be too high. Thus, we will of course react, give feedback, and take patches also via GitHub pull requests. However, as the GitHub repository is strictly downstream from our Subversion repository (and this will not change, due to considerable complications when synchronizing this two-way), we cannot directly merge pull requests on GitHub. We will merge contributions to our Subversion repository, which will then in turn be mirrored to GitHub automatically, after which we will close the pull request. Authorship attribution in git relies on the email address used in the commit header, which is not how it usually works in Subversion. We will thus add an additional line to the commit message in the form of `Patch-by: John Doe `. If you prefer to be attributed with your nickname and/or without your email address, that would also be fine for us. libopenmpt-0.6.1+release.autotools/doc/libopenmpt_styleguide.md0000644000175000017500000000557713673126612021777 00000000000000 libopenmpt Style Guide ====================== ### libopenmpt **Note:** **This applies to `libopenmpt/` and `openmpt123/` directories only.** **Use OpenMPT style otherwise.** The code generally tries to follow these conventions, but they are not strictly enforced and there are valid reasons to diverge from these conventions. Using common sense is recommended. - In general, the most important thing is to keep style consistent with directly surrounding code. - Use C++ std types when possible, prefer `std::size_t` and `std::int32_t` over `long` or `int`. Do not use C99 std types (e.g. no pure `int32_t`) - Qualify namespaces explicitly, do not use `using`. Members of `namespace openmpt` can be named without full namespace qualification. - Prefer the C++ version in `namespace std` if the same functionality is provided by the C standard library as well. Also, include the C++ version of C standard library headers (e.g. use `` instead of ``. - Do not use ANY locale-dependant C functions. For locale-dependant C++ functionaly (especially iostream), always imbue the `std::locale::classic()` locale. - Prefer kernel_style_names over CamelCaseNames. - If a folder (or one of its parent folders) contains .clang-format, use clang-format v3.5 for indenting C++ and C files, otherwise: - `{` are placed at the end of the opening line. - Enclose even single statements in curly braces. - Avoid placing single statements on the same line as the `if`. - Opening parentheses are separated from keywords with a space. - Opening parentheses are not separated from function names. - Place spaces around operators and inside parentheses. - Align `:` and `,` when inheriting or initializing members in a constructor. - The pointer `*` is separated from both the type and the variable name. - Use tabs for identation, spaces for formatting. Tabs should only appear at the very beginning of a line. Do not assume any particular width of the TAB character. If width is important for formatting reasons, use spaces. - Use empty lines at will. - API documentation is done with doxygen. Use general C doxygen for the C API. Use QT-style doxygen for the C++ API. #### libopenmpt indentation example ~~~~{.cpp} namespace openmpt { // This is totally meaningless code and just illustrates indentation. class foo : public base , public otherbase { private: std::int32_t x; std::int16_t y; public: foo() : x(0) , y(-1) { return; } int bar() const; }; // class foo int foo::bar() const { for ( int i = 0; i < 23; ++i ) { switch ( x ) { case 2: something( y ); break; default: something( ( y - 1 ) * 2 ); break; } } if ( x == 12 ) { return -1; } else if ( x == 42 ) { return 1; } return 42; } } // namespace openmpt ~~~~ libopenmpt-0.6.1+release.autotools/doc/module_formats.md0000644000175000017500000001434014004056732020366 00000000000000Adding support for new module formats ===================================== This document describes the basics of writing a new module loader and related work that has to be done. We will not discuss in detail how to write the loader, have a look at existing loaders to get an idea how they work in general. General hints ------------- * We strive for quality over quantity. The goal is not to support as many module formats as possible, but to support them as well as possible. * Write defensive code. Guard against out-of-bound values, division by zero and similar stuff. libopenmpt is constantly fuzz-tested to catch any crashes, but of course we want our code to be reliable from the start. * Every format should have its own `MODTYPE` flag, unless it can be reasonably represented as a subset of another format (like Ice Tracker ICE files being a subset of ProTracker MOD). * When reading binary structs from the file, use our data types with defined endianness, which can be found in `common/Endianness.h`: * Big-Endian: (u)int8/16/32/64be, float32be, float64be * Little-Endian: (u)int8/16/32/64le, float32le, float64le Entire structs containing integers with defined endianness can be read in one go if they are tagged with `MPT_BINARY_STRUCT` (see existing loaders for an example). * `CSoundFile::m_nChannels` **MUST NOT** be changed after a pattern has been created, as existing patterns will be interpreted incorrectly. For module formats that support per-pattern channel amounts, the maximum number of channels must be determined beforehand. * Strings can be safely handled using: * `FileReader::ReadString` and friends for reading them directly from a file * `mpt::String::ReadBuf` for reading them from a struct or char array These functions take care of string padding (zero / space padding) and will avoid reading past the end of the string if there is no terminating null character. * Do not use non-const static variables in your loader. Loaders need to be thread-safe for libopenmpt. * `FileReader` instances may be used to treat a portion of a file as its own independent file (through `FileReader::ReadChunk`). This can be useful with "embedded files" such as WAV or Ogg samples. Container formats such as UMX are another good example for this usage. * Samples *either* use middle-C frequency *or* finetune + transpose. For the few weird formats that use both, it may make sense to translate everything into middle-C frequency. * Add the new `MODTYPE` to `CSoundFile::UseFinetuneAndTranspose` if applicable, and see if any effect handlers in `soundlib/Snd_fx.cpp` need to know the new `MODTYPE`. * Do not rely on hard-coded magic numbers. For example, when comparing if an index is valid for a given array, do not hard-code the array size but rather use `std::size` (or `mpt::array_size` in contexts where `std::size` is not usable) or, for ensuring that char arrays are null-terminated, `mpt::String::SetNullTerminator`. Similarly, do not assume any specific quantities for OpenMPT's constants like MAX_SAMPLES, MAX_PATTERN_ROWS, etc. These may change at any time. * Pay attention to off-by-one errors when comparing against MAX_SAMPLES and MAX_INSTRUMENTS, since sample and instrument numbers are 1-based. Prefer using `CSoundFile::CanAddMoreSamples` and `CSoundFile::CanAddMoreInstruments` to check if a given amount of samples or instruments can be added to the file rather than doing the calculations yourself. * Placement of the loader function in the `ModuleFormatLoaders` array in Sndfile.cpp depends on various factors. In general, module formats that have very bad magic numbers (and thus might cause other formats to get mis-interpreted) should be placed at the bottom of the list. Two notable examples are 669 files, where the first two bytes of the file are "if" (which may e.g. cause a song title starting with if ..." in various other formats to be interpreted as a 669 module), and of course Ultimate SoundTracker modules, which have no magic bytes at all. * Avoid use of functions tagged with [[deprecated]]. Probing ------- libopenmpt provides fast probing functions that can be used by library users to quickly check if a file is most likely playable with libopenmpt, even if only a fraction of the file is available (e.g. when streaming from the internet). In order to satisfy these requirements, probing functions should do as little work as possible (e.g. only parse the header of the file), but as much as required to tell with some certainty that the file is really of a certain mod format. However, probing functions should not rely on having access to more than the first `CSoundFile::ProbeRecommendedSize` bytes of the file. * Probing functions **must not** allocate any memory on the heap. * Probing functions **must not** return ProbeFailure or ProbeWantMoreData for any file that would normally be accepted by the loader. In particular, this means that any header checks must not be any more aggressive than they would be in the real loader (hence it is a good idea to not copy-paste this code but rather put it in a separate function), and the minimum additional size passed to `CSoundFile::ProbeAdditionalSize` must not be higher than the biggest size that would cause a hard failure (i.e. returning `false`) in the module loader. * Probing functions **may** return ProbeSuccess for files that would be rejected by a loader after a more thorough inspection. For example, probing functions do not need to verify that all required chunks of an IFF-like file format are actually present, if the header makes it obvious enough that the file is highly likely to be a module. Adding loader to the build systems and various other locations -------------------------------------------------------------- Apart from writing the module loader itself, there are a couple of other places that need to be updated: * Add loader file to `build/android_ndk/Android.mk`. * Add loader file to `build/autotools/Makefile.am`. * Run `build/regenerate_vs_projects.sh` / `build/regenerate_vs_projects.cmd` (depending on your platform) * Add file extension to `installer/filetypes-*.iss`. * Add file extension to `CTrackApp::OpenModulesDialog` in `mptrack/Mptrack.cpp`. * Add format information to `soundlib/Tables.cpp`. libopenmpt-0.6.1+release.autotools/doc/openmpt_styleguide.md0000644000175000017500000000153413673126612021275 00000000000000 OpenMPT Style Guide =================== ### OpenMPT **Note:** **This applies to all source code *except* for `libopenmpt/` and `openmpt123/`** **directories.** **Use libopenmpt style otherwise.** (see below for an example) * Place curly braces at the beginning of the line, not at the end * Generally make use of the custom index types like `SAMPLEINDEX` or `ORDERINDEX` when referring to samples, orders, etc. * When changing playback behaviour, make sure that you use the function `CSoundFile::IsCompatibleMode()` so that modules made with previous versions of MPT still sound correct (if the change is extremely small, this might be unnecessary) * `CamelCase` function and variable names are preferred. #### OpenMPT code example ~~~~{.cpp} void Foo::Bar(int foobar) { while(true) { // some code } } ~~~~ libopenmpt-0.6.1+release.autotools/examples/0000755000175000017500000000000014175541576016171 500000000000000libopenmpt-0.6.1+release.autotools/examples/libopenmpt_example_cxx.cpp0000644000175000017500000001101114012704654023341 00000000000000/* * libopenmpt_example_cxx.cpp * -------------------------- * Purpose: libopenmpt C++ API example * Notes : PortAudio C++ is used for sound output. * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ /* * Usage: libopenmpt_example_cxx SOMEMODULE */ #include #include #include #include #include #include #include #if defined( __clang__ ) #if ( ( __clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__ ) >= 40000 ) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-dynamic-exception-spec" #endif #endif #include #if defined( __clang__ ) #if ( ( __clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__ ) >= 40000 ) #pragma clang diagnostic pop #endif #endif #if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) #if defined( __GNUC__ ) // mingw-w64 g++ does only default to special C linkage for "main", but not for "wmain" (see ). extern "C" int wmain( int argc, wchar_t * argv[] ) { #else int wmain( int argc, wchar_t * argv[] ) { #endif #else int main( int argc, char * argv[] ) { #endif try { if ( argc != 2 ) { throw std::runtime_error( "Usage: libopenmpt_example_cxx SOMEMODULE" ); } constexpr std::size_t buffersize = 480; constexpr std::int32_t samplerate = 48000; std::ifstream file( argv[1], std::ios::binary ); openmpt::module mod( file ); portaudio::AutoSystem portaudio_initializer; portaudio::System & portaudio = portaudio::System::instance(); std::vector left( buffersize ); std::vector right( buffersize ); std::vector interleaved_buffer( buffersize * 2 ); bool is_interleaved = false; #if defined( _MSC_VER ) && defined( _PREFAST_ ) // work-around bug in VS2019 MSVC 16.5.5 static analyzer is_interleaved = false; portaudio::DirectionSpecificStreamParameters outputstream_parameters( portaudio.defaultOutputDevice(), 2, portaudio::FLOAT32, false, portaudio.defaultOutputDevice().defaultHighOutputLatency(), 0 ); portaudio::StreamParameters stream_parameters( portaudio::DirectionSpecificStreamParameters::null(), outputstream_parameters, samplerate, paFramesPerBufferUnspecified, paNoFlag ); portaudio::BlockingStream stream( stream_parameters ); #else portaudio::BlockingStream stream = [&]() { try { is_interleaved = false; portaudio::DirectionSpecificStreamParameters outputstream_parameters( portaudio.defaultOutputDevice(), 2, portaudio::FLOAT32, false, portaudio.defaultOutputDevice().defaultHighOutputLatency(), 0 ); portaudio::StreamParameters stream_parameters( portaudio::DirectionSpecificStreamParameters::null(), outputstream_parameters, samplerate, paFramesPerBufferUnspecified, paNoFlag ); return portaudio::BlockingStream( stream_parameters ); } catch ( const portaudio::PaException & e ) { if ( e.paError() != paSampleFormatNotSupported ) { throw; } is_interleaved = true; portaudio::DirectionSpecificStreamParameters outputstream_parameters( portaudio.defaultOutputDevice(), 2, portaudio::FLOAT32, true, portaudio.defaultOutputDevice().defaultHighOutputLatency(), 0 ); portaudio::StreamParameters stream_parameters( portaudio::DirectionSpecificStreamParameters::null(), outputstream_parameters, samplerate, paFramesPerBufferUnspecified, paNoFlag ); return portaudio::BlockingStream( stream_parameters ); } }(); #endif stream.start(); while ( true ) { std::size_t count = is_interleaved ? mod.read_interleaved_stereo( samplerate, buffersize, interleaved_buffer.data() ) : mod.read( samplerate, buffersize, left.data(), right.data() ); if ( count == 0 ) { break; } try { if ( is_interleaved ) { stream.write( interleaved_buffer.data(), static_cast( count ) ); } else { const float * const buffers[2] = { left.data(), right.data() }; stream.write( buffers, static_cast( count ) ); } } catch ( const portaudio::PaException & pa_exception ) { if ( pa_exception.paError() != paOutputUnderflowed ) { throw; } } } stream.stop(); } catch ( const std::bad_alloc & ) { std::cerr << "Error: " << std::string( "out of memory" ) << std::endl; return 1; } catch ( const std::exception & e ) { std::cerr << "Error: " << std::string( e.what() ? e.what() : "unknown error" ) << std::endl; return 1; } return 0; } libopenmpt-0.6.1+release.autotools/examples/libopenmpt_example_c_mem.c0000644000175000017500000001574713655517022023306 00000000000000/* * libopenmpt_example_c_mem.c * -------------------------- * Purpose: libopenmpt C API example * Notes : PortAudio is used for sound output. * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ /* * Usage: libopenmpt_example_c_mem SOMEMODULE */ #include #include #include #include #include #include #include #define BUFFERSIZE 480 #define SAMPLERATE 48000 static int16_t left[BUFFERSIZE]; static int16_t right[BUFFERSIZE]; static int16_t * const buffers[2] = { left, right }; static int16_t interleaved_buffer[BUFFERSIZE * 2]; static int is_interleaved = 0; static void libopenmpt_example_logfunc( const char * message, void * userdata ) { (void)userdata; if ( message ) { fprintf( stderr, "openmpt: %s\n", message ); } } static int libopenmpt_example_errfunc( int error, void * userdata ) { (void)userdata; (void)error; return OPENMPT_ERROR_FUNC_RESULT_DEFAULT & ~OPENMPT_ERROR_FUNC_RESULT_LOG; } static void libopenmpt_example_print_error( const char * func_name, int mod_err, const char * mod_err_str ) { if ( !func_name ) { func_name = "unknown function"; } if ( mod_err == OPENMPT_ERROR_OUT_OF_MEMORY ) { mod_err_str = openmpt_error_string( mod_err ); if ( !mod_err_str ) { fprintf( stderr, "Error: %s\n", "OPENMPT_ERROR_OUT_OF_MEMORY" ); } else { fprintf( stderr, "Error: %s\n", mod_err_str ); openmpt_free_string( mod_err_str ); mod_err_str = NULL; } } else { if ( !mod_err_str ) { mod_err_str = openmpt_error_string( mod_err ); if ( !mod_err_str ) { fprintf( stderr, "Error: %s failed.\n", func_name ); } else { fprintf( stderr, "Error: %s failed: %s\n", func_name, mod_err_str ); } openmpt_free_string( mod_err_str ); mod_err_str = NULL; } fprintf( stderr, "Error: %s failed: %s\n", func_name, mod_err_str ); } } typedef struct blob_t { size_t size; void * data; } blob_t; static void free_blob( blob_t * blob ) { if ( blob ) { if ( blob->data ) { free( blob->data ); blob->data = 0; } blob->size = 0; free( blob ); } } #if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) static blob_t * load_file( const wchar_t * filename ) { #else static blob_t * load_file( const char * filename ) { #endif blob_t * result = 0; blob_t * blob = 0; FILE * file = 0; long tell_result = 0; blob = malloc( sizeof( blob_t ) ); if ( !blob ) { goto fail; } memset( blob, 0, sizeof( blob_t ) ); #if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) file = _wfopen( filename, L"rb" ); #else file = fopen( filename, "rb" ); #endif if ( !file ) { goto fail; } if ( fseek( file, 0, SEEK_END ) != 0 ) { goto fail; } tell_result = ftell( file ); if ( tell_result < 0 ) { goto fail; } if ( (unsigned long)tell_result > SIZE_MAX ) { goto fail; } blob->size = (size_t)tell_result; if ( fseek( file, 0, SEEK_SET ) != 0 ) { goto fail; } blob->data = malloc( blob->size ); if ( !blob->data ) { goto fail; } memset( blob->data, 0, blob->size ); if ( fread( blob->data, 1, blob->size, file ) != blob->size ) { goto fail; } result = blob; blob = 0; goto cleanup; fail: result = 0; cleanup: if ( blob ) { free_blob( blob ); blob = 0; } if ( file ) { fclose( file ); file = 0; } return result; } #if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) int wmain( int argc, wchar_t * argv[] ) { #else int main( int argc, char * argv[] ) { #endif int result = 0; blob_t * blob = 0; openmpt_module * mod = 0; int mod_err = OPENMPT_ERROR_OK; const char * mod_err_str = NULL; size_t count = 0; PaError pa_error = paNoError; int pa_initialized = 0; PaStream * stream = 0; if ( argc != 2 ) { fprintf( stderr, "Error: %s\n", "Wrong invocation. Use 'libopenmpt_example_c_mem SOMEMODULE'." ); goto fail; } #if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) if ( wcslen( argv[1] ) == 0 ) { fprintf( stderr, "Error: %s\n", "Wrong invocation. Use 'libopenmpt_example_c_mem SOMEMODULE'." ); goto fail; } #else if ( strlen( argv[1] ) == 0 ) { fprintf( stderr, "Error: %s\n", "Wrong invocation. Use 'libopenmpt_example_c_mem SOMEMODULE'." ); goto fail; } #endif blob = load_file( argv[1] ); if ( !blob ) { fprintf( stderr, "Error: %s\n", "load_file() failed." ); goto fail; } mod = openmpt_module_create_from_memory2( blob->data, blob->size, &libopenmpt_example_logfunc, NULL, &libopenmpt_example_errfunc, NULL, &mod_err, &mod_err_str, NULL ); if ( !mod ) { libopenmpt_example_print_error( "openmpt_module_create_from_memory2()", mod_err, mod_err_str ); openmpt_free_string( mod_err_str ); mod_err_str = NULL; goto fail; } pa_error = Pa_Initialize(); if ( pa_error != paNoError ) { fprintf( stderr, "Error: %s\n", "Pa_Initialize() failed." ); goto fail; } pa_initialized = 1; is_interleaved = 0; pa_error = Pa_OpenDefaultStream( &stream, 0, 2, paInt16 | paNonInterleaved, SAMPLERATE, paFramesPerBufferUnspecified, NULL, NULL ); if ( pa_error == paSampleFormatNotSupported ) { is_interleaved = 1; pa_error = Pa_OpenDefaultStream( &stream, 0, 2, paInt16, SAMPLERATE, paFramesPerBufferUnspecified, NULL, NULL ); } if ( pa_error != paNoError ) { fprintf( stderr, "Error: %s\n", "Pa_OpenStream() failed." ); goto fail; } if ( !stream ) { fprintf( stderr, "Error: %s\n", "Pa_OpenStream() failed." ); goto fail; } pa_error = Pa_StartStream( stream ); if ( pa_error != paNoError ) { fprintf( stderr, "Error: %s\n", "Pa_StartStream() failed." ); goto fail; } while ( 1 ) { openmpt_module_error_clear( mod ); count = is_interleaved ? openmpt_module_read_interleaved_stereo( mod, SAMPLERATE, BUFFERSIZE, interleaved_buffer ) : openmpt_module_read_stereo( mod, SAMPLERATE, BUFFERSIZE, left, right ); mod_err = openmpt_module_error_get_last( mod ); mod_err_str = openmpt_module_error_get_last_message( mod ); if ( mod_err != OPENMPT_ERROR_OK ) { libopenmpt_example_print_error( "openmpt_module_read_stereo()", mod_err, mod_err_str ); openmpt_free_string( mod_err_str ); mod_err_str = NULL; } if ( count == 0 ) { break; } pa_error = is_interleaved ? Pa_WriteStream( stream, interleaved_buffer, (unsigned long)count ) : Pa_WriteStream( stream, buffers, (unsigned long)count ); if ( pa_error == paOutputUnderflowed ) { pa_error = paNoError; } if ( pa_error != paNoError ) { fprintf( stderr, "Error: %s\n", "Pa_WriteStream() failed." ); goto fail; } } result = 0; goto cleanup; fail: result = 1; cleanup: if ( stream ) { if ( Pa_IsStreamActive( stream ) == 1 ) { Pa_StopStream( stream ); } Pa_CloseStream( stream ); stream = 0; } if ( pa_initialized ) { Pa_Terminate(); pa_initialized = 0; } if ( mod ) { openmpt_module_destroy( mod ); mod = 0; } if ( blob ) { free_blob( blob ); blob = 0; } return result; } libopenmpt-0.6.1+release.autotools/examples/libopenmpt_example_c_unsafe.c0000644000175000017500000000351613051352114023765 00000000000000/* * libopenmpt_example_c_unsafe.c * ----------------------------- * Purpose: libopenmpt C API simplified example * Notes : PortAudio is used for sound output. * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ /* * Usage: libopenmpt_example_c_unsafe SOMEMODULE * CAUTION: This simple example does no error cheking at all. */ #include #include #include #include #include #include #include #include #define BUFFERSIZE 480 #define SAMPLERATE 48000 static int16_t left[BUFFERSIZE]; static int16_t right[BUFFERSIZE]; static int16_t * const buffers[2] = { left, right }; #if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) int wmain( int argc, wchar_t * argv[] ) { #else int main( int argc, char * argv[] ) { #endif FILE * file = 0; openmpt_module * mod = 0; size_t count = 0; PaStream * stream = 0; (void)argc; #if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) file = _wfopen( argv[1], L"rb" ); #else file = fopen( argv[1], "rb" ); #endif mod = openmpt_module_create2( openmpt_stream_get_file_callbacks(), file, NULL, NULL, NULL, NULL, NULL, NULL, NULL ); fclose( file ); Pa_Initialize(); Pa_OpenDefaultStream( &stream, 0, 2, paInt16 | paNonInterleaved, SAMPLERATE, paFramesPerBufferUnspecified, NULL, NULL ); Pa_StartStream( stream ); while ( 1 ) { count = openmpt_module_read_stereo( mod, SAMPLERATE, BUFFERSIZE, left, right ); if ( count == 0 ) { break; } Pa_WriteStream( stream, buffers, (unsigned long)count ); } Pa_StopStream( stream ); Pa_CloseStream( stream ); Pa_Terminate(); openmpt_module_destroy( mod ); return 0; } libopenmpt-0.6.1+release.autotools/examples/libopenmpt_example_c.c0000644000175000017500000001311013655517022022426 00000000000000/* * libopenmpt_example_c.c * ---------------------- * Purpose: libopenmpt C API example * Notes : PortAudio is used for sound output. * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ /* * Usage: libopenmpt_example_c SOMEMODULE */ #include #include #include #include #include #include #include #include #define BUFFERSIZE 480 #define SAMPLERATE 48000 static int16_t left[BUFFERSIZE]; static int16_t right[BUFFERSIZE]; static int16_t * const buffers[2] = { left, right }; static int16_t interleaved_buffer[BUFFERSIZE * 2]; static int is_interleaved = 0; static void libopenmpt_example_logfunc( const char * message, void * userdata ) { (void)userdata; if ( message ) { fprintf( stderr, "openmpt: %s\n", message ); } } static int libopenmpt_example_errfunc( int error, void * userdata ) { (void)userdata; (void)error; return OPENMPT_ERROR_FUNC_RESULT_DEFAULT & ~OPENMPT_ERROR_FUNC_RESULT_LOG; } static void libopenmpt_example_print_error( const char * func_name, int mod_err, const char * mod_err_str ) { if ( !func_name ) { func_name = "unknown function"; } if ( mod_err == OPENMPT_ERROR_OUT_OF_MEMORY ) { mod_err_str = openmpt_error_string( mod_err ); if ( !mod_err_str ) { fprintf( stderr, "Error: %s\n", "OPENMPT_ERROR_OUT_OF_MEMORY" ); } else { fprintf( stderr, "Error: %s\n", mod_err_str ); openmpt_free_string( mod_err_str ); mod_err_str = NULL; } } else { if ( !mod_err_str ) { mod_err_str = openmpt_error_string( mod_err ); if ( !mod_err_str ) { fprintf( stderr, "Error: %s failed.\n", func_name ); } else { fprintf( stderr, "Error: %s failed: %s\n", func_name, mod_err_str ); } openmpt_free_string( mod_err_str ); mod_err_str = NULL; } fprintf( stderr, "Error: %s failed: %s\n", func_name, mod_err_str ); } } #if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) int wmain( int argc, wchar_t * argv[] ) { #else int main( int argc, char * argv[] ) { #endif int result = 0; FILE * file = 0; openmpt_module * mod = 0; int mod_err = OPENMPT_ERROR_OK; const char * mod_err_str = NULL; size_t count = 0; PaError pa_error = paNoError; int pa_initialized = 0; PaStream * stream = 0; if ( argc != 2 ) { fprintf( stderr, "Error: %s\n", "Wrong invocation. Use 'libopenmpt_example_c SOMEMODULE'." ); goto fail; } #if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) if ( wcslen( argv[1] ) == 0 ) { fprintf( stderr, "Error: %s\n", "Wrong invocation. Use 'libopenmpt_example_c SOMEMODULE'." ); goto fail; } file = _wfopen( argv[1], L"rb" ); #else if ( strlen( argv[1] ) == 0 ) { fprintf( stderr, "Error: %s\n", "Wrong invocation. Use 'libopenmpt_example_c SOMEMODULE'." ); goto fail; } file = fopen( argv[1], "rb" ); #endif if ( !file ) { fprintf( stderr, "Error: %s\n", "fopen() failed." ); goto fail; } mod = openmpt_module_create2( openmpt_stream_get_file_callbacks(), file, &libopenmpt_example_logfunc, NULL, &libopenmpt_example_errfunc, NULL, &mod_err, &mod_err_str, NULL ); if ( !mod ) { libopenmpt_example_print_error( "openmpt_module_create2()", mod_err, mod_err_str ); openmpt_free_string( mod_err_str ); mod_err_str = NULL; goto fail; } openmpt_module_set_error_func( mod, NULL, NULL ); pa_error = Pa_Initialize(); if ( pa_error != paNoError ) { fprintf( stderr, "Error: %s\n", "Pa_Initialize() failed." ); goto fail; } pa_initialized = 1; pa_error = Pa_OpenDefaultStream( &stream, 0, 2, paInt16 | paNonInterleaved, SAMPLERATE, paFramesPerBufferUnspecified, NULL, NULL ); if ( pa_error == paSampleFormatNotSupported ) { is_interleaved = 1; pa_error = Pa_OpenDefaultStream( &stream, 0, 2, paInt16, SAMPLERATE, paFramesPerBufferUnspecified, NULL, NULL ); } if ( pa_error != paNoError ) { fprintf( stderr, "Error: %s\n", "Pa_OpenStream() failed." ); goto fail; } if ( !stream ) { fprintf( stderr, "Error: %s\n", "Pa_OpenStream() failed." ); goto fail; } pa_error = Pa_StartStream( stream ); if ( pa_error != paNoError ) { fprintf( stderr, "Error: %s\n", "Pa_StartStream() failed." ); goto fail; } while ( 1 ) { openmpt_module_error_clear( mod ); count = is_interleaved ? openmpt_module_read_interleaved_stereo( mod, SAMPLERATE, BUFFERSIZE, interleaved_buffer ) : openmpt_module_read_stereo( mod, SAMPLERATE, BUFFERSIZE, left, right ); mod_err = openmpt_module_error_get_last( mod ); mod_err_str = openmpt_module_error_get_last_message( mod ); if ( mod_err != OPENMPT_ERROR_OK ) { libopenmpt_example_print_error( "openmpt_module_read_stereo()", mod_err, mod_err_str ); openmpt_free_string( mod_err_str ); mod_err_str = NULL; } if ( count == 0 ) { break; } pa_error = is_interleaved ? Pa_WriteStream( stream, interleaved_buffer, (unsigned long)count ) : Pa_WriteStream( stream, buffers, (unsigned long)count ); if ( pa_error == paOutputUnderflowed ) { pa_error = paNoError; } if ( pa_error != paNoError ) { fprintf( stderr, "Error: %s\n", "Pa_WriteStream() failed." ); goto fail; } } result = 0; goto cleanup; fail: result = 1; cleanup: if ( stream ) { if ( Pa_IsStreamActive( stream ) == 1 ) { Pa_StopStream( stream ); } Pa_CloseStream( stream ); stream = 0; } if ( pa_initialized ) { Pa_Terminate(); pa_initialized = 0; } if ( mod ) { openmpt_module_destroy( mod ); mod = 0; } if ( file ) { fclose( file ); file = 0; } return result; } libopenmpt-0.6.1+release.autotools/examples/libopenmpt_example_c_probe.c0000644000175000017500000001173314012704654023623 00000000000000/* * libopenmpt_example_c_probe.c * ---------------------------- * Purpose: libopenmpt C API probing example * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ /* * Usage: libopenmpt_example_c_probe SOMEMODULE ... * Returns 0 on successful probing for all files. * Returns 1 on failed probing for 1 or more files. * Returns 2 on error. */ #define LIBOPENMPT_EXAMPLE_PROBE_RESULT_BINARY 1 #define LIBOPENMPT_EXAMPLE_PROBE_RESULT_FLOAT 2 #define LIBOPENMPT_EXAMPLE_PROBE_RESULT LIBOPENMPT_EXAMPLE_PROBE_RESULT_BINARY #include #include #include #include #include #include #include static void libopenmpt_example_logfunc( const char * message, void * userdata ) { (void)userdata; if ( message ) { fprintf( stderr, "%s\n", message ); } } #if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) static int probe_file( const wchar_t * filename ) { #else static int probe_file( const char * filename ) { #endif int result = 0; int mod_err = OPENMPT_ERROR_OK; FILE * file = NULL; #if ( LIBOPENMPT_EXAMPLE_PROBE_RESULT == LIBOPENMPT_EXAMPLE_PROBE_RESULT_BINARY ) int result_binary = 0; int probe_file_header_result = OPENMPT_PROBE_FILE_HEADER_RESULT_FAILURE; const char * probe_file_header_result_str = NULL; #endif #if ( LIBOPENMPT_EXAMPLE_PROBE_RESULT == LIBOPENMPT_EXAMPLE_PROBE_RESULT_FLOAT ) double probability = 0.0; #endif #if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) if ( wcslen( filename ) == 0 ) { fprintf( stderr, "Error: %s\n", "Wrong invocation. Use 'libopenmpt_example_c_probe SOMEMODULE'." ); goto fail; } #else if ( strlen( filename ) == 0 ) { fprintf( stderr, "Error: %s\n", "Wrong invocation. Use 'libopenmpt_example_c_probe SOMEMODULE'." ); goto fail; } #endif #if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) file = _wfopen( filename, L"rb" ); #else file = fopen( filename, "rb" ); #endif if ( !file ) { fprintf( stderr, "Error: %s\n", "fopen() failed." ); goto fail; } #if ( LIBOPENMPT_EXAMPLE_PROBE_RESULT == LIBOPENMPT_EXAMPLE_PROBE_RESULT_BINARY ) probe_file_header_result = openmpt_probe_file_header_from_stream( OPENMPT_PROBE_FILE_HEADER_FLAGS_DEFAULT, openmpt_stream_get_file_callbacks(), file, &libopenmpt_example_logfunc, NULL, &openmpt_error_func_default, NULL, &mod_err, NULL ); probe_file_header_result_str = NULL; result_binary = 0; switch ( probe_file_header_result ) { case OPENMPT_PROBE_FILE_HEADER_RESULT_SUCCESS: probe_file_header_result_str = "Success "; result_binary = 1; break; case OPENMPT_PROBE_FILE_HEADER_RESULT_FAILURE: probe_file_header_result_str = "Failure "; result_binary = 0; break; case OPENMPT_PROBE_FILE_HEADER_RESULT_WANTMOREDATA: probe_file_header_result_str = "WantMoreData"; result_binary = 0; break; case OPENMPT_PROBE_FILE_HEADER_RESULT_ERROR: result_binary = 0; fprintf( stderr, "Error: %s\n", "openmpt_probe_file_header() failed." ); goto fail; break; default: result_binary = 0; fprintf( stderr, "Error: %s\n", "openmpt_probe_file_header() failed." ); goto fail; break; } #if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) fprintf( stdout, "%s - %ls\n", probe_file_header_result_str, filename ); #else fprintf( stdout, "%s - %s\n", probe_file_header_result_str, filename ); #endif if ( result_binary ) { result = 0; } else { result = 1; } #elif ( LIBOPENMPT_EXAMPLE_PROBE_RESULT == LIBOPENMPT_EXAMPLE_PROBE_RESULT_FLOAT ) probability = openmpt_could_open_probability2( openmpt_stream_get_file_callbacks(), file, 0.25, &libopenmpt_example_logfunc, NULL, &openmpt_error_func_default, NULL, &mod_err, NULL ); #if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) fprintf( stdout, "%s: %f - %ls\n", "Result", probability, filename ); #else fprintf( stdout, "%s: %f - %s\n", "Result", probability, filename ); #endif if ( probability >= 0.5 ) { result = 0; } else { result = 1; } #else #error "LIBOPENMPT_EXAMPLE_PROBE_RESULT is wrong" #endif goto cleanup; fail: result = 2; cleanup: if ( file ) { fclose( file ); file = 0; } return result; } #if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) int wmain( int argc, wchar_t * argv[] ) { #else int main( int argc, char * argv[] ) { #endif int global_result = 0; if ( argc <= 1 ) { fprintf( stderr, "Error: %s\n", "Wrong invocation. Use 'libopenmpt_example_c_probe SOMEMODULE ...'." ); goto fail; } for ( int i = 1; i < argc; ++i ) { int result = probe_file( argv[i] ); if ( result > global_result ) { global_result = result; } } goto cleanup; fail: global_result = 2; cleanup: return global_result; } libopenmpt-0.6.1+release.autotools/examples/libopenmpt_example_c_stdout.c0000644000175000017500000001077613051352114024034 00000000000000/* * libopenmpt_example_c_stdout.c * ----------------------------- * Purpose: libopenmpt C API simple example * Notes : This example writes raw 48000Hz / stereo / 16bit native endian PCM data to stdout. * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ /* * Usage: libopenmpt_example_c_stdout SOMEMODULE | aplay --file-type raw --format=dat */ #include #include #include #include #include #include #include #include #include #define BUFFERSIZE 480 #define SAMPLERATE 48000 static void libopenmpt_example_logfunc( const char * message, void * userdata ) { (void)userdata; if ( message ) { fprintf( stderr, "openmpt: %s\n", message ); } } static int libopenmpt_example_errfunc( int error, void * userdata ) { (void)userdata; (void)error; return OPENMPT_ERROR_FUNC_RESULT_DEFAULT & ~OPENMPT_ERROR_FUNC_RESULT_LOG; } static void libopenmpt_example_print_error( const char * func_name, int mod_err, const char * mod_err_str ) { if ( !func_name ) { func_name = "unknown function"; } if ( mod_err == OPENMPT_ERROR_OUT_OF_MEMORY ) { mod_err_str = openmpt_error_string( mod_err ); if ( !mod_err_str ) { fprintf( stderr, "Error: %s\n", "OPENMPT_ERROR_OUT_OF_MEMORY" ); } else { fprintf( stderr, "Error: %s\n", mod_err_str ); openmpt_free_string( mod_err_str ); mod_err_str = NULL; } } else { if ( !mod_err_str ) { mod_err_str = openmpt_error_string( mod_err ); if ( !mod_err_str ) { fprintf( stderr, "Error: %s failed.\n", func_name ); } else { fprintf( stderr, "Error: %s failed: %s\n", func_name, mod_err_str ); } openmpt_free_string( mod_err_str ); mod_err_str = NULL; } fprintf( stderr, "Error: %s failed: %s\n", func_name, mod_err_str ); } } static ssize_t xwrite( int fd, const void * buffer, size_t size ) { size_t written = 0; ssize_t retval = 0; while ( written < size ) { retval = write( fd, (const char *)buffer + written, size - written ); if ( retval < 0 ) { if ( errno != EINTR ) { break; } retval = 0; } written += retval; } return written; } static int16_t buffer[BUFFERSIZE * 2]; #if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) int wmain( int argc, wchar_t * argv[] ) { #else int main( int argc, char * argv[] ) { #endif int result = 0; FILE * file = 0; openmpt_module * mod = 0; int mod_err = OPENMPT_ERROR_OK; const char * mod_err_str = NULL; size_t count = 0; size_t written = 0; if ( argc != 2 ) { fprintf( stderr, "Error: %s\n", "Wrong invocation. Use 'libopenmpt_example_c_stdout SOMEMODULE'." ); goto fail; } #if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) if ( wcslen( argv[1] ) == 0 ) { fprintf( stderr, "Error: %s\n", "Wrong invocation. Use 'libopenmpt_example_c_stdout SOMEMODULE'." ); goto fail; } file = _wfopen( argv[1], L"rb" ); #else if ( strlen( argv[1] ) == 0 ) { fprintf( stderr, "Error: %s\n", "Wrong invocation. Use 'libopenmpt_example_c_stdout SOMEMODULE'." ); goto fail; } file = fopen( argv[1], "rb" ); #endif if ( !file ) { fprintf( stderr, "Error: %s\n", "fopen() failed." ); goto fail; } mod = openmpt_module_create2( openmpt_stream_get_file_callbacks(), file, &libopenmpt_example_logfunc, NULL, &libopenmpt_example_errfunc, NULL, &mod_err, &mod_err_str, NULL ); if ( !mod ) { libopenmpt_example_print_error( "openmpt_module_create2()", mod_err, mod_err_str ); openmpt_free_string( mod_err_str ); mod_err_str = NULL; goto fail; } while ( 1 ) { openmpt_module_error_clear( mod ); count = openmpt_module_read_interleaved_stereo( mod, SAMPLERATE, BUFFERSIZE, buffer ); mod_err = openmpt_module_error_get_last( mod ); mod_err_str = openmpt_module_error_get_last_message( mod ); if ( mod_err != OPENMPT_ERROR_OK ) { libopenmpt_example_print_error( "openmpt_module_read_interleaved_stereo()", mod_err, mod_err_str ); openmpt_free_string( mod_err_str ); mod_err_str = NULL; } if ( count == 0 ) { break; } written = xwrite( STDOUT_FILENO, buffer, count * 2 * sizeof( int16_t ) ); if ( written == 0 ) { fprintf( stderr, "Error: %s\n", "write() failed." ); goto fail; } } result = 0; goto cleanup; fail: result = 1; cleanup: if ( mod ) { openmpt_module_destroy( mod ); mod = 0; } if ( file ) { fclose( file ); file = 0; } return result; } libopenmpt-0.6.1+release.autotools/examples/.clang-format0000644000175000017500000000376014005226106020447 00000000000000# clang-format 10 Language: Cpp Standard: c++17 AccessModifierOffset: -2 AlignAfterOpenBracket: true AlignConsecutiveAssignments: false AlignConsecutiveMacros: true AlignEscapedNewlinesLeft: true AlignOperands: true AlignTrailingComments: false AllowAllParametersOfDeclarationOnNextLine: false AllowShortBlocksOnASingleLine: false AllowShortCaseLabelsOnASingleLine: false AllowShortFunctionsOnASingleLine: All AllowShortIfStatementsOnASingleLine: false AllowShortLoopsOnASingleLine: false AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: false BinPackArguments: false BinPackParameters: false BreakBeforeBinaryOperators: None BreakBeforeBraces: Attach BreakBeforeTernaryOperators: false BreakConstructorInitializersBeforeComma: true ColumnLimit: 0 CommentPragmas: '^ IWYU pragma:' ConstructorInitializerAllOnOneLineOrOnePerLine: true ConstructorInitializerIndentWidth: 2 ContinuationIndentWidth: 2 Cpp11BracedListStyle: false DerivePointerAlignment: false DisableFormat: false ExperimentalAutoDetectBinPacking: false ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] IndentCaseLabels: true IndentWidth: 2 IndentWrappedFunctionNames: false KeepEmptyLinesAtTheStartOfBlocks: true MacroBlockBegin: '' MacroBlockEnd: '' MaxEmptyLinesToKeep: 3 NamespaceIndentation: None ObjCBlockIndentWidth: 2 ObjCSpaceAfterProperty: false ObjCSpaceBeforeProtocolList: true PenaltyBreakBeforeFirstCallParameter: 1 PenaltyBreakComment: 60 PenaltyBreakFirstLessLess: 120 PenaltyBreakString: 1000 PenaltyExcessCharacter: 1000000 PenaltyReturnTypeOnItsOwnLine: 200 PointerAlignment: Middle SpaceAfterCStyleCast: false SpaceBeforeAssignmentOperators: true SpaceBeforeParens: ControlStatements SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 1 SpacesInAngles: false SpacesInContainerLiterals: true SpacesInCStyleCastParentheses: false SpacesInParentheses: true SpacesInSquareBrackets: false TabWidth: 2 UseTab: ForIndentation libopenmpt-0.6.1+release.autotools/libopenmpt/0000755000175000017500000000000014175541576016524 500000000000000libopenmpt-0.6.1+release.autotools/libopenmpt/bindings/0000755000175000017500000000000014175541574020317 500000000000000libopenmpt-0.6.1+release.autotools/libopenmpt/bindings/freebasic/0000755000175000017500000000000014175541576022244 500000000000000libopenmpt-0.6.1+release.autotools/libopenmpt/bindings/freebasic/libopenmpt.bi0000644000175000017500000027427414035630602024651 00000000000000/' ' libopenmpt.bi ' ------------- ' Purpose: libopenmpt public interface for FreeBASIC ' Notes : (currently none) ' Authors: Johannes Schultz ' OpenMPT Devs ' The OpenMPT source code is released under the BSD license. Read LICENSE for more details. '/ #Include Once "crt/stdio.bi" #Include Once "file.bi" #Inclib "openmpt" /'* \page libopenmpt_freebasic_overview FreeBASIC API \section libopenmpt_freebasic_error Error Handling - Functions with no return value in the corresponding C++ API return 0 on failure and 1 on success. - Functions that return integer values signal error condition by returning an invalid value (-1 in most cases, 0 in some cases). - All functions that work on an openmpt_module object will call an openmpt_error_func and depending on the value returned by this function log the error code and/xor/or store it inside the openmpt_module object. Stored error codes can be accessed with the openmpt_module_error_get_last() and openmpt_module_error_get_last_message(). Stored errors will not get cleared automatically and should be reset with openmpt_module_error_clear(). - Some functions not directly related to an openmpt_module object take an explicit openmpt_error_func error function callback and a pointer to an int and behave analog to the functions working on an openmpt_module object. \section libopenmpt_freebasic_strings Strings - All strings returned from libopenmpt are encoded in UTF-8. - All strings passed to libopenmpt should also be encoded in UTF-8. Behaviour in case of invalid UTF-8 is unspecified. - libopenmpt does not enforce or expect any particular Unicode normalization form. - Some libopenmpt functions return strings and are provided in two flavours: The raw libopenmpt function (with a trailing underscore) and a FreeBASIC wrapper function that completely takes care of memory handling (recommended). All strings returned from raw libopenmpt functions are dynamically allocated and must be freed with openmpt_free_string(). When using the FreeBASIC wrappers (which is the recommended way), FreeBASIC automatically takes care of this. - All strings passed to libopenmpt are copied. No ownership is assumed or transferred. \section libopenmpt_freebasic_outputformat Output Format libopenmpt supports a wide range of PCM output formats: [8000..192000]/[mono|stereo|quad]/[f32|i16]. Unless you have some very specific requirements demanding a particular aspect of the output format, you should always prefer 48000/stereo/f32 as the libopenmpt PCM format. - Please prefer 48000Hz unless the user explicitly demands something else. Practically all audio equipment and file formats use 48000Hz nowadays. - Practically all module formats are made for stereo output. Mono will not give you any measurable speed improvements and can trivially be obtained from the stereo output anyway. Quad is not expected by almost all modules and even if they do use surround effects, they expect the effects to be mixed to stereo. - Floating point output provides headroom instead of hard clipping if the module is louder than 0dBFs, will give you a better signal-to-noise ratio than int16 output, and avoid the need to apply an additional dithering to the output by libopenmpt. Unless your platform has no floating point unit at all, floating point will thus also be slightly faster. \section libopenmpt_freebasic_threads libopenmpt in multi-threaded environments - libopenmpt is thread-aware. - Individual libopenmpt objects are not thread-safe. - libopenmpt itself does not spawn any user-visible threads but may spawn threads for internal use. - You must ensure to only ever access a particular libopenmpt object from a single thread at a time. - Consecutive accesses can happen from different threads. - Different objects can be accessed concurrently from different threads. \section libopenmpt_freebasic_detailed Detailed documentation \ref libopenmpt_freebasic '/ Extern "C" '* API version of this header file Const OPENMPT_API_VERSION_MAJOR = 0 Const OPENMPT_API_VERSION_MINOR = 3 Const OPENMPT_API_VERSION_PATCH = 0 Const OPENMPT_API_VERSION = (OPENMPT_API_VERSION_MAJOR Shl 24) Or (OPENMPT_API_VERSION_MINOR Shl 16) Or (OPENMPT_API_VERSION_PATCH Shl 0) #Define OPENMPT_API_VERSION_STRING (OPENMPT_API_VERSION_MAJOR & "." & OPENMPT_API_VERSION_MINOR & "." & OPENMPT_API_VERSION_PATCH) /'* \brief Get the libopenmpt version number Returns the libopenmpt version number. \return The value represents (major Shl 24 + minor Shl 16 + patch Shl 0). \remarks libopenmpt < 0.3.0-pre used the following scheme: (major Shl 24 + minor Shl 16 + revision). \remarks Check the HiWord of the return value against OPENMPT_API_VERSION to ensure that the correct library version is loaded. '/ Declare Function openmpt_get_library_version() As ULong /'* \brief Get the core version number Return the OpenMPT core version number. \return The value represents (majormajor << 24 + major << 16 + minor << 8 + minorminor). '/ Declare Function openmpt_get_core_version() As ULong '* Return a verbose library version string from openmpt_get_string(). \deprecated Please use \code "library_version" \endcode directly. #Define OPENMPT_STRING_LIBRARY_VERSION "library_version" '* Return a verbose library features string from openmpt_get_string(). \deprecated Please use \code "library_features" \endcode directly. #Define OPENMPT_STRING_LIBRARY_FEATURES "library_features" '* Return a verbose OpenMPT core version string from openmpt_get_string(). \deprecated Please use \code "core_version" \endcode directly. #Define OPENMPT_STRING_CORE_VERSION "core_version" '* Return information about the current build (e.g. the build date or compiler used) from openmpt_get_string(). \deprecated Please use \code "build" \endcode directly. #Define OPENMPT_STRING_BUILD "build" '* Return all contributors from openmpt_get_string(). \deprecated Please use \code "credits" \endcode directly. #Define OPENMPT_STRING_CREDITS "credits" '* Return contact information about libopenmpt from openmpt_get_string(). \deprecated Please use \code "contact" \endcode directly. #Define OPENMPT_STRING_CONTACT "contact" '* Return the libopenmpt license from openmpt_get_string(). \deprecated Please use \code "license" \endcode directly. #Define OPENMPT_STRING_LICENSE "license" /'* \brief Free a string returned by libopenmpt Frees any string that got returned by libopenmpt. '/ Declare Sub openmpt_free_string(ByVal Str As Const ZString Ptr) /'* \brief Get library related metadata. \param key Key to query. Possible keys are: - "library_version": verbose library version string - "library_version_is_release": "1" if the version is an officially released version - "library_features": verbose library features string - "core_version": verbose OpenMPT core version string - "source_url": original source code URL - "source_date": original source code date - "source_revision": original source code revision - "source_is_modified": "1" if the original source has been modified - "source_has_mixed_revisions": "1" if the original source has been compiled from different various revision - "source_is_package": "1" if the original source has been obtained from a source pacakge instead of source code version control - "build": information about the current build (e.g. the build date or compiler used) - "build_compiler": information about the compiler used to build libopenmpt - "credits": all contributors - "contact": contact information about libopenmpt - "license": the libopenmpt license - "url": libopenmpt website URL - "support_forum_url": libopenmpt support and discussions forum URL - "bugtracker_url": libopenmpt bug and issue tracker URL \return A (possibly multi-line) string containing the queried information. If no information is available, the string is empty. \remarks Use openmpt_get_string to automatically handle the lifetime of the returned pointer. '/ Declare Function openmpt_get_string_ Alias "openmpt_get_string" (ByVal key As Const ZString Ptr) As Const ZString Ptr /'* \brief Get a list of supported file extensions \return The semicolon-separated list of extensions supported by this libopenmpt build. The extensions are returned lower-case without a leading dot. \remarks Use openmpt_get_supported_extensions to automatically handle the lifetime of the returned pointer. '/ Declare Function openmpt_get_supported_extensions() As Const ZString Ptr /'* \brief Query whether a file extension is supported \param extension file extension to query without a leading dot. The case is ignored. \return 1 if the extension is supported by libopenmpt, 0 otherwise. '/ Declare Function openmpt_is_extension_supported(ByVal extension As Const ZString Ptr) As Long '* Seek to the given offset relative to the beginning of the file. Const OPENMPT_STREAM_SEEK_SET = 0 '* Seek to the given offset relative to the current position in the file. Const OPENMPT_STREAM_SEEK_CUR = 1 '* Seek to the given offset relative to the end of the file. Const OPENMPT_STREAM_SEEK_END = 2 /'* \brief Read bytes from stream Read bytes data from stream to dst. \param stream Stream to read data from \param dst Target where to copy data. \param bytes Number of bytes to read. \return Number of bytes actually read and written to dst. \retval 0 End of stream or error. \remarks Short reads are allowed as long as they return at least 1 byte if EOF is not reached. '/ Type openmpt_stream_read_func As Function(ByVal stream As Any Ptr, ByVal dst As Any Ptr, ByVal bytes As UInteger) As UInteger /'* \brief Seek stream position Seek to stream position offset at whence. \param stream Stream to operate on. \param offset Offset to seek to. \param whence OPENMPT_STREAM_SEEK_SET, OPENMPT_STREAM_SEEK_CUR, OPENMPT_STREAM_SEEK_END. See C89 documentation. \return Returns 0 on success. \retval 0 Success. \retval -1 Failure. Position does not get updated. \remarks libopenmpt will not try to seek beyond the file size, thus it is not important whether you allow for virtual positioning after the file end, or return an error in that case. The position equal to the file size needs to be seekable to. '/ Type openmpt_stream_seek_func As Function(ByVal stream As Any Ptr, ByVal offset As LongInt, ByVal whence As Long) As Long /'* \brief Tell stream position Tell position of stream. \param stream Stream to operate on. \return Current position in stream. \retval -1 Failure. '/ Type openmpt_stream_tell_func As Function(ByVal stream As Any Ptr) As LongInt /'* \brief Stream callbacks Stream callbacks used by libopenmpt for stream operations. '/ Type openmpt_stream_callbacks /'* \brief Read callback. \sa openmpt_stream_read_func '/ read_func As openmpt_stream_read_func /'* \brief Seek callback. Seek callback can be NULL if seeking is not supported. \sa openmpt_stream_seek_func '/ seek_func As openmpt_stream_seek_func /'* \brief Tell callback. Tell callback can be NULL if seeking is not supported. \sa openmpt_stream_tell_func '/ tell_func As openmpt_stream_tell_func End Type /'* \brief Logging function \param message UTF-8 encoded log message. \param user User context that was passed to openmpt_module_create2(), openmpt_module_create_from_memory2() or openmpt_could_open_probability2(). '/ Type openmpt_log_func As Sub(ByVal message As Const ZString Ptr, ByVal user As Any Ptr) /'* \brief Default logging function Default logging function that logs anything to stderr. '/ Declare Sub openmpt_log_func_default(ByVal message As Const ZString Ptr, ByVal user As Any Ptr) /'* \brief Silent logging function Silent logging function that throws any log message away. '/ Declare Sub openmpt_log_func_silent(ByVal message As Const ZString Ptr, ByVal user As Any Ptr) '* No error. \since 0.3.0 Const OPENMPT_ERROR_OK = 0 '* Lowest value libopenmpt will use for any of its own error codes. \since 0.3.0 Const OPENMPT_ERROR_BASE = 256 '* Unknown internal error. \since 0.3.0 Const OPENMPT_ERROR_UNKNOWN = OPENMPT_ERROR_BASE + 1 '* Unknown internal C++ exception. \since 0.3.0 Const OPENMPT_ERROR_EXCEPTION = OPENMPT_ERROR_BASE + 11 '* Out of memory. \since 0.3.0 Const OPENMPT_ERROR_OUT_OF_MEMORY = OPENMPT_ERROR_BASE + 21 '* Runtime error. \since 0.3.0 Const OPENMPT_ERROR_RUNTIME = OPENMPT_ERROR_BASE + 30 '* Range error. \since 0.3.0 Const OPENMPT_ERROR_RANGE = OPENMPT_ERROR_BASE + 31 '* Arithmetic overflow. \since 0.3.0 Const OPENMPT_ERROR_OVERFLOW = OPENMPT_ERROR_BASE + 32 '* Arithmetic underflow. \since 0.3.0 Const OPENMPT_ERROR_UNDERFLOW = OPENMPT_ERROR_BASE + 33 '* Logic error. \since 0.3.0 Const OPENMPT_ERROR_LOGIC = OPENMPT_ERROR_BASE + 40 '* Value domain error. \since 0.3.0 Const OPENMPT_ERROR_DOMAIN = OPENMPT_ERROR_BASE + 41 '* Maximum supported size exceeded. \since 0.3.0 Const OPENMPT_ERROR_LENGTH = OPENMPT_ERROR_BASE + 42 '* Argument out of range. \since 0.3.0 Const OPENMPT_ERROR_OUT_OF_RANGE = OPENMPT_ERROR_BASE + 43 '* Invalid argument. \since 0.3.0 Const OPENMPT_ERROR_INVALID_ARGUMENT = OPENMPT_ERROR_BASE + 44 '* General libopenmpt error. \since 0.3.0 Const OPENMPT_ERROR_GENERAL = OPENMPT_ERROR_BASE + 101 '* openmpt_module Ptr is invalid. \since 0.3.0 Const OPENMPT_ERROR_INVALID_MODULE_POINTER = OPENMPT_ERROR_BASE + 102 '* NULL pointer argument. \since 0.3.0 Const OPENMPT_ERROR_ARGUMENT_NULL_POINTER = OPENMPT_ERROR_BASE + 103 /'* \brief Check whether the error is transient Checks whether an error code represents a transient error which may not occur again in a later try if for example memory has been freed up after an out-of-memory error. \param errorcode Error code. \retval 0 Error is not transient. \retval 1 Error is transient. \sa OPENMPT_ERROR_OUT_OF_MEMORY \since 0.3.0 '/ Declare Function openmpt_error_is_transient(ByVal errorcode As Long) As Long /'* \brief Convert error code to text Converts an error code into a text string describing the error. \param errorcode Error code. \return Allocated string describing the error. \retval NULL Not enough memory to allocate the string. \since 0.3.0 \remarks Use openmpt_error_string to automatically handle the lifetime of the returned pointer. '/ Declare Function openmpt_error_string_ Alias "openmpt_error_string" (ByVal errorcode As Long) As Const ZString Ptr '* Do not log or store the error. \since 0.3.0 Const OPENMPT_ERROR_FUNC_RESULT_NONE = 0 '* Log the error. \since 0.3.0 Const OPENMPT_ERROR_FUNC_RESULT_LOG = 1 '* Store the error. \since 0.3.0 Const OPENMPT_ERROR_FUNC_RESULT_STORE = 2 '* Log and store the error. \since 0.3.0 Const OPENMPT_ERROR_FUNC_RESULT_DEFAULT = OPENMPT_ERROR_FUNC_RESULT_LOG Or OPENMPT_ERROR_FUNC_RESULT_STORE /'* \brief Error function \param errorcode Error code. \param user User context that was passed to openmpt_module_create2(), openmpt_module_create_from_memory2() or openmpt_could_open_probability2(). \return Mask of OPENMPT_ERROR_FUNC_RESULT_LOG and OPENMPT_ERROR_FUNC_RESULT_STORE. \retval OPENMPT_ERROR_FUNC_RESULT_NONE Do not log or store the error. \retval OPENMPT_ERROR_FUNC_RESULT_LOG Log the error. \retval OPENMPT_ERROR_FUNC_RESULT_STORE Store the error. \retval OPENMPT_ERROR_FUNC_RESULT_DEFAULT Log and store the error. \sa OPENMPT_ERROR_FUNC_RESULT_NONE \sa OPENMPT_ERROR_FUNC_RESULT_LOG \sa OPENMPT_ERROR_FUNC_RESULT_STORE \sa OPENMPT_ERROR_FUNC_RESULT_DEFAULT \sa openmpt_error_func_default \sa openmpt_error_func_log \sa openmpt_error_func_store \sa openmpt_error_func_ignore \sa openmpt_error_func_errno \since 0.3.0 '/ Type openmpt_error_func As Function(ByVal errorcode As Long, ByVal user As Any Ptr) As Long /'* \brief Default error function Causes all errors to be logged and stored. \param errorcode Error code. \param user Ignored. \retval OPENMPT_ERROR_FUNC_RESULT_DEFAULT Always. \since 0.3.0 '/ Declare Function openmpt_error_func_default(ByVal errorcode As Long, ByVal User As Any Ptr) As Long /'* \brief Log error function Causes all errors to be logged. \param errorcode Error code. \param user Ignored. \retval OPENMPT_ERROR_FUNC_RESULT_LOG Always. \since 0.3.0 '/ Declare Function openmpt_error_func_log(ByVal errorcode As Long, ByVal user As Any Ptr) As Long /'* \brief Store error function Causes all errors to be stored. \param errorcode Error code. \param user Ignored. \retval OPENMPT_ERROR_FUNC_RESULT_STORE Always. \since 0.3.0 '/ Declare Function openmpt_error_func_store(ByVal errorcode As Long, ByVal user As Any Ptr) As Long /'* \brief Ignore error function Causes all errors to be neither logged nor stored. \param errorcode Error code. \param user Ignored. \retval OPENMPT_ERROR_FUNC_RESULT_NONE Always. \since 0.3.0 '/ Declare Function openmpt_error_func_ignore(ByVal errorcode As Long, ByVal user As Any Ptr) As Long /'* \brief Errno error function Causes all errors to be stored in the pointer passed in as user. \param errorcode Error code. \param user Pointer to an int as generated by openmpt_error_func_errno_userdata. \retval OPENMPT_ERROR_FUNC_RESULT_NONE user is not NULL. \retval OPENMPT_ERROR_FUNC_RESULT_DEFAULT user is NULL. \since 0.3.0 '/ Declare Function openmpt_error_func_errno(ByVal errorcode As Long, ByVal user As Any Ptr) As Long /'* \brief User pointer for openmpt_error_func_errno Provides a suitable user pointer argument for openmpt_error_func_errno. \param errorcode Pointer to an integer value to be used as output by openmpt_error_func_errno. \retval Cast(Any Ptr, errorcode). \since 0.3.0 '/ Declare Function openmpt_error_func_errno_userdata(ByVal errorcode As Long Ptr) As Any Ptr /'* \brief Roughly scan the input stream to find out whether libopenmpt might be able to open it \param stream_callbacks Input stream callback operations. \param stream Input stream to scan. \param effort Effort to make when validating stream. Effort 0.0 does not even look at stream at all and effort 1.0 completely loads the file from stream. A lower effort requires less data to be loaded but only gives a rough estimate answer. Use an effort of 0.25 to only verify the header data of the module file. \param logfunc Logging function where warning and errors are written. May be NULL. \param user Logging function user context. \return Probability between 0.0 and 1.0. \remarks openmpt_could_open_probability() can return any value between 0.0 and 1.0. Only 0.0 and 1.0 are definitive answers, all values in between are just estimates. In general, any return value >0.0 means that you should try loading the file, and any value below 1.0 means that loading may fail. If you want a threshold above which you can be reasonably sure that libopenmpt will be able to load the file, use >=0.5. If you see the need for a threshold below which you could reasonably outright reject a file, use <0.25 (Note: Such a threshold for rejecting on the lower end is not recommended, but may be required for better integration into some other framework's probe scoring.). \remarks openmpt_could_open_probability() expects the complete file data to be eventually available to it, even if it is asked to just parse the header. Verification will be unreliable (both false positives and false negatives), if you pretend that the file is just some few bytes of initial data threshold in size. In order to really just access the first bytes of a file, check in your callback functions whether data or seeking is requested beyond your initial data threshold, and in that case, return an error. openmpt_could_open_probability() will treat this as any other I/O error and return 0.0. You must not expect the correct result in this case. You instead must remember that it asked for more data than you currently want to provide to it and treat this situation as if openmpt_could_open_probability() returned 0.5. \sa openmpt_stream_callbacks \deprecated Please use openmpt_module_could_open_probability2(). \since 0.3.0 '/ Declare Function openmpt_could_open_probability(ByVal stream_callbacks As openmpt_stream_callbacks, ByVal stream As Any Ptr, ByVal effort As Double, ByVal logfunc As openmpt_log_func, ByVal user As Any Ptr) As Double /'* \brief Roughly scan the input stream to find out whether libopenmpt might be able to open it \param stream_callbacks Input stream callback operations. \param stream Input stream to scan. \param effort Effort to make when validating stream. Effort 0.0 does not even look at stream at all and effort 1.0 completely loads the file from stream. A lower effort requires less data to be loaded but only gives a rough estimate answer. Use an effort of 0.25 to only verify the header data of the module file. \param logfunc Logging function where warning and errors are written. May be NULL. \param loguser Logging function user context. \param errfunc Error function to define error behaviour. May be NULL. \param erruser Error function user context. \param errorcode Pointer to an integer where an error may get stored. May be NULL. \param error_message Pointer to a string pointer where an error message may get stored. May be NULL. \return Probability between 0.0 and 1.0. \remarks openmpt_probe_file_header() or openmpt_probe_file_header_without_filesize() provide a simpler and faster interface that fits almost all use cases better. It is recommended to use openmpt_probe_file_header() or openmpt_probe_file_header_without_filesize() instead of openmpt_could_open_probability(). \remarks openmpt_could_open_probability2() can return any value between 0.0 and 1.0. Only 0.0 and 1.0 are definitive answers, all values in between are just estimates. In general, any return value >0.0 means that you should try loading the file, and any value below 1.0 means that loading may fail. If you want a threshold above which you can be reasonably sure that libopenmpt will be able to load the file, use >=0.5. If you see the need for a threshold below which you could reasonably outright reject a file, use <0.25 (Note: Such a threshold for rejecting on the lower end is not recommended, but may be required for better integration into some other framework's probe scoring.). \remarks openmpt_could_open_probability2() expects the complete file data to be eventually available to it, even if it is asked to just parse the header. Verification will be unreliable (both false positives and false negatives), if you pretend that the file is just some few bytes of initial data threshold in size. In order to really just access the first bytes of a file, check in your callback functions whether data or seeking is requested beyond your initial data threshold, and in that case, return an error. openmpt_could_open_probability() will treat this as any other I/O error and return 0.0. You must not expect the correct result in this case. You instead must remember that it asked for more data than you currently want to provide to it and treat this situation as if openmpt_could_open_probability() returned 0.5. \sa openmpt_stream_callbacks \sa openmpt_probe_file_header \sa openmpt_probe_file_header_without_filesize \since 0.3.0 '/ Declare Function openmpt_could_open_probability2(ByVal stream_callbacks As openmpt_stream_callbacks, ByVal stream As Any Ptr, ByVal effort As Double, ByVal logfunc As openmpt_log_func, ByVal loguser As Any Ptr, ByVal errfunc As openmpt_error_func, ByVal erruser As Any Ptr, ByVal errorcode As Long Ptr, ByVal error_message As Const ZString Ptr Ptr) As Double /'* \brief Get recommended header size for successfull format probing \sa openmpt_probe_file_header() \sa openmpt_probe_file_header_without_filesize() \since 0.3.0 '/ Declare Function openmpt_probe_file_header_get_recommended_size() As UInteger '* Probe for module formats in openmpt_probe_file_header() or openmpt_probe_file_header_without_filesize(). \since 0.3.0 Const OPENMPT_PROBE_FILE_HEADER_FLAGS_MODULES = 1 '* Probe for module-specific container formats in openmpt_probe_file_header() or openmpt_probe_file_header_without_filesize(). \since 0.3.0 Const OPENMPT_PROBE_FILE_HEADER_FLAGS_CONTAINERS = 2 '* Probe for the default set of formats in openmpt_probe_file_header() or openmpt_probe_file_header_without_filesize(). \since 0.3.0 Const OPENMPT_PROBE_FILE_HEADER_FLAGS_DEFAULT = OPENMPT_PROBE_FILE_HEADER_FLAGS_MODULES or OPENMPT_PROBE_FILE_HEADER_FLAGS_CONTAINERS '* Probe for no formats in openmpt_probe_file_header() or openmpt_probe_file_header_without_filesize(). \since 0.3.0 Const OPENMPT_PROBE_FILE_HEADER_FLAGS_NONE = 0 '* Possible return values fo openmpt_probe_file_header() and openmpt_probe_file_header_without_filesize(). \since 0.3.0 Const OPENMPT_PROBE_FILE_HEADER_RESULT_SUCCESS = 1 '* Possible return values fo openmpt_probe_file_header() and openmpt_probe_file_header_without_filesize(). \since 0.3.0 Const OPENMPT_PROBE_FILE_HEADER_RESULT_FAILURE = 0 '* Possible return values fo openmpt_probe_file_header() and openmpt_probe_file_header_without_filesize(). \since 0.3.0 Const OPENMPT_PROBE_FILE_HEADER_RESULT_WANTMOREDATA = -1 '* Possible return values fo openmpt_probe_file_header() and openmpt_probe_file_header_without_filesize(). \since 0.3.0 Const OPENMPT_PROBE_FILE_HEADER_RESULT_ERROR = -255 /'* \brief Probe the provided bytes from the beginning of a file for supported file format headers to find out whether libopenmpt might be able to open it \param flags Bit mask of OPENMPT_PROBE_FILE_HEADER_FLAGS_MODULES and OPENMPT_PROBE_FILE_HEADER_FLAGS_CONTAINERS, or OPENMPT_PROBE_FILE_HEADER_FLAGS_DEFAULT. \param data Beginning of the file data. \param size Size of the beginning of the file data. \param filesize Full size of the file data on disk. \param logfunc Logging function where warning and errors are written. May be NULL. \param loguser Logging function user context. Used to pass any user-defined data associated with this module to the logging function. \param errfunc Error function to define error behaviour. May be NULL. \param erruser Error function user context. Used to pass any user-defined data associated with this module to the logging function. \param error Pointer to an integer where an error may get stored. May be NULL. \param error_message Pointer to a string pointer where an error message may get stored. May be NULL. \remarks It is recommended to provide openmpt_probe_file_header_get_recommended_size() bytes of data for data and size. If the file is smaller, only provide the filesize amount and set size and filesize to the file's size. \remarks openmpt_could_open_probability2() provides a more elaborate interface that might be required for special use cases. It is recommended to use openmpt_probe_file_header() though, if possible. \retval OPENMPT_PROBE_FILE_HEADER_RESULT_SUCCESS The file will most likely be supported by libopenmpt. \retval OPENMPT_PROBE_FILE_HEADER_RESULT_FAILURE The file is not supported by libopenmpt. \retval OPENMPT_PROBE_FILE_HEADER_RESULT_WANTMOREDATA An answer could not be determined with the amount of data provided. \retval OPENMPT_PROBE_FILE_HEADER_RESULT_ERROR An internal error occurred. \sa openmpt_probe_file_header_get_recommended_size() \sa openmpt_probe_file_header_without_filesize() \sa openmpt_probe_file_header_from_stream() \sa openmpt_could_open_probability2() \since 0.3.0 '/ Declare Function openmpt_probe_file_header(ByVal flags As ULongInt, ByVal Data As Const Any Ptr, ByVal size As UInteger, ByVal filesize As ULongInt, ByVal logfunc As openmpt_log_func, ByVal loguser As Any Ptr, ByVal errfunc As openmpt_error_func, ByVal erruser As Any Ptr, ByVal Error As Long Ptr, ByVal error_message As Const ZString Ptr Ptr) As Long /'* \brief Probe the provided bytes from the beginning of a file for supported file format headers to find out whether libopenmpt might be able to open it \param flags Bit mask of OPENMPT_PROBE_FILE_HEADER_FLAGS_MODULES and OPENMPT_PROBE_FILE_HEADER_FLAGS_CONTAINERS, or OPENMPT_PROBE_FILE_HEADER_FLAGS_DEFAULT. \param data Beginning of the file data. \param size Size of the beginning of the file data. \param logfunc Logging function where warning and errors are written. May be NULL. \param loguser Logging function user context. Used to pass any user-defined data associated with this module to the logging function. \param errfunc Error function to define error behaviour. May be NULL. \param erruser Error function user context. Used to pass any user-defined data associated with this module to the logging function. \param error Pointer to an integer where an error may get stored. May be NULL. \param error_message Pointer to a string pointer where an error message may get stored. May be NULL. \remarks It is recommended to use openmpt_probe_file_header() and provide the acutal file's size as a parameter if at all possible. libopenmpt can provide more accurate answers if the filesize is known. \remarks It is recommended to provide openmpt_probe_file_header_get_recommended_size() bytes of data for data and size. If the file is smaller, only provide the filesize amount and set size to the file's size. \remarks openmpt_could_open_probability2() provides a more elaborate interface that might be required for special use cases. It is recommended to use openmpt_probe_file_header() though, if possible. \retval OPENMPT_PROBE_FILE_HEADER_RESULT_SUCCESS The file will most likely be supported by libopenmpt. \retval OPENMPT_PROBE_FILE_HEADER_RESULT_FAILURE The file is not supported by libopenmpt. \retval OPENMPT_PROBE_FILE_HEADER_RESULT_WANTMOREDATA An answer could not be determined with the amount of data provided. \retval OPENMPT_PROBE_FILE_HEADER_RESULT_ERROR An internal error occurred. \sa openmpt_probe_file_header_get_recommended_size() \sa openmpt_probe_file_header() \sa openmpt_probe_file_header_from_stream() \sa openmpt_could_open_probability2() \since 0.3.0 '/ Declare Function openmpt_probe_file_header_without_filesize(ByVal flags As ULongInt, ByVal Data As Const Any Ptr, ByVal size As UInteger, ByVal logfunc As openmpt_log_func, ByVal loguser As Any Ptr, ByVal errfunc As openmpt_error_func, ByVal erruser As Any Ptr, ByVal Error As Long Ptr, ByVal error_message As Const ZString Ptr Ptr) As Long /'* \brief Probe the provided bytes from the beginning of a file for supported file format headers to find out whether libopenmpt might be able to open it \param flags Bit mask of OPENMPT_PROBE_FILE_HEADER_FLAGS_MODULES and OPENMPT_PROBE_FILE_HEADER_FLAGS_CONTAINERS, or OPENMPT_PROBE_FILE_HEADER_FLAGS_DEFAULT. \param stream_callbacks Input stream callback operations. \param stream Input stream to scan. \param logfunc Logging function where warning and errors are written. May be NULL. \param loguser Logging function user context. Used to pass any user-defined data associated with this module to the logging function. \param errfunc Error function to define error behaviour. May be NULL. \param erruser Error function user context. Used to pass any user-defined data associated with this module to the logging function. \param error Pointer to an integer where an error may get stored. May be NULL. \param error_message Pointer to a string pointer where an error message may get stored. May be NULL. \remarks The stream is left in an unspecified state when this function returns. \remarks It is recommended to provide openmpt_probe_file_header_get_recommended_size() bytes of data for data and size. If the file is smaller, only provide the filesize amount and set size and filesize to the file's size. \remarks openmpt_could_open_probability2() provides a more elaborate interface that might be required for special use cases. It is recommended to use openmpt_probe_file_header() though, if possible. \retval OPENMPT_PROBE_FILE_HEADER_RESULT_SUCCESS The file will most likely be supported by libopenmpt. \retval OPENMPT_PROBE_FILE_HEADER_RESULT_FAILURE The file is not supported by libopenmpt. \retval OPENMPT_PROBE_FILE_HEADER_RESULT_WANTMOREDATA An answer could not be determined with the amount of data provided. \retval OPENMPT_PROBE_FILE_HEADER_RESULT_ERROR An internal error occurred. \sa openmpt_probe_file_header_get_recommended_size() \sa openmpt_probe_file_header() \sa openmpt_probe_file_header_without_filesize() \sa openmpt_could_open_probability2() \since 0.3.0 '/ Declare Function openmpt_probe_file_header_from_stream(ByVal flags As ULongInt, ByVal stream_callbacks As openmpt_stream_callbacks, ByVal stream As Any Ptr, ByVal logfunc As openmpt_log_func, ByVal loguser As Any Ptr, ByVal errfunc As openmpt_error_func, ByVal erruser As Any Ptr, ByVal Error As Long Ptr, ByVal error_message As Const ZString Ptr Ptr) As Long '* \brief Opaque type representing a libopenmpt module Type openmpt_module opaque As Any Ptr End Type Type openmpt_module_initial_ctl ctl As Const ZString Ptr value As Const ZString Ptr End Type /'* \brief Construct an openmpt_module \param stream_callbacks Input stream callback operations. \param stream Input stream to load the module from. \param logfunc Logging function where warning and errors are written. The logging function may be called throughout the lifetime of openmpt_module. \param user User-defined data associated with this module. This value will be passed to the logging callback function (logfunc) \param ctls A map of initial ctl values. See openmpt_module_get_ctls(). \return A pointer to the constructed openmpt_module, or NULL on failure. \remarks The input data can be discarded after an openmpt_module has been constructed successfully. \sa openmpt_stream_callbacks '/ Declare Function openmpt_module_create(ByVal stream_callbacks As openmpt_stream_callbacks, ByVal stream As Any Ptr, ByVal logfunc As openmpt_log_func = 0, ByVal user As Any Ptr = 0, ByVal ctls As Const openmpt_module_initial_ctl Ptr = 0) As openmpt_module Ptr /'* \brief Construct an openmpt_module \param stream_callbacks Input stream callback operations. \param stream Input stream to load the module from. \param logfunc Logging function where warning and errors are written. The logging function may be called throughout the lifetime of openmpt_module. May be NULL. \param loguser User-defined data associated with this module. This value will be passed to the logging callback function (logfunc) \param errfunc Error function to define error behaviour. May be NULL. \param erruser Error function user context. \param errorcode Pointer to an integer where an error may get stored. May be NULL. \param error_message Pointer to a string pointer where an error message may get stored. May be NULL. \param ctls A map of initial ctl values. See openmpt_module_get_ctls(). \return A pointer to the constructed openmpt_module, or NULL on failure. \remarks The input data can be discarded after an openmpt_module has been constructed successfully. \sa openmpt_stream_callbacks \since 0.3.0 '/ Declare Function openmpt_module_create2(ByVal stream_callbacks As openmpt_stream_callbacks, ByVal stream As Any Ptr, ByVal logfunc As openmpt_log_func = 0, ByVal loguser As Any Ptr = 0, ByVal errfunc As openmpt_error_func = 0, ByVal erruser As Any Ptr = 0, ByVal errorcode As Long Ptr = 0, ByVal error_message As Const ZString Ptr Ptr = 0, ByVal ctls As Const openmpt_module_initial_ctl Ptr = 0) As openmpt_module Ptr /'* \brief Construct an openmpt_module \param filedata Data to load the module from. \param filesize Amount of data available. \param logfunc Logging function where warning and errors are written. The logging function may be called throughout the lifetime of openmpt_module. \param user User-defined data associated with this module. This value will be passed to the logging callback function (logfunc) \param ctls A map of initial ctl values. See openmpt_module_get_ctls(). \return A pointer to the constructed openmpt_module, or NULL on failure. \remarks The input data can be discarded after an openmpt_module has been constructed successfully. '/ Declare Function openmpt_module_create_from_memory(ByVal filedata As Const Any Ptr, ByVal filesize As UInteger, ByVal logfunc As openmpt_log_func = 0, ByVal user As Any Ptr = 0, ByVal ctls As Const openmpt_module_initial_ctl Ptr = 0) As openmpt_module Ptr /'* \brief Construct an openmpt_module \param filedata Data to load the module from. \param filesize Amount of data available. \param logfunc Logging function where warning and errors are written. The logging function may be called throughout the lifetime of openmpt_module. \param loguser User-defined data associated with this module. This value will be passed to the logging callback function (logfunc) \param errfunc Error function to define error behaviour. May be NULL. \param erruser Error function user context. \param errorcode Pointer to an integer where an error may get stored. May be NULL. \param error_message Pointer to a string pointer where an error message may get stored. May be NULL. \param ctls A map of initial ctl values. See openmpt_module_get_ctls(). \return A pointer to the constructed openmpt_module, or NULL on failure. \remarks The input data can be discarded after an openmpt_module has been constructed successfully. \since 0.3.0 '/ Declare Function openmpt_module_create_from_memory2(ByVal filedata As Const Any Ptr, ByVal filesize As UInteger, ByVal logfunc As openmpt_log_func, ByVal loguser As Any Ptr, ByVal errfunc As openmpt_error_func, ByVal erruser As Any Ptr, ByVal errorcode As Long Ptr, ByVal error_message As Const ZString Ptr Ptr, ByVal ctls As Const openmpt_module_initial_ctl Ptr) As openmpt_module Ptr /'* \brief Unload a previously created openmpt_module from memory. \param module The module to unload. '/ Declare Sub openmpt_module_destroy(ByVal module As openmpt_module Ptr) /'* \brief Set logging function. Set the logging function of an already constructed openmpt_module. \param module The module handle to work on. \param logfunc Logging function where warning and errors are written. The logging function may be called throughout the lifetime of openmpt_module. \param loguser User-defined data associated with this module. This value will be passed to the logging callback function (logfunc) \since 0.3.0 '/ Declare Sub openmpt_module_set_log_func(ByVal module As openmpt_module Ptr, ByVal logfunc As openmpt_log_func, ByVal loguser As Any Ptr) /'* \brief Set error function. Set the error function of an already constructed openmpt_module. \param module The module handle to work on. \param errfunc Error function to define error behaviour. May be NULL. \param erruser Error function user context. \since 0.3.0 '/ Declare Sub openmpt_module_set_error_func(ByVal module As openmpt_module Ptr, ByVal errfunc As openmpt_error_func, ByVal erruser As Any Ptr) /'* \brief Get last error. Return the error currently stored in an openmpt_module. The stored error is not cleared. \param module The module handle to work on. \return The error currently stored. \sa openmpt_module_error_get_last_message \sa openmpt_module_error_set_last \sa openmpt_module_error_clear \since 0.3.0 '/ Declare Function openmpt_module_error_get_last(ByVal module As openmpt_module Ptr) As Long /'* \brief Get last error message. Return the error message currently stored in an openmpt_module. The stored error is not cleared. \param module The module handle to work on. \return The error message currently stored. \sa openmpt_module_error_set_last \sa openmpt_module_error_clear \since 0.3.0 \remarks Use openmpt_module_error_get_last_message to automatically handle the lifetime of the returned pointer. '/ Declare Function openmpt_module_error_get_last_message_ Alias "openmpt_module_error_get_last_message" (ByVal module As openmpt_module Ptr) As Const ZString Ptr /'* \brief Set last error. Set the error currently stored in an openmpt_module. \param module The module handle to work on. \param errorcode Error to be stored. \sa openmpt_module_error_get_last \sa openmpt_module_error_clear \since 0.3.0 '/ Declare Sub openmpt_module_error_set_last(ByVal module As openmpt_module Ptr, ByVal errorcode As Long) /'* \brief Clear last error. Set the error currently stored in an openmpt_module to OPPENMPT_ERROR_OK. \param module The module handle to work on. \sa openmpt_module_error_get_last \sa openmpt_module_error_set_last \since 0.3.0 '/ Declare Sub openmpt_module_error_clear(ByVal module As openmpt_module Ptr) /'* \defgroup openmpt_module_render_param Render param indices \brief Parameter index to use with openmpt_module_get_render_param() and openmpt_module_set_render_param() @{ '/ /'* \brief Master Gain The related value represents a relative gain in milliBel.\n The default value is 0.\n The supported value range is unlimited.\n '/ Const OPENMPT_MODULE_RENDER_MASTERGAIN_MILLIBEL = 1 /'* \brief Stereo Separation The related value represents the stereo separation generated by the libopenmpt mixer in percent.\n The default value is 100.\n The supported value range is [0,200].\n '/ Const OPENMPT_MODULE_RENDER_STEREOSEPARATION_PERCENT = 2 /'* \brief Interpolation Filter The related value represents the interpolation filter length used by the libopenmpt mixer.\n The default value is 0, which indicates a recommended default value.\n The supported value range is [0,inf). Values greater than the implementation limit are clamped to the maximum supported value.\n Currently supported values: - 0: internal default - 1: no interpolation (zero order hold) - 2: linear interpolation - 4: cubic interpolation - 8: windowed sinc with 8 taps '/ Const OPENMPT_MODULE_RENDER_INTERPOLATIONFILTER_LENGTH = 3 /'* \brief Volume Ramping Strength The related value represents the amount of volume ramping done by the libopenmpt mixer.\n The default value is -1, which indicates a recommended default value.\n The meaningful value range is [-1..10].\n A value of 0 completely disables volume ramping. This might cause clicks in sound output.\n Higher values imply slower/softer volume ramps. '/ Const OPENMPT_MODULE_RENDER_VOLUMERAMPING_STRENGTH = 4 '* @} /'* \defgroup openmpt_module_command_index Pattern cell indices \brief Parameter index to use with openmpt_module_get_pattern_row_channel_command(), openmpt_module_format_pattern_row_channel_command() and openmpt_module_highlight_pattern_row_channel_command() @{ '/ Const OPENMPT_MODULE_COMMAND_NOTE = 0 Const OPENMPT_MODULE_COMMAND_INSTRUMENT = 1 Const OPENMPT_MODULE_COMMAND_VOLUMEEFFECT = 2 Const OPENMPT_MODULE_COMMAND_EFFECT = 3 Const OPENMPT_MODULE_COMMAND_VOLUME = 4 Const OPENMPT_MODULE_COMMAND_PARAMETER = 5 '* @} /'* \brief Select a sub-song from a multi-song module \param module The module handle to work on. \param subsong Index of the sub-song. -1 plays all sub-songs consecutively. \return 1 on success, 0 on failure. \sa openmpt_module_get_num_subsongs, openmpt_module_get_selected_subsong, openmpt_module_get_subsong_name \remarks Whether subsong -1 (all subsongs consecutively), subsong 0 or some other subsong is selected by default, is an implementation detail and subject to change. If you do not want to care about subsongs, it is recommended to just not call openmpt_module_select_subsong() at all. '/ Declare Function openmpt_module_select_subsong(ByVal module As openmpt_module Ptr, ByVal subsong As Long) As Long /'* \brief Get currently selected sub-song from a multi-song module \param module The module handle to work on. \return Currently selected sub-song. -1 for all subsongs consecutively, 0 or greater for the current sub-song index. \sa openmpt_module_get_num_subsongs, openmpt_module_select_subsong, openmpt_module_get_subsong_name \since 0.3.0 '/ Declare Function openmpt_module_get_selected_subsong(ByVal module As openmpt_module Ptr) As Long /'* \brief Set Repeat Count \param module The module handle to work on. \param repeat_count Repeat Count - -1: repeat forever - 0: play once, repeat zero times (the default) - n>0: play once and repeat n times after that \return 1 on success, 0 on failure. \sa openmpt_module_get_repeat_count '/ Declare Function openmpt_module_set_repeat_count(ByVal module As openmpt_module Ptr, ByVal repeat_count As Long) As Long /'* \brief Get Repeat Count \param module The module handle to work on. \return Repeat Count - -1: repeat forever - 0: play once, repeat zero times (the default) - n>0: play once and repeat n times after that \sa openmpt_module_set_repeat_count '/ Declare Function openmpt_module_get_repeat_count(ByVal module As openmpt_module Ptr) As Long /'* \brief approximate song duration \param module The module handle to work on. \return Approximate duration of current sub-song in seconds. \remarks The function may return infinity if the pattern data is too complex to evaluate. '/ Declare Function openmpt_module_get_duration_seconds(ByVal module As openmpt_module Ptr) As Double /'* \brief Set approximate current song position \param module The module handle to work on. \param seconds Seconds to seek to. If seconds is out of range, the position gets set to song start or end respectively. \return Approximate new song position in seconds. \sa openmpt_module_get_position_seconds '/ Declare Function openmpt_module_set_position_seconds(ByVal module As openmpt_module Ptr, ByVal seconds As Double) As Double /'* \brief Get current song position \param module The module handle to work on. \return Current song position in seconds. \sa openmpt_module_set_position_seconds '/ Declare Function openmpt_module_get_position_seconds(ByVal module As openmpt_module Ptr) As Double /'* \brief Set approximate current song position If order or row are out of range, to position is not modified and the current position is returned. \param module The module handle to work on. \param order Pattern order number to seek to. \param row Pattern row number to seek to. \return Approximate new song position in seconds. \sa openmpt_module_set_position_seconds \sa openmpt_module_get_position_seconds '/ Declare Function openmpt_module_set_position_order_row(ByVal module As openmpt_module Ptr, ByVal order As Long, ByVal row As Long) As Double /'* \brief Get render parameter \param module The module handle to work on. \param param Parameter to query. See \ref openmpt_module_render_param \param value Pointer to the variable that receives the current value of the parameter. \return 1 on success, 0 on failure (invalid param or value is NULL). \sa OPENMPT_MODULE_RENDER_MASTERGAIN_MILLIBEL \sa OPENMPT_MODULE_RENDER_STEREOSEPARATION_PERCENT \sa OPENMPT_MODULE_RENDER_INTERPOLATIONFILTER_LENGTH \sa OPENMPT_MODULE_RENDER_VOLUMERAMPING_STRENGTH \sa openmpt_module_set_render_param '/ Declare Function openmpt_module_get_render_param(ByVal module As openmpt_module Ptr, ByVal param As Long, ByVal value As Long Ptr) As Long /'* \brief Set render parameter \param module The module handle to work on. \param param Parameter to set. See \ref openmpt_module_render_param \param value The value to set param to. \return 1 on success, 0 on failure (invalid param). \sa OPENMPT_MODULE_RENDER_MASTERGAIN_MILLIBEL \sa OPENMPT_MODULE_RENDER_STEREOSEPARATION_PERCENT \sa OPENMPT_MODULE_RENDER_INTERPOLATIONFILTER_LENGTH \sa OPENMPT_MODULE_RENDER_VOLUMERAMPING_STRENGTH \sa openmpt_module_get_render_param '/ Declare Function openmpt_module_set_render_param(ByVal module As openmpt_module Ptr, ByVal param As Long, ByVal value As Long) As Long '*@{ /'* \brief Render audio data \param module The module handle to work on. \param samplerate Sample rate to render output. Should be in [8000,192000], but this is not enforced. \param count Number of audio frames to render per channel. \param mono Pointer to a buffer of at least count elements that receives the mono/center output. \return The number of frames actually rendered. \retval 0 The end of song has been reached. \remarks The output buffers are only written to up to the returned number of elements. \remarks You can freely switch between any of the "openmpt_module_read*" variants if you see a need to do so. libopenmpt tries to introduce as little switching annoyances as possible. Normally, you would only use a single one of these functions for rendering a particular module. \remarks It is recommended to use the floating point API because of the greater dynamic range and no implied clipping. \sa \ref libopenmpt_freebasic_outputformat '/ Declare Function openmpt_module_read_mono(ByVal module As openmpt_module Ptr, ByVal samplerate As Long, ByVal count As UInteger, ByVal mono As Short Ptr) As UInteger /'* \brief Render audio data \param module The module handle to work on. \param samplerate Sample rate to render output. Should be in [8000,192000], but this is not enforced. \param count Number of audio frames to render per channel. \param left Pointer to a buffer of at least count elements that receives the left output. \param right Pointer to a buffer of at least count elements that receives the right output. \return The number of frames actually rendered. \retval 0 The end of song has been reached. \remarks The output buffers are only written to up to the returned number of elements. \remarks You can freely switch between any of the "openmpt_module_read*" variants if you see a need to do so. libopenmpt tries to introduce as little switching annoyances as possible. Normally, you would only use a single one of these functions for rendering a particular module. \remarks It is recommended to use the floating point API because of the greater dynamic range and no implied clipping. \sa \ref libopenmpt_freebasic_outputformat '/ Declare Function openmpt_module_read_stereo(ByVal module As openmpt_module Ptr, ByVal samplerate As Long, ByVal count As UInteger, ByVal Left As Short Ptr, ByVal Right As Short Ptr) As UInteger /'* \brief Render audio data \param module The module handle to work on. \param samplerate Sample rate to render output. Should be in [8000,192000], but this is not enforced. \param count Number of audio frames to render per channel. \param left Pointer to a buffer of at least count elements that receives the left output. \param right Pointer to a buffer of at least count elements that receives the right output. \param rear_left Pointer to a buffer of at least count elements that receives the rear left output. \param rear_right Pointer to a buffer of at least count elements that receives the rear right output. \return The number of frames actually rendered. \retval 0 The end of song has been reached. \remarks The output buffers are only written to up to the returned number of elements. \remarks You can freely switch between any of the "openmpt_module_read*" variants if you see a need to do so. libopenmpt tries to introduce as little switching annoyances as possible. Normally, you would only use a single one of these functions for rendering a particular module. \remarks It is recommended to use the floating point API because of the greater dynamic range and no implied clipping. \sa \ref libopenmpt_freebasic_outputformat '/ Declare Function openmpt_module_read_quad(ByVal module As openmpt_module Ptr, ByVal samplerate As Long, ByVal count As UInteger, ByVal Left As Short Ptr, ByVal Right As Short Ptr, ByVal rear_left As Short Ptr, ByVal rear_right As Short Ptr) As UInteger /'* \brief Render audio data \param module The module handle to work on. \param samplerate Sample rate to render output. Should be in [8000,192000], but this is not enforced. \param count Number of audio frames to render per channel. \param mono Pointer to a buffer of at least count elements that receives the mono/center output. \return The number of frames actually rendered. \retval 0 The end of song has been reached. \remarks The output buffers are only written to up to the returned number of elements. \remarks You can freely switch between any of the "openmpt_module_read*" variants if you see a need to do so. libopenmpt tries to introduce as little switching annoyances as possible. Normally, you would only use a single one of these functions for rendering a particular module. \remarks Floating point samples are in the [-1.0..1.0] nominal range. They are not clipped to that range though and thus might overshoot. \sa \ref libopenmpt_freebasic_outputformat '/ Declare Function openmpt_module_read_float_mono(ByVal module As openmpt_module Ptr, ByVal samplerate As Long, ByVal count As UInteger, ByVal mono As Single Ptr) As UInteger /'* \brief Render audio data \param module The module handle to work on. \param samplerate Sample rate to render output. Should be in [8000,192000], but this is not enforced. \param count Number of audio frames to render per channel. \param left Pointer to a buffer of at least count elements that receives the left output. \param right Pointer to a buffer of at least count elements that receives the right output. \return The number of frames actually rendered. \retval 0 The end of song has been reached. \remarks The output buffers are only written to up to the returned number of elements. \remarks You can freely switch between any of the "openmpt_module_read*" variants if you see a need to do so. libopenmpt tries to introduce as little switching annoyances as possible. Normally, you would only use a single one of these functions for rendering a particular module. \remarks Floating point samples are in the [-1.0..1.0] nominal range. They are not clipped to that range though and thus might overshoot. \sa \ref libopenmpt_freebasic_outputformat '/ Declare Function openmpt_module_read_float_stereo(ByVal module As openmpt_module Ptr, ByVal samplerate As Long, ByVal count As UInteger, ByVal Left As Single Ptr, ByVal Right As Single Ptr) As UInteger /'* \brief Render audio data \param module The module handle to work on. \param samplerate Sample rate to render output. Should be in [8000,192000], but this is not enforced. \param count Number of audio frames to render per channel. \param left Pointer to a buffer of at least count elements that receives the left output. \param right Pointer to a buffer of at least count elements that receives the right output. \param rear_left Pointer to a buffer of at least count elements that receives the rear left output. \param rear_right Pointer to a buffer of at least count elements that receives the rear right output. \return The number of frames actually rendered. \retval 0 The end of song has been reached. \remarks The output buffers are only written to up to the returned number of elements. \remarks You can freely switch between any of the "openmpt_module_read*" variants if you see a need to do so. libopenmpt tries to introduce as little switching annoyances as possible. Normally, you would only use a single one of these functions for rendering a particular module. \remarks Floating point samples are in the [-1.0..1.0] nominal range. They are not clipped to that range though and thus might overshoot. \sa \ref libopenmpt_freebasic_outputformat '/ Declare Function openmpt_module_read_float_quad(ByVal module As openmpt_module Ptr, ByVal samplerate As Long, ByVal count As UInteger, ByVal Left As Single Ptr, ByVal Right As Single Ptr, ByVal rear_left As Single Ptr, ByVal rear_right As Single Ptr) As UInteger /'* \brief Render audio data \param module The module handle to work on. \param samplerate Sample rate to render output. Should be in [8000,192000], but this is not enforced. \param count Number of audio frames to render per channel. \param interleaved_stereo Pointer to a buffer of at least count*2 elements that receives the interleaved stereo output in the order (L,R). \return The number of frames actually rendered. \retval 0 The end of song has been reached. \remarks The output buffers are only written to up to the returned number of elements. \remarks You can freely switch between any of the "openmpt_module_read*" variants if you see a need to do so. libopenmpt tries to introduce as little switching annoyances as possible. Normally, you would only use a single one of these functions for rendering a particular module. \remarks It is recommended to use the floating point API because of the greater dynamic range and no implied clipping. \sa \ref libopenmpt_freebasic_outputformat '/ Declare Function openmpt_module_read_interleaved_stereo(ByVal module As openmpt_module Ptr, ByVal samplerate As Long, ByVal count As UInteger, ByVal interleaved_stereo As Short Ptr) As UInteger /'* \brief Render audio data \param module The module handle to work on. \param samplerate Sample rate to render output. Should be in [8000,192000], but this is not enforced. \param count Number of audio frames to render per channel. \param interleaved_quad Pointer to a buffer of at least count*4 elements that receives the interleaved quad surround output in the order (L,R,RL,RR). \return The number of frames actually rendered. \retval 0 The end of song has been reached. \remarks The output buffers are only written to up to the returned number of elements. \remarks You can freely switch between any of the "openmpt_module_read*" variants if you see a need to do so. libopenmpt tries to introduce as little switching annoyances as possible. Normally, you would only use a single one of these functions for rendering a particular module. \remarks It is recommended to use the floating point API because of the greater dynamic range and no implied clipping. \sa \ref libopenmpt_freebasic_outputformat '/ Declare Function openmpt_module_read_interleaved_quad(ByVal module As openmpt_module Ptr, ByVal samplerate As Long, ByVal count As UInteger, ByVal interleaved_quad As Short Ptr) As UInteger /'* \brief Render audio data \param module The module handle to work on. \param samplerate Sample rate to render output. Should be in [8000,192000], but this is not enforced. \param count Number of audio frames to render per channel. \param interleaved_stereo Pointer to a buffer of at least count*2 elements that receives the interleaved stereo output in the order (L,R). \return The number of frames actually rendered. \retval 0 The end of song has been reached. \remarks The output buffers are only written to up to the returned number of elements. \remarks You can freely switch between any of the "openmpt_module_read*" variants if you see a need to do so. libopenmpt tries to introduce as little switching annoyances as possible. Normally, you would only use a single one of these functions for rendering a particular module. \remarks Floating point samples are in the [-1.0..1.0] nominal range. They are not clipped to that range though and thus might overshoot. \sa \ref libopenmpt_freebasic_outputformat '/ Declare Function openmpt_module_read_interleaved_float_stereo(ByVal module As openmpt_module Ptr, ByVal samplerate As Long, ByVal count As UInteger, ByVal interleaved_stereo As Single Ptr) As UInteger /'* \brief Render audio data \param module The module handle to work on. \param samplerate Sample rate to render output. Should be in [8000,192000], but this is not enforced. \param count Number of audio frames to render per channel. \param interleaved_quad Pointer to a buffer of at least count*4 elements that receives the interleaved quad surround output in the order (L,R,RL,RR). \return The number of frames actually rendered. \retval 0 The end of song has been reached. \remarks The output buffers are only written to up to the returned number of elements. \remarks You can freely switch between any of the "openmpt_module_read*" variants if you see a need to do so. libopenmpt tries to introduce as little switching annoyances as possible. Normally, you would only use a single one of these functions for rendering a particular module. \remarks Floating point samples are in the [-1.0..1.0] nominal range. They are not clipped to that range though and thus might overshoot. \sa \ref libopenmpt_freebasic_outputformat '/ Declare Function openmpt_module_read_interleaved_float_quad(ByVal module As openmpt_module Ptr, ByVal samplerate As Long, ByVal count As UInteger, ByVal interleaved_quad As Single Ptr) As UInteger '*@} /'* \brief Get the list of supported metadata item keys \param module The module handle to work on. \return Metadata item keys supported by openmpt_module_get_metadata, as a semicolon-separated list. \sa openmpt_module_get_metadata \remarks Use openmpt_module_get_metadata_keys to automatically handle the lifetime of the returned pointer. '/ Declare Function openmpt_module_get_metadata_keys_ Alias "openmpt_module_get_metadata_keys" (ByVal module As openmpt_module Ptr) As Const ZString Ptr /'* \brief Get a metadata item value \param module The module handle to work on. \param key Metadata item key to query. Use openmpt_module_get_metadata_keys to check for available keys. Possible keys are: - type: Module format extension (e.g. it) or another similar identifier for modules formats that typically do not use a file extension - type_long: Format name associated with the module format (e.g. Impulse Tracker) - originaltype: Module format extension (e.g. it) of the original module in case the actual type is a converted format (e.g. mo3 or gdm) - originaltype_long: Format name associated with the module format (e.g. Impulse Tracker) of the original module in case the actual type is a converted format (e.g. mo3 or gdm) - container: Container format the module file is embedded in, if any (e.g. umx) - container_long: Full container name if the module is embedded in a container (e.g. Unreal Music) - tracker: Tracker that was (most likely) used to save the module file, if known - artist: Author of the module - title: Module title - date: Date the module was last saved, in ISO-8601 format. - message: Song message. If the song message is empty or the module format does not support song messages, a list of instrument and sample names is returned instead. - message_raw: Song message. If the song message is empty or the module format does not support song messages, an empty string is returned. - warnings: A list of warnings that were generated while loading the module. \return The associated value for key. \sa openmpt_module_get_metadata_keys \remarks Use openmpt_module_get_metadata to automatically handle the lifetime of the returned pointer. '/ Declare Function openmpt_module_get_metadata_ Alias "openmpt_module_get_metadata" (ByVal module As openmpt_module Ptr, ByVal key As Const ZString Ptr) As Const ZString Ptr /'* \brief Get the current estimated beats per minute (BPM). \param module The module handle to work on. \remarks Many module formats lack time signature metadata. It is common that this estimate is off by a factor of two, but other multipliers are also possible. \remarks Due to the nature of how module tempo works, the estimate may change slightly after switching libopenmpt's output to a different sample rate. \return The current estimated BPM. '/ Declare Function openmpt_module_get_current_estimated_bpm(ByVal module As openmpt_module Ptr) As Double /'* \brief Get the current speed \param module The module handle to work on. \return The current speed in ticks per row. '/ Declare Function openmpt_module_get_current_speed(ByVal module As openmpt_module Ptr) As Long /'* \brief Get the current tempo \param module The module handle to work on. \return The current tempo in tracker units. The exact meaning of this value depends on the tempo mode being used. '/ Declare Function openmpt_module_get_current_tempo(ByVal module As openmpt_module Ptr) As Long /'* \brief Get the current order \param module The module handle to work on. \return The current order at which the module is being played back. '/ Declare Function openmpt_module_get_current_order(ByVal module As openmpt_module Ptr) As Long /'* \brief Get the current pattern \param module The module handle to work on. \return The current pattern that is being played. '/ Declare Function openmpt_module_get_current_pattern(ByVal module As openmpt_module Ptr) As Long /'* \brief Get the current row \param module The module handle to work on. \return The current row at which the current pattern is being played. '/ Declare Function openmpt_module_get_current_row(ByVal module As openmpt_module Ptr) As Long /'* \brief Get the current amount of playing channels. \param module The module handle to work on. \return The amount of sample channels that are currently being rendered. '/ Declare Function openmpt_module_get_current_playing_channels(ByVal module As openmpt_module Ptr) As Long /'* \brief Get an approximate indication of the channel volume. \param module The module handle to work on. \param channel The channel whose volume should be retrieved. \return The approximate channel volume. \remarks The returned value is solely based on the note velocity and does not take the actual waveform of the playing sample into account. '/ Declare Function openmpt_module_get_current_channel_vu_mono(ByVal module As openmpt_module Ptr, ByVal channel As Long) As Single /'* \brief Get an approximate indication of the channel volume on the front-left speaker. \param module The module handle to work on. \param channel The channel whose volume should be retrieved. \return The approximate channel volume. \remarks The returned value is solely based on the note velocity and does not take the actual waveform of the playing sample into account. '/ Declare Function openmpt_module_get_current_channel_vu_left(ByVal module As openmpt_module Ptr, ByVal channel As Long) As Single /'* \brief Get an approximate indication of the channel volume on the front-right speaker. \param module The module handle to work on. \param channel The channel whose volume should be retrieved. \return The approximate channel volume. \remarks The returned value is solely based on the note velocity and does not take the actual waveform of the playing sample into account. '/ Declare Function openmpt_module_get_current_channel_vu_right(ByVal module As openmpt_module Ptr, ByVal channel As Long) As Single /'* \brief Get an approximate indication of the channel volume on the rear-left speaker. \param module The module handle to work on. \param channel The channel whose volume should be retrieved. \return The approximate channel volume. \remarks The returned value is solely based on the note velocity and does not take the actual waveform of the playing sample into account. '/ Declare Function openmpt_module_get_current_channel_vu_rear_left(ByVal module As openmpt_module Ptr, ByVal channel As Long) As Single /'* \brief Get an approximate indication of the channel volume on the rear-right speaker. \param module The module handle to work on. \param channel The channel whose volume should be retrieved. \return The approximate channel volume. \remarks The returned value is solely based on the note velocity and does not take the actual waveform of the playing sample into account. '/ Declare Function openmpt_module_get_current_channel_vu_rear_right(ByVal module As openmpt_module Ptr, ByVal channel As Long) As Single /'* \brief Get the number of sub-songs \param module The module handle to work on. \return The number of sub-songs in the module. This includes any "hidden" songs (songs that share the same sequence, but start at different order indices) and "normal" sub-songs or "sequences" (if the format supports them). \sa openmpt_module_get_subsong_name, openmpt_module_select_subsong, openmpt_module_get_selected_subsong '/ Declare Function openmpt_module_get_num_subsongs(ByVal module As openmpt_module Ptr) As Long /'* \brief Get the number of pattern channels \param module The module handle to work on. \return The number of pattern channels in the module. Not all channels do necessarily contain data. \remarks The number of pattern channels is completely independent of the number of output channels. libopenmpt can render modules in mono, stereo or quad surround, but the choice of which of the three modes to use must not be made based on the return value of this function, which may be any positive integer amount. Only use this function for informational purposes. '/ Declare Function openmpt_module_get_num_channels(ByVal module As openmpt_module Ptr) As Long /'* \brief Get the number of orders \param module The module handle to work on. \return The number of orders in the current sequence of the module. '/ Declare Function openmpt_module_get_num_orders(ByVal module As openmpt_module Ptr) As Long /'* \brief Get the number of patterns \param module The module handle to work on. \return The number of distinct patterns in the module. '/ Declare Function openmpt_module_get_num_patterns(ByVal module As openmpt_module Ptr) As Long /'* \brief Get the number of instruments \param module The module handle to work on. \return The number of instrument slots in the module. Instruments are a layer on top of samples, and are not supported by all module formats. '/ Declare Function openmpt_module_get_num_instruments(ByVal module As openmpt_module Ptr) As Long /'* \brief Get the number of samples \param module The module handle to work on. \return The number of sample slots in the module. '/ Declare Function openmpt_module_get_num_samples(ByVal module As openmpt_module Ptr) As Long /'* \brief Get a sub-song name \param module The module handle to work on. \param index The sub-song whose name should be retrieved \return The sub-song name. \sa openmpt_module_get_num_subsongs, openmpt_module_select_subsong, openmpt_module_get_selected_subsong \remarks Use openmpt_module_get_subsong_name to automatically handle the lifetime of the returned pointer. '/ Declare Function openmpt_module_get_subsong_name_ Alias "openmpt_module_get_subsong_name" (ByVal module As openmpt_module Ptr, ByVal index As Long) As Const ZString Ptr /'* \brief Get a channel name \param module The module handle to work on. \param index The channel whose name should be retrieved \return The channel name. \sa openmpt_module_get_num_channels \remarks Use openmpt_module_get_channel_name to automatically handle the lifetime of the returned pointer. '/ Declare Function openmpt_module_get_channel_name_ Alias "openmpt_module_get_channel_name" (ByVal module As openmpt_module Ptr, ByVal index As Long) As Const ZString Ptr /'* \brief Get an order name \param module The module handle to work on. \param index The order whose name should be retrieved \return The order name. \sa openmpt_module_get_num_orders \remarks Use openmpt_module_get_order_name to automatically handle the lifetime of the returned pointer. '/ Declare Function openmpt_module_get_order_name_ Alias "openmpt_module_get_order_name" (ByVal module As openmpt_module Ptr, ByVal index As Long) As Const ZString Ptr /'* \brief Get a pattern name \param module The module handle to work on. \param index The pattern whose name should be retrieved \return The pattern name. \sa openmpt_module_get_num_patterns \remarks Use openmpt_module_get_pattern_name to automatically handle the lifetime of the returned pointer. '/ Declare Function openmpt_module_get_pattern_name_ Alias "openmpt_module_get_pattern_name" (ByVal module As openmpt_module Ptr, ByVal index As Long) As Const ZString Ptr /'* \brief Get an instrument name \param module The module handle to work on. \param index The instrument whose name should be retrieved \return The instrument name. \sa openmpt_module_get_num_instruments \remarks Use openmpt_module_get_instrument_name to automatically handle the lifetime of the returned pointer. '/ Declare Function openmpt_module_get_instrument_name_ Alias "openmpt_module_get_instrument_name" (ByVal module As openmpt_module Ptr, ByVal index As Long) As Const ZString Ptr /'* \brief Get a sample name \param module The module handle to work on. \param index The sample whose name should be retrieved \return The sample name. \sa openmpt_module_get_num_samples \remarks Use openmpt_module_get_sample_name to automatically handle the lifetime of the returned pointer. '/ Declare Function openmpt_module_get_sample_name_ Alias "openmpt_module_get_sample_name" (ByVal module As openmpt_module Ptr, ByVal index As Long) As Const ZString Ptr /'* \brief Get pattern at order position \param module The module handle to work on. \param order The order item whose pattern index should be retrieved. \return The pattern index found at the given order position of the current sequence. '/ Declare Function openmpt_module_get_order_pattern(ByVal module As openmpt_module Ptr, ByVal order As Long) As Long /'* \brief Get the number of rows in a pattern \param module The module handle to work on. \param pattern The pattern whose row count should be retrieved. \return The number of rows in the given pattern. If the pattern does not exist, 0 is returned. '/ Declare Function openmpt_module_get_pattern_num_rows(ByVal module As openmpt_module Ptr, ByVal pattern As Long) As Long /'* \brief Get raw pattern content \param module The module handle to work on. \param pattern The pattern whose data should be retrieved. \param row The row from which the data should be retrieved. \param channel The channel from which the data should be retrieved. \param command The cell index at which the data should be retrieved. See \ref openmpt_module_command_index \return The internal, raw pattern data at the given pattern position. '/ Declare Function openmpt_module_get_pattern_row_channel_command_(ByVal module As openmpt_module Ptr, ByVal pattern As Long, ByVal row As Long, ByVal channel As Long, ByVal command_ As Long) As UByte /'* \brief Get formatted (human-readable) pattern content \param module The module handle to work on. \param pattern The pattern whose data should be retrieved. \param row The row from which the data should be retrieved. \param channel The channel from which the data should be retrieved. \param command The cell index at which the data should be retrieved. \return The formatted pattern data at the given pattern position. See \ref openmpt_module_command_index \sa openmpt_module_highlight_pattern_row_channel_command \remarks Use openmpt_module_format_pattern_row_channel_command to automatically handle the lifetime of the returned pointer. '/ Declare Function openmpt_module_format_pattern_row_channel_command_ Alias "openmpt_module_format_pattern_row_channel_command" (ByVal module As openmpt_module Ptr, ByVal pattern As Long, ByVal row As Long, ByVal channel As Long, ByVal command_ As Long) As Const ZString Ptr /'* \brief Get highlighting information for formatted pattern content \param module The module handle to work on. \param pattern The pattern whose data should be retrieved. \param row The row from which the data should be retrieved. \param channel The channel from which the data should be retrieved. \param command The cell index at which the data should be retrieved. See \ref openmpt_module_command_index \return The highlighting string for the formatted pattern data as retrieved by openmpt_module_get_pattern_row_channel_command at the given pattern position. \remarks The returned string will map each character position of the string returned by openmpt_module_get_pattern_row_channel_command to a highlighting instruction. Possible highlighting characters are: - " " : empty/space - "." : empty/dot - "n" : generic note - "m" : special note - "i" : generic instrument - "u" : generic volume column effect - "v" : generic volume column parameter - "e" : generic effect column effect - "f" : generic effect column parameter \sa openmpt_module_get_pattern_row_channel_command \remarks Use openmpt_module_highlight_pattern_row_channel_command to automatically handle the lifetime of the returned pointer. '/ Declare Function openmpt_module_highlight_pattern_row_channel_command_ Alias "openmpt_module_highlight_pattern_row_channel_command" (ByVal module As openmpt_module Ptr, ByVal pattern As Long, ByVal row As Long, ByVal channel As Long, ByVal command_ As Long) As Const ZString Ptr /'* \brief Get formatted (human-readable) pattern content \param module The module handle to work on. \param pattern The pattern whose data should be retrieved. \param row The row from which the data should be retrieved. \param channel The channel from which the data should be retrieved. \param width The maximum number of characters the string should contain. 0 means no limit. \param pad If true, the string will be resized to the exact length provided in the width parameter. \return The formatted pattern data at the given pattern position. \sa openmpt_module_highlight_pattern_row_channel \remarks Use openmpt_module_format_pattern_row_channel to automatically handle the lifetime of the returned pointer. '/ Declare Function openmpt_module_format_pattern_row_channel_ Alias "openmpt_module_format_pattern_row_channel" (ByVal module As openmpt_module Ptr, ByVal pattern As Long, ByVal row As Long, ByVal channel As Long, ByVal width_ As UInteger, ByVal pad As Long) As Const ZString Ptr /'* \brief Get highlighting information for formatted pattern content \param module The module handle to work on. \param pattern The pattern whose data should be retrieved. \param row The row from which the data should be retrieved. \param channel The channel from which the data should be retrieved. \param width The maximum number of characters the string should contain. 0 means no limit. \param pad If true, the string will be resized to the exact length provided in the width parameter. \return The highlighting string for the formatted pattern data as retrieved by openmpt_module_format_pattern_row_channel at the given pattern position. \sa openmpt_module_format_pattern_row_channel \remarks Use openmpt_module_highlight_pattern_row_channel to automatically handle the lifetime of the returned pointer. '/ Declare Function openmpt_module_highlight_pattern_row_channel_ Alias "openmpt_module_highlight_pattern_row_channel" (ByVal module As openmpt_module Ptr, ByVal pattern As Long, ByVal row As Long, ByVal channel As Long, ByVal width_ As UInteger, ByVal pad As Long) As Const ZString Ptr /'* \brief Retrieve supported ctl keys \param module The module handle to work on. \return A semicolon-separated list containing all supported ctl keys. \remarks Currently supported ctl values are: - load.skip_samples: Set to "1" to avoid loading samples into memory - load.skip_patterns: Set to "1" to avoid loading patterns into memory - load.skip_plugins: Set to "1" to avoid loading plugins - load.skip_subsongs_init: Set to "1" to avoid pre-initializing sub-songs. Skipping results in faster module loading but slower seeking. - seek.sync_samples: Set to "1" to sync sample playback when using openmpt_module_set_position_seconds or openmpt_module_set_position_order_row. - subsong: The current subsong. Setting it has identical semantics as openmpt_module_select_subsong(), getting it returns the currently selected subsong. - play.at_end: Chooses the behaviour when the end of song is reached: - "fadeout": Fades the module out for a short while. Subsequent reads after the fadeout will return 0 rendered frames. - "continue": Returns 0 rendered frames when the song end is reached. Subsequent reads will continue playing from the song start or loop start. - "stop": Returns 0 rendered frames when the song end is reached. Subsequent reads will return 0 rendered frames. - play.tempo_factor: Set a floating point tempo factor. "1.0" is the default tempo. - play.pitch_factor: Set a floating point pitch factor. "1.0" is the default pitch. - render.resampler.emulate_amiga: Set to "1" to enable the Amiga resampler for Amiga modules. This emulates the sound characteristics of the Paula chip and overrides the selected interpolation filter. Non-Amiga module formats are not affected by this setting. - render.resampler.emulate_amiga_type: Configures the filter type to use for the Amiga resampler. Supported values are: - "auto": Filter type is chosen by the library and might change. This is the default. - "a500": Amiga A500 filter. - "a1200": Amiga A1200 filter. - "unfiltered": BLEP synthesis without model-specific filters. The LED filter is ignored by this setting. This filter mode is considered to be experimental and might change in the future. - render.opl.volume_factor: Set volume factor applied to synthesized OPL sounds, relative to the default OPL volume. - dither: Set the dither algorithm that is used for the 16 bit versions of openmpt_module_read. Supported values are: - 0: No dithering. - 1: Default mode. Chosen by OpenMPT code, might change. - 2: Rectangular, 0.5 bit depth, no noise shaping (original ModPlug Tracker). - 3: Rectangular, 1 bit depth, simple 1st order noise shaping \remarks Use openmpt_module_get_ctls to automatically handle the lifetime of the returned pointer. '/ Declare Function openmpt_module_get_ctls_ Alias "openmpt_module_get_ctls" (ByVal module As openmpt_module Ptr) As Const ZString Ptr /'* \brief Get current ctl value \param module The module handle to work on. \param ctl The ctl key whose value should be retrieved. \return The associated ctl value, or NULL on failure. \sa openmpt_module_get_ctls \remarks Use openmpt_module_ctl_get to automatically handle the lifetime of the returned pointer. '/ Declare Function openmpt_module_ctl_get_ Alias "openmpt_module_ctl_get" (ByVal module As openmpt_module Ptr, ByVal ctl As Const ZString Ptr) As Const ZString Ptr /'* \brief Set ctl value \param module The module handle to work on. \param ctl The ctl key whose value should be set. \param value The value that should be set. \return 1 if successful, 0 in case the value is not sensible (e.g. negative tempo factor) or the ctl is not recognized. \sa openmpt_module_get_ctls '/ Declare Function openmpt_module_ctl_set(ByVal module As openmpt_module Ptr, ByVal ctl As Const ZString Ptr, ByVal value As Const ZString Ptr) As Long '* Callbacks for CRT FILE* handling Function openmpt_stream_read_func(ByVal stream As Any Ptr, ByVal dst As Any Ptr, ByVal bytes As UInteger) As UInteger Dim retval As UInteger = 0 Var f = Cast( FILE Ptr, stream ) If ( f = 0 ) Then Return 0 retval = fread( dst, 1, bytes, f ) If ( retval <= 0 ) Then Return 0 Return retval End Function '* Callbacks for CRT FILE* handling Function openmpt_stream_seek_func(ByVal stream As Any Ptr, ByVal offset As LongInt, ByVal whence As Long) As Long Var f = Cast( FILE Ptr, stream ) If ( f = 0 ) Then Return -1 Dim fwhence As Long Select Case whence Case OPENMPT_STREAM_SEEK_SET fwhence = SEEK_SET Case OPENMPT_STREAM_SEEK_CUR fwhence = SEEK_CUR Case OPENMPT_STREAM_SEEK_END fwhence = SEEK_END Case Else Return -1 End Select Return IIf(fseek( f, offset, fwhence ) <> 0, -1, 0) End Function '* Callbacks for CRT FILE* handling Function openmpt_stream_tell_func(ByVal stream As Any Ptr) As LongInt Dim retval As LongInt = 0 Var f = Cast( FILE Ptr, stream ) If ( f = 0 ) Then Return -1 EndIf retval = ftell( f ) If ( retval < 0 ) Then Return -1 Return retval End Function End Extern '* Retrieve the set of stream callbacks for CRT FILE* Function openmpt_stream_get_file_callbacks() As openmpt_stream_callbacks Static callbacks As openmpt_stream_callbacks = (@openmpt_stream_read_func, @openmpt_stream_seek_func, @openmpt_stream_tell_func) Return callbacks End Function /'* \brief Construct an openmpt_module \param file The FreeBASIC file handle to load from. \param logfunc Logging function where warning and errors are written. May be NULL. \param loguser Logging function user context. Used to pass any user-defined data associated with this module to the logging function. \param errfunc Error function to define error behaviour. May be NULL. \param erruser Error function user context. Used to pass any user-defined data associated with this module to the error function. \param errorcode Pointer to an integer where an error may get stored. May be NULL. \param error_message Pointer to a string pointer where an error message may get stored. May be NULL. \param ctls A map of initial ctl values. See openmpt_module_get_ctls(). \return A pointer to the constructed openmpt_module, or NULL on failure. \remarks The file handle can be closed after an openmpt_module has been constructed successfully. \sa openmpt_module_create2 '/ Function openmpt_module_create_from_fbhandle2(_ ByVal file As Integer,_ ByVal logfunc As openmpt_log_func = 0,_ ByVal loguser As Any Ptr = 0,_ ByVal errfunc As openmpt_error_func = 0,_ ByVal erruser As Any Ptr = 0,_ ByVal errorcode As Long Ptr = 0,_ ByVal error_message As Const ZString Ptr Ptr = 0,_ ByVal ctls As Const openmpt_module_initial_ctl Ptr = 0) As openmpt_module Ptr Return openmpt_module_create2(openmpt_stream_get_file_callbacks(), Cast(FILE Ptr, FileAttr(file, fbFileAttrHandle)), logfunc, loguser, errfunc, erruser, errorcode, error_message, ctls) End Function /'* \brief Construct an openmpt_module \param file The FreeBASIC file handle to load from. \param logfunc Logging function where warning and errors are written. May be NULL. \param loguser Logging function user context. Used to pass any user-defined data associated with this module to the logging function. \param ctls A map of initial ctl values. See openmpt_module_get_ctls(). \return A pointer to the constructed openmpt_module, or NULL on failure. \remarks The file handle can be closed after an openmpt_module has been constructed successfully. \deprecated Please use openmpt_module_create_from_fbhandle2(). \sa openmpt_module_create2 '/ Function openmpt_module_create_from_fbhandle(_ ByVal file As Integer,_ ByVal logfunc As openmpt_log_func = 0,_ ByVal loguser As Any Ptr = 0,_ ByVal ctls As Const openmpt_module_initial_ctl Ptr = 0) As openmpt_module Ptr Return openmpt_module_create_from_fbhandle2(file, logfunc, loguser, 0, 0, 0, 0, ctls) End Function /'* \brief Construct an openmpt_module \param filename The file to load from. \param logfunc Logging function where warning and errors are written. May be NULL. \param loguser Logging function user context. Used to pass any user-defined data associated with this module to the logging function. \param errfunc Error function to define error behaviour. May be NULL. \param erruser Error function user context. Used to pass any user-defined data associated with this module to the error function. \param errorcode Pointer to an integer where an error may get stored. May be NULL. \param error_message Pointer to a string pointer where an error message may get stored. May be NULL. \param ctls A map of initial ctl values. See openmpt_module_get_ctls(). \return A pointer to the constructed openmpt_module, or NULL on failure. \sa openmpt_module_create2 '/ Function openmpt_module_create_from_filename2(_ ByRef filename As String,_ ByVal logfunc As openmpt_log_func = 0,_ ByVal loguser As Any Ptr = 0,_ ByVal errfunc As openmpt_error_func = 0,_ ByVal erruser As Any Ptr = 0,_ ByVal errorcode As Long Ptr = 0,_ ByVal error_message As Const ZString Ptr Ptr = 0,_ ByVal ctls As Const openmpt_module_initial_ctl Ptr = 0) As openmpt_module Ptr Var file = fopen(filename, "rb") Var retval = CPtr(openmpt_module Ptr, 0) If(file <> 0) Then retval = openmpt_module_create2(openmpt_stream_get_file_callbacks(), file, logfunc, loguser, errfunc, erruser, errorcode, error_message, ctls) fclose(file) EndIf Return retval End Function /'* \brief Construct an openmpt_module \param filename The file to load from. \param logfunc Logging function where warning and errors are written. May be NULL. \param loguser Logging function user context. Used to pass any user-defined data associated with this module to the logging function. \param ctls A map of initial ctl values. See openmpt_module_get_ctls(). \return A pointer to the constructed openmpt_module, or NULL on failure. \deprecated Please use openmpt_module_create_from_filename2(). \sa openmpt_module_create2 '/ Function openmpt_module_create_from_filename(_ ByRef filename As String,_ ByVal logfunc As openmpt_log_func = 0,_ ByVal loguser As Any Ptr = 0,_ ByVal ctls As Const openmpt_module_initial_ctl Ptr = 0) As openmpt_module Ptr Return openmpt_module_create_from_filename2(filename, logfunc, loguser, 0, 0, 0, 0, ctls) End Function '* String handling for wrapping and freeing ZStrings returned by libopenmpt Function openmpt_get_zstring(sz As Const ZString Ptr) As String If(sz = 0) Then Return "" Dim As String s = *sz openmpt_free_string(sz) Return s End Function '* \sa openmpt_get_string_ Function openmpt_get_string(ByVal key As Const ZString Ptr) As String Return openmpt_get_zstring(openmpt_get_string_(key)) End Function '* \sa openmpt_error_string_ Function openmpt_error_string (ByVal errorcode As Long) As String Return openmpt_get_zstring(openmpt_error_string_(errorcode)) End Function '* \sa openmpt_module_error_get_last_message_ Function openmpt_module_error_get_last_message (ByVal module As openmpt_module Ptr) As String Return openmpt_get_zstring(openmpt_module_error_get_last_message_(module)) End Function '* \sa openmpt_module_get_metadata_keys_ Function openmpt_module_get_metadata_keys(ByVal module As openmpt_module Ptr) As String Return openmpt_get_zstring(openmpt_module_get_metadata_keys_(module)) End Function '* \sa openmpt_module_get_metadata_ Function openmpt_module_get_metadata(ByVal module As openmpt_module Ptr, ByVal key As Const ZString Ptr) As String Return openmpt_get_zstring(openmpt_module_get_metadata_(module, key)) End Function '* \sa openmpt_module_get_subsong_name_ Function openmpt_module_get_subsong_name(ByVal module As openmpt_module Ptr, ByVal index As Long) As String Return openmpt_get_zstring(openmpt_module_get_subsong_name_(module, index)) End Function '* \sa openmpt_module_get_channel_name_ Function openmpt_module_get_channel_name(ByVal module As openmpt_module Ptr, ByVal index As Long) As String Return openmpt_get_zstring(openmpt_module_get_channel_name_(module, index)) End Function '* \sa openmpt_module_get_order_name_ Function openmpt_module_get_order_name(ByVal module As openmpt_module Ptr, ByVal index As Long) As String Return openmpt_get_zstring(openmpt_module_get_order_name_(module, index)) End Function '* \sa openmpt_module_get_pattern_name_ Function openmpt_module_get_pattern_name(ByVal module As openmpt_module Ptr, ByVal index As Long) As String Return openmpt_get_zstring(openmpt_module_get_pattern_name_(module, index)) End Function '* \sa openmpt_module_get_instrument_name_ Function openmpt_module_get_instrument_name(ByVal module As openmpt_module Ptr, ByVal index As Long) As String Return openmpt_get_zstring(openmpt_module_get_instrument_name_(module, index)) End Function '* \sa openmpt_module_get_sample_name_ Function openmpt_module_get_sample_name(ByVal module As openmpt_module Ptr, ByVal index As Long) As String Return openmpt_get_zstring(openmpt_module_get_sample_name_(module, index)) End Function '* \sa openmpt_module_format_pattern_row_channel_command_ Function openmpt_module_format_pattern_row_channel_command(ByVal module As openmpt_module Ptr, ByVal pattern As Long, ByVal row As Long, ByVal channel As Long, ByVal command_ As Long) As String Return openmpt_get_zstring(openmpt_module_format_pattern_row_channel_command_(module, pattern, row, channel, command_)) End Function '* \sa openmpt_module_highlight_pattern_row_channel_command_ Function openmpt_module_highlight_pattern_row_channel_command(ByVal module As openmpt_module Ptr, ByVal pattern As Long, ByVal row As Long, ByVal channel As Long, ByVal command_ As Long) As String Return openmpt_get_zstring(openmpt_module_highlight_pattern_row_channel_command_(module, pattern, row, channel, command_)) End Function '* \sa openmpt_module_format_pattern_row_channel_ Function openmpt_module_format_pattern_row_channel(ByVal module As openmpt_module Ptr, ByVal pattern As Long, ByVal row As Long, ByVal channel As Long, ByVal width_ As UInteger, ByVal pad As Long) As String Return openmpt_get_zstring(openmpt_module_format_pattern_row_channel_(module, pattern, row, channel, width_, pad)) End Function '* \sa openmpt_module_highlight_pattern_row_channel_ Function openmpt_module_highlight_pattern_row_channel(ByVal module As openmpt_module Ptr, ByVal pattern As Long, ByVal row As Long, ByVal channel As Long, ByVal width_ As UInteger, ByVal pad As Long) As String Return openmpt_get_zstring(openmpt_module_highlight_pattern_row_channel_(module, pattern, row, channel, width_, pad)) End Function '* \sa openmpt_module_get_ctls_ Function openmpt_module_get_ctls(ByVal module As openmpt_module Ptr) As String Return openmpt_get_zstring(openmpt_module_get_ctls_(module)) End Function '* \sa openmpt_module_ctl_get_ Function openmpt_module_ctl_get(ByVal module As openmpt_module Ptr, ByVal ctl As Const ZString Ptr) As String Return openmpt_get_zstring(openmpt_module_ctl_get_(module, ctl)) End Function libopenmpt-0.6.1+release.autotools/libopenmpt/dox/0000755000175000017500000000000014175541576017316 500000000000000libopenmpt-0.6.1+release.autotools/libopenmpt/dox/changelog.md0000644000175000017500000015251414175540615021510 00000000000000 Changelog {#changelog} ========= For fully detailed change log, please see the source repository directly. This is just a high-level summary. ### libopenmpt 0.6.1 (2022-01-30) * [**Bug**] Linking libmpg123 no longer fails on OpenBSD. * [**Bug**] Possible hang with malformed DMF, DSM, MED, MUS, OKT and SymMOD files containing 65536 or more patterns when destroying the module. * [**Bug**] Avoid NaNs and infinite values with custom tunings and in the I3DL2Reverb plugin. * The letter "z" is now evaluated in fixed MIDI macros (Z80...ZFF) the same way as in Impulse Tracker. * MOD: Loosened VBlank timing heuristics so that "frame of mind" by Dascon plays correctly. * MOD: Validate the contents of "hidden" patterns beyond the end of the order list when the file size matches the expected size when only taken "official" patterns into account. This fixes Shofixti Ditty.mod from Star Control 2 while keeping other (partly broken) modules working. * MED: Command 20 (reverse sample) is now only applied when it's next to a note. * S3M: Introducing the "Send OPL key-off when triggering notes" compatibility setting broke retrigger for OPL notes again (they retriggered rather than not retriggering). * S3M: Retriggering a note no longer resets its pitch after a portamento. * S3M: Partially implement retrigger behaviour for stopped notes in SoundBlaster mode: Like in IT, it is not possible to retrigger a sample that has already stopped playing. * DIGI: Improve compatibility with E3x reverse sample command. * DSym: Tempos < 32 were treated as tempo slides. * SymMOD: Key-off command was not implemented properly. ### libopenmpt 0.6.0 (2021-12-23) * [**New**] `MUS` files from Psycho Pinball and Micro Machines 2 are now supported. * [**New**] `SymMOD` files created with Symphonie / Symphonie Pro are now supported. * [**New**] `FMT` files created with Davey W Taylor's FM Tracker are now supported. * [**New**] `DSYM` files created with Digital Symphony are now supported. * [**New**] `STX` files (transitional format between Scream Tracker 2 and 3) are now supported. * [**New**] TakeTracker MODs with `TDZ1` to `TDZ3` magic bytes are now supported. * [**New**] openmpt123: openmpt123 will now expand file wildcards passed on the command line in Windows when built with MSVC. * [**New**] libopenmpt_ext: New interface `interactive2` adding `openmpt::ext::interactive2::note_off()`, `openmpt::ext::interactive2::note_fade()`, `openmpt::ext::interactive2::set_channel_panning()`, `openmpt::ext::interactive2::get_channel_panning()`, `openmpt::ext::interactive2::set_note_finetune()`, and `openmpt::ext::interactive2::get_note_finetune()` (C++) and `openmpt_module_ext_interface_interactive2.note_off()`, `openmpt_module_ext_interface_interactive2.note_fade()`, `openmpt_module_ext_interface_interactive2.set_channel_panning()`, `openmpt_module_ext_interface_interactive2.get_channel_panning()`, `openmpt_module_ext_interface_interactive2.set_note_finetune()`, and `openmpt_module_ext_interface_interactive2.get_note_finetune()` (C). * [**New**] `Makefile` `CONFIG=emscripten` now supports `EMSCRIPTEN_TARGET=audioworkletprocessor` which builds an ES6 module in a single file with reduced dependencies suitable to be used in an AudioWorkletProcessor. * [**New**] `Makefile` `CONFIG=emscripten` now supports `EMSCRIPTEN_PORTS=1` which uses dependencies (zlib, mp123, ogg, and vorbis) from Emscripten Ports instead of using miniz, minimp3, and stb_vorbis locally or building zlib, mp123, ogg, and vorbis locally. * [**New**] `Makefile` `CONFIG=emscripten` and `CONFIG=djgpp` can now build zlib, mpg123, and vorbis locally instead of only supporting miniz, minimp3, and stb_vorbis via `ALLOW_LGPL=1`. * [**Change**] `Makefile` `CONFIG=emscripten` now supports `EMSCRIPTEN_TARGET=all` which provides WebAssembly as well as fallback to JavaScript in a single build. * [**Change**] openmpt123: DOS builds now use the Mercury fork of `liballegro 4.2` for improved hardware compatibility. * [**Change**] libopenmpt no longer generates internal interpolation tables on library load time, but instead only on first module load time. * [**Regression**] `Makefile` `CONFIG=emscripten` does not support `EMSCRIPTEN_TARGET=asmjs` or `EMSCRIPTEN_TARGET=asmjs128m` any more because support has been removed from current Emscripten versions. * [**Regression**] Support for GCC 7 has been removed. * [**Regression**] Support for Clang 5, 6 has been removed. * [**Regression**] Support for Emscripten versions older than 1.39.7 has been removed. * [**Regression**] Building with Android NDK older than NDK r19c is not supported any more. * libopenmpt can now detect infinite pattern loops and treats them as the song end. This means that setting a repeat count other than -1 now always guarantees that playback will eventually end. The song loop counter is decremented each time it ends up at the start of the infinite loop, so the song does not restart from the beginning even if the repeat count is not 0. * `openmpt::module::set_position_seconds()` accuracy has been improved for modules with pattern loops. * Samples played at the wrong volume when rendering modules in mono. * IT: Portamentos in files with Linear Slides disabled are now more accurate. * IT: Pitch/Pan Separation was affected by note-off commands, and wasn't reset by panning commands like in Impulse Tracker. * IT: Even after libopenmpt 0.5.14 the filter reset logic was still not 100% identical to Impulse Tracker: A note triggered on tick 0 of a row with a Pattern Delay effect still caused the filter to be reset on repetitions of that row even though the note wasn't retriggered. * IT: Added read-only support for BeRoTracker commands 1 and 2 (equivalent to XM commands K and L). * XM: BeRoTracker saves smooth MIDI macros in a different way from OpenMPT. This command is now imported correctly. * XM: Emulate FT2 Tone Portamento quirk that inverts portamento direction after the target was reached (if target note was higher than previous note). * S3M files saved with Impulse Tracker and latest Schism Tracker now also compute sample playback speed in Hertz. * Depending on whether an S3M file was last saved in Scream Tracker with the Sound Blaster or Gravis Ultrasound drivers loaded, different compatibility flags are now applied. For files saved with the GUS, the sample volume factor is now also ignored (fixes volume levels in S3Ms made on the GUS, in particular if they use both samples and OPL instruments). * S3M: Enforce the lower frequency bound. * MOD: Loosened VBlank timing heuristics so that the original copy of Guitar Slinger from Dizzy Tunes II plays correctly. * FAR: Correct portamento depth is now used. * DMF / IMF: Improved accuracy of finetune commands. * MDL: Implemented finetune command. * OKT: Various accuracy improvements such as: Sharing volume between mixed channels, volume commands on mixed channels are permanent (not reset with new notes), mixed channels do not support default sample volume, 7-bit samples are actually supposed to be played as-is (not amplified to full 8-bit range), reject speed command parameters >= 20. * zlib: v1.2.11 (2017-01-15). * mpg123: v1.29.3 (2021-12-11). * ogg: v1.3.5 (2021-06-04). * vorbis: v1.3.7 (2020-07-04). * miniz: v2.2.0 (2021-06-27). * minimp3: commit 50d2aaf360a53653b718fead8e258d654c3a7e41 (2021-11-27). * stb_vorbis: v1.22 commit 5a0bb8b1c1b1ca3f4e2485f4114c1c8ea021b781 (2021-07-12). * FLAC: v1.3.3 (2019-08-04). * PortAudio: v19.7.0 (2021-04-06). ### libopenmpt 0.5.0 (2020-05-24) * [**New**] OggMod compressed FastTracker 2 XM (OXM) modules are now supported. * [**New**] The emulated Amiga type when Amiga resampler emulation is enabled can now be selected via ctl `render.resampler.emulate_amiga_type`. Possible values are: `"auto"`, `"a500"`, `"a1200"`, and an experimental option `"unfiltered"`. * [**New**] libopenmpt: New API `openmpt::module::get_current_estimated_bpm()` (C++), and `openmpt_module_get_current_estimated_bpm()` (C) which provides accurate beats per minute information for module formats with time signature and an educated guess based on speed and tempo for others. * [**New**] libopenmpt: New type-aware ctl APIs that do not require memory allocations and are thus realtime-safe: `openmpt::module::ctl_get_boolean()`, `openmpt::module::ctl_get_integer()`, `openmpt::module::ctl_get_floatingpoint()`, `openmpt::module::ctl_get_text()`, `openmpt::module::ctl_set_boolean()`, `openmpt::module::ctl_set_integer()`, `openmpt::module::ctl_set_floatingpoint()` (C++), and `openmpt_module_ctl_get_boolean()`, `openmpt_module_ctl_get_integer()`, `openmpt_module_ctl_get_floatingpoint()`, `openmpt_module_ctl_get_text()`, `openmpt_module_ctl_set_boolean()`, `openmpt_module_ctl_set_integer()`, `openmpt_module_ctl_set_floatingpoint()` (C). * [**New**] libopenmpt C++ New API `openmpt::is_extension_supported2()` which takes a `std::string_view` parameter instead of `std::string`. * [**New**] libopenmpt C++: New API `openmpt::module::module(std::vector data)`, `openmpt::module::module(const std::byte * data, std::size_t size)`, `openmpt::module::module(const std::byte * beg, const std::byte * end)`. * [**New**] libopenmpt C++: New API `openmpt::probe_file_header(flags, const std::byte * data, std::size_t size, filesize)`, `openmpt::probe_file_header(flags, const std::byte * data, std::size_t size)`. * [**New**] libopenmpt_ext C++: New API `openmpt::module_ext::module_ext(std::vector data)`, `openmpt::module_ext::module_ext(const std::byte * data, std::size_t size)`, `openmpt::module_ext::module_ext(std::vector data)`, `openmpt::module_ext::module_ext(const std::uint8_t * data, std::size_t size)`. * [**Change**] std::istream based file I/O has been speed up. * [**Change**] Dependency on iconv on Linux has been removed. * [**Regression**] libmodplug: The libmodplug emulation layer has been removed from the libopenmpt tree. Please use the separate `libopenmpt-modplug` package instead. * [**Regression**] foo_openmpt: foo_openmpt is discontinued. Please use Kode54's fork foo_openmpt54: . * [**Regression**] Support for building with C++11 or C++14 has been removed. C++17 is now required to build libopenmpt. * [**Regression**] Support for client code using C++11 or C++ 14 has been removed. C++17 is now required to build libopenmpt client applications. * [**Regression**] Support for Visual Studio 2015 has been removed. * [**Regression**] Support for GCC 4.8, 4.9, 5, 6 has been removed. * [**Regression**] Support for Clang 3.6, 3.7, 3.8, 3.9, 4 has been removed. * [**Regression**] Support for Emscripten versions older than 1.39.1 has been removed. * [**Regression**] Building with Android NDK older than NDK r18b is not supported any more. * [**Regression**] openmpt123: Support for SDL1 (but not SDL2) output has been removed. * [**Regression**] openmpt123: Support for SDL2 older than 2.0.4 has been removed. * [**Regression**] Windows XP and Windows Vista are no longer supported. * [**Regression**] It is no longer possible to optionally use iconv for character set conversions. * [**Bug**] openmpt123: openmpt123 now honors the current locale and outputs text appropriately. * [**Bug**] openmpt123: Piping text output to other than console window targets on Windows has been fixed. * Greatly improved MED import. Synthesized instruments are still not supported but support was added for: Multisampled instruments, delta samples, more pattern commands, Hold and Decay, multiple songs and many other small changes. * Improved OPL channel allocation when more than 18 notes are active, so that channels that have completely faded out are prioritized over channels that have already been released but have not faded out yet. * Interactively triggering an OPL instrument could cause the first pattern channel to no longer be played back correctly. * Fix some inaccuracies in OPL emulator. * Fix overflow of OPL amplification happening at a synth volume level of 510. * End-of-sample pop reduction of surround channels was applied to front channels instead, causing a pop on the front channels instead of removing it on the back channels. * IT: Disable retrigger with short notes quirk for modules saved with Chibi Tracker, as it does not implement that quirk. * IT: Instrument and sample panning should not override channel panning for following notes. * IT: SBx is now prioritized over Bxx commands that are to the left of it. * IT: Duplicate Check Type "Sample" should only be applied if the instruments match, too. * IT: Duplicate Check Type "Note" should compare pattern notes, but it was comparing the new pattern note against the old translated note. * IT: Various fixes for envelope resetting. * IT / S3M: When combining SBx and EEx effects, don't skip the first row of the loop like in FastTracker 2. * S3M: Empty pattern commands now affect effect memory as well. * S3M: Offset beyond loop end wraps around to loop start like in Scream Tracker 3 + GUS (previously it just keep playing from the loop start, which is neither what GUS nor Sound Blaster drivers do). * S3M: Notes cannot be retriggered after they have been cut. * S3M: Fix portamento after note cut (fixes antediluvian_song.s3m). * S3M / MOD: Previous note offset is no longer used for retriggered notes if there was no instrument number next to the Qxy effect. * MOD: Sample swapping now also works if the sample that is being swapped from does not loop. Swapping to a non-looped sample now stops playback once the swapped-from sample reaches its (loop) end. * MOD: Fix early song ending due to ProTracker pattern jump quirk (EEx + Dxx on same row) if infinite looping is disabled. Fixes Haunted Tracks.mod by Triace. * MOD: Previous note offset is no longer used for retriggered notes if there was no instrument number next to the E9x effect. * MOD: Vibrato type "ramp down" was upside down. * XM: If a file contains patterns longer than 1024 rows, they are now clamped to 1024 rows instead of 64 rows. * XM: Do not reset note-off status on portamento if there is no instrument number. * mpg123: v1.26rc3. * ogg: v1.3.4. * vorbis: v1.3.6. * zlib: v1.2.11. * minimp3: commit 55da78cbeea5fb6757f8df672567714e1e8ca3e9 (2020-03-04). * stb_vorbis: v1.19 commit 37b9b20fdec06c75a0493e0bb59e2d0f288bfb51 (2020-02-05). * miniz: v2.1.0. * FLAC: v1.3.3. * PortAudio: commit 799a6834a58592eadc5712cba73b35956dc51579 (2020-03-26). ### libopenmpt 0.4.0 (2018-12-23) * [**New**] libopenmpt now includes emulation of the OPL chip and thus plays OPL instruments in S3M, C67 and MPTM files. OPL chip emulation volume can be changed with the new ctl `render.opl.volume_factor`. * [**New**] libopenmpt now supports CDFM / Composer 670 module files. * [**New**] Autotools `configure` and plain `Makefile` now honor the variable `CXXSTDLIB_PCLIBSPRIVATE` which serves the sole purpose of listing the standard library (or libraries) required for static linking. The contents of this variable will be put in `libopenmpt.pc` `Libs.private` and used for nothing else. See \ref libopenmpt_c_staticlinking . * [**New**] foo_openmpt: foo_openmpt now also works on Windows XP. * [**New**] libopenmpt Emscripten builds now ship with MP3 support by default, based on minimp3 by Lion (github.com/lieff). * [**New**] libopenmpt: New ctl `play.at_end` can be used to change what happens when the song end is reached: * "fadeout": Fades the module out for a short while. Subsequent reads after the fadeout will return 0 rendered frames. This is the default and identical to the behaviour in previous libopenmpt versions. * "continue": Returns 0 rendered frames when the song end is reached. Subsequent reads will continue playing from the song start or loop start. This can be used for custom loop logic, such as loop auto-detection and longer fadeouts. * "stop": Returns 0 rendered frames when the song end is reached. Subsequent reads will return 0 rendered frames. * [**New**] Add new metadata fields `"originaltype"` and `"originaltype_long"` which allow more clearly reflecting what is going on with converted formats like MO3 and GDM. * [**New**] `Makefile` `CONFIG=emscripten` now can generate WebAssembly via the additional option `EMSCRIPTEN_TARGET=wasm`. * [**New**] Compiling for DOS is now experimentally supported via DJGPP GCC 7.2 or later. * [**Change**] minimp3: Instead of the LGPL-2.1-licensed minimp3 by KeyJ, libopenmpt now uses the CC0-1.0-licensed minimp3 by Lion (github.com/lieff) as a fallback if libmpg123 is unavailable. The `USE_MINIMP3` `Makefile` option is gone and minimp3 will be used automatically in the `Makefile` build system if libmpg123 is not available. * [**Change**] openmpt123: openmpt123 now rejects `--output-type` in `--ui` and `--batch` modes and also rejects `--output` in `--render` mode. These combinations of options really made no sense and were rather confusing. * [**Change**] Android NDK build system now uses libc++ (`c++_shared`) instead of GNU libstdc++ (`gnustl_shared`), as recommended by Android NDK r16b. * [**Change**] xmp-openmpt: `openmpt-mpg123.dll` is no longer optional and must be placed into the same directory as `xmp-openmpt.dll`. * [**Change**] in_openmpt: `openmpt-mpg123.dll` is no longer optional and must be placed either into the directory of the player itself or into the same directory as `in_openmpt.dll`. This is dependent on how the player loads its plugins. For WinAMP 5, `openmpt-mpg123.dll` needs to be in the directory which contains `winamp.exe`. `in_openmpt.dll` needs to be in the `Plugins` directory. * [**Change**] foo_openmpt: foo_openmpt is now packaged as a fb2k-component package for easier installation. * [**Change**] When building libopenmpt with MinGW-w64, it is now recommended to use the posix thread model (as opposed to the win32 threading model), because the former does support std::mutex while the latter does not. When building with win32 threading model with the Autotools build system, it is recommended to provide the `mingw-std-threads` package. Building libopenmpt with MinGW-w64 without any `std::thread`/`std::mutex` support is deprecated and support for such configurations will be removed in libopenmpt 0.5. * [**Change**] `Makefile` `CONFIG=emscripten` now has 4 `EMSCRIPTEN_TARGET=` settings: `wasm` generates WebAssembly, `asmjs128m` generates asm.js with a fixed size 128MB heap, `asmjs` generates asm.js with a fixed default size heap (as of Emscripten 1.38.11, this amounts to 16MB), `js` generates JavaScript with dynamic heap growth and with compatibility for older VMs. * [**Change**] libmodplug: Update public headers to libmodplug 0.8.8.5. This adds support for kind-of automatic MODPLUG_EXPORT decoration on Windows. * [**Regression**] Support for Clang 3.4, 3.5 has been removed. * [**Regression**] Building with Android NDK older than NDK r16b is not supported any more. * [**Regression**] Support for Emscripten versions older than 1.38.5 has been removed. * [**Regression**] Support for libmpg123 older than 1.14.0 has been removed. * [**Regression**] Using MediaFoundation to decode MP3 samples is no longer supported. Use libmpg123 or minimp3 instead. * [**Regression**] libmodplug: Support for emulating libmodplug 0.8.7 API/ABI has been removed. * [**Bug**] xmp-openmpt: Sample rate and number of output channels were not applied correctly when using per-file settings. * [**Bug**] Internal mixer state was not initialized properly when initially rendering in 44100kHz stereo format. * [**Bug**] openmpt123: Prevent libsdl2 and libsdl from being enabled at the same time because they conflict with each other. * [**Bug**] libmodplug: Setting `SNDMIX_NORESAMPLING` in the C++ API always resulted in linear interpolation instead of nearest neighbour * IT: In Compatible Gxx mode, allow sample changes next to a tone portamento effect if a previous sample has already stopped playing. * IT: Fix broken volume envelopes with negative values as found in breakdwn.it by Elysis. * MOD: Slides and delayed notes are executed on every repetition of a row with row delay (fixes "ode to protracker"). * XM: If the sustain point of the panning envelope is reached before key-off, it is never released. * XM: Do not default recall volume / panning for delayed instrument-less notes * XM :E60 loop bug was not considered in song length calucation. * S3M: Notes without instrument number use previous note's sample offset. * Tighten M15 and MOD file rejection heuristics. * J2B: Ignore frequency limits from file header. Fixes Medivo.j2b, broken since libopenmpt-0.2.6401-beta17. * STM: More accurate tempo calculation. * STM: Better support for early format revisions (no such files have been found in the wild, though). * STM: Last character of sample name was missing. * SFX: Work around bad conversions of the "Operation Stealth" soundtrack by turning pattern breaks into note stops. * IMF: Filter cutoff was upside down and the cutoff range was too small. * ParamEq plugin center frequency was not limited correctly. * Keep track of active SFx macro during seeking. * The "note cut" duplicate note action did not volume-ramp the previously playing sample. * A song starting with non-existing patterns could not be played. * DSM: Support restart position and 16-bit samples. * DTM: Import global volume. * MOD: Support notes in octave 2, like in FastTracker 2 (fixes DOPE.MOD). * Do not apply Amiga playback heuristics to MOD files that have clearly been written with a PC tracker. * MPTM: More logical release node behaviour. * Subsong search is now less thorough. It could previously find many subsongs that are technically correct (unplayed rows at the beginning of patterns that have been jumped over due to pattern breaks), but so far no real-world module that would require such a thorough subsong detection was found. The old mechanism caused way more false positives than intended with real-world modules, though. * Restrict the unpacked size of compressed DMF, IT, MDL and MO3 samples to avoid huge allocations with malformed small files. ### libopenmpt 0.3 (2017-09-27) * [**New**] New error handling functionality in the C API, which in particular allows distinguishing potentially transient out-of-memory errors from parse errors during module loading. * [**New**] New API `openmpt::module::get_selected_subsong()` (C++) and `openmpt_module_get_selected_subsong()` (C). * [**New**] Faster file header probing API `openmpt::probe_file_header()` and `openmpt::probe_file_header_get_recommended_size` (C++), and `openmpt_probe_file_header()`, `openmpt_probe_file_header_without_filesize()`, `openmpt_probe_file_header_from_stream()` and `openmpt_probe_file_header_get_recommended_size()` (C). * [**New**] New API `openmpt::could_open_probability()` (C++) and `openmpt_could_open_probability()` (C). This fixes a spelling error in the old 0.2 API. * [**New**] openmpt123: openmpt123 can now open M3U, M3U8, M3UEXT, M3U8EXT and PLSv2 playlists via the `--playlist` option. * [**New**] openmpt123: openmpt123 now supports very fast file header probing via the `--probe` option. * [**New**] Libopenmpt now supports building for Windows 10 Universal (Windows Store 8.2) APIs with MSVC, and also for the older Windows Runtime APIs with MinGW-w64. * [**New**] New API header `libopenmpt_ext.h` which implements the libopenmpt extension APIs also for the C interface. * [**New**] The Reverb effect (S99 in S3M/IT/MPTM, and X99 in XM) is now implemented in libopenmpt. * [**New**] For Amiga modules, a new resampler based on the Amiga's sound characteristics has been added. It can be activated by passing the `render.resampler.emulate_amiga` ctl with a value of `1`. Non-Amiga modules are not affected by this, and setting the ctl overrides the resampler choice specified by `OPENMPT_MODULE_RENDER_INTERPOLATIONFILTER_LENGTH` or `openmpt::module::RENDER_INTERPOLATIONFILTER_LENGTH`. Support for the MOD command E0x (Set LED Filter) is also available when the Amiga resampler is enabled. * [**Change**] libopenmpt versioning changed and follows the more conventional major.minor.patch as well as the recommendations of the [SemVer](http://semver.org/) scheme now. In addition to the SemVer requirements, pre-1.0.0 versions will also honor API and ABI stability in libopenmpt (i.e. libopenmpt ignores SemVer Clause 4). * [**Change**] The output directories of the MSVC build system were changed to `bin/vs2015-shared/x86-64-win7/` (and similar) layout which allows building in the same tree with different compiler versions without overwriting other outputs. * [**Change**] The emscripten build now exports libopenmpt as 'libopenmpt' instead of the default 'Module'. * [**Change**] Android: The build system changed. The various Android.mk files have been merged into a single one which can be controlled using command line options. * [**Change**] The `Makefile` build system now passes `std=c++11` to the compiler by default. Older compilers may still work if you pass `STDCXX=c++0x` to the `make` invocation. * [**Change**] The `Makefile` option `ANCIENT=1` is gone. * [**Change**] The optional dependencies on `libltdl` or `libdl` are gone. They are no longer needed for any functionality. * [**Regression**] Compiling client code using the C++ API now requires a compiler running in C++11 mode. * [**Regression**] Support for GCC 4.1, 4.2, 4.3, 4.4, 4.5, 4.6, 4.7 has been removed. * [**Regression**] Support for Clang 3.0, 3.1, 3.2, 3.3 has been removed. * [**Regression**] Support for Emscripten versions older than 1.31.0 has been removed. * [**Regression**] Support for Android NDK versions older than 11 has been removed. * [**Regression**] Visual Studio 2008, 2010, 2012, 2013 support has been removed. * [**Regression**] Dynamic run-time loading of libmpg123 is no longer supported. Libmpg123 must be linked at link-time now. * [**Regression**] xmp-openmpt: xmp-openmpt now requires XMPlay 3.8 or later and compiling xmp-openmpt requires an appropriate XMPlay SDK with `XMPIN_FACE` >= `4`. * [**Regression**] Support for libmpg123 older than 1.13.0 has been removed. * [**Regression**] Un4seen unmo3 support has been removed. * [**Bug**] C++ API: `openmpt::exception` did not define copy and move constructors or copy and move assignment operators in libopenmpt 0.2. The compiler-generated ones were not adequate though. libopenmpt 0.3 adds the appropriate special member functions. This adds the respective symbol names to the exported ABI, which, depending on the compiler, might or might not have been there in libopenmpt 0.2. The possibly resulting possible ODR violation only affects cases that did crash in the libopenmpt 0.2 API anyway due to memory double-free, and does not cause any further problems in practice for all known platforms and compilers. * [**Bug**] The C API could crash instead of failing gracefully in out-of-memory situations. * [**Bug**] The test suite could fail on MacOSX or FreeBSD in non-fatal ways when no locale was active. * [**Bug**] `libopenmpt_stream_callbacks_fd.h` and `libopenmpt_stream_callbacks_file.h` were missing in Windows development packages. * [**Bug**] libopenmpt on Windows did not properly guard against current working directory DLL injection attacks. * [**Bug**] localtime() was used to determine the version of Schism Tracker used to save IT and S3M files. This function is not guaranteed to be thread-safe by the standard and is now no longer used. * [**Bug**] Possible crashes with malformed IT, ITP, AMS, MDL, MED, MPTM, PSM and Startrekker files. * [**Bug**] Possible hangs with malformed DBM, MPTM and PSM files. * [**Bug**] Possible hangs with malformed files containing cyclic plugin routings. * [**Bug**] Excessive loading times with malformed ITP / truncated AMS files. * [**Bug**] Plugins did not work correctly when changing the sample rate between two render calls. * [**Bug**] Possible NULL-pointer dereference read during obscure out-of-memory situations while handling exceptions in the C API. * [**Bug**] libmodplug: `libmodplug.pc` was wrong. * [**Bug**] Cross-compiling libopenmpt with autotools for Windows now properly sets `-municode` and `-mconsole` as well as all required Windows system libraries. * [**Bug**] foo_openmpt: Interpolation filter and volume ramping settings were confused in previous versions. This version resets both to the defaults. * [**Bug**] libmodplug: The CSoundFile::Read function in the emulated libmodplug C++ API returned the wrong value, causing qmmp (and possibly other software) to crash. * Support for SoundTracker Pro II (STP) and Digital Tracker (DTM) modules. * Increased accuracy of the sample position and sample rate to drift less when playing very long samples. * Various playback improvements for IT and XM files. * Channel frequency could wrap around after some excessive portamento / down in some formats since libopenmpt 0.2-beta17. * Playback improvements for S3M files made with Impulse Tracker and Schism Tracker. * ParamEq plugin emulation didn't do anything at full gain (+15dB). * All standard DMO effects are now also emulated on non-Windows and non-MSVC systems. * Added `libopenmpt_stream_callbacks_buffer.h` which adds `openmpt_stream_callbacks` support for in-memory buffers, possibly even only using a truncated prefix view into a bigger file which is useful for probing. * Avoid enabling some ProTracker-specific quirks for MOD files most likely created with ScreamTracker 3. * Tremolo effect only had half the intended strength in MOD files. * Pattern loops ending on the last row a pattern were not executed correctly in S3M files. * Work-around for reading MIDI macros and plugin settings in some malformed IT files written by old UNMO3 versions. * Improve tracker detection in IT format. * Playback fixes for 8-channel MED files * Do not set note volume to 0 on out-of-range offset in XM files. * Better import of some slide commands in SFX files. * Sample 15 in "Crew Generation" by Necros requires short loops at the beginning of the sample to not be ignored. Since we need to ignore them in some (non-ProTracker) modules, we heuristically disable the old loop sanitization behaviour based on the module channel count. * Both normal and percentage offset in PLM files were handled as percentage offset. * MT2 files with instruments that had both sample and plugin assignments were not read correctly. * Some valid FAR files were rejected erroneously. * Support for VBlank timing flag and comment field in PT36 files. * Improved accuracy of vibrato command in DIGI / DBM files. * STM: Add support for "WUZAMOD!" magic bytes and allow some slightly malformed STM files to load which were previously rejected. * Detect whether "hidden" patterns in the order list of SoundTracker modules should be taken into account or not. * Tighten heuristics for rejecting invalid 669, M15, MOD and ICE files and loosen them in other places to allow some valid MOD files to load. * Improvements to seeking: Channel panning was not always updated from instruments / samples when seeking, and out-of-range global volume was not applied correctly in some formats. * seek.sync_samples=1 did not apply PTM reverse offset effect and the volume slide part of combined volume slide + vibrato commands. * If the order list was longer than 256 items and there was a pattern break effect without a position jump on the last pattern of the sequence, it did not jump to the correct restart order. * `Makefile` has now explicit support for FreeBSD with no special option or configuration required. * openmpt123: Improved section layout in man page. * libmodplug: Added all missing C++ API symbols that are accessible via the public libmodplug header file. * Autotools build system now has options `--disable-openmpt123`, `--disable-tests` and `--disable-examples` which may be desireable when cross-compiling. * Windows binary packages now ship with libmpg123 included. ### libopenmpt 0.2-beta20 (2016-08-07) * [**Bug**] PSM loader was broken on big-endian platforms since forever. * [**Bug**] `load.skip_samples` ctl did not work for PSM16 modules. * There is a new `subsong` ctl, which can return the currently selected subsong. * More accurate ProTracker arpeggio wrap-around emulation. * More accurate sample tuning in PSM16 files. * Samples in DSM files were sometimes detuned and some pattern commands were not imported correctly. * More accurate import of MDL 7-bit panning command. * Only import pattern commands supported by the UltraTracker version that was used to save ULT files. Add support for command 5-C (end loop). * DMF sample loop lengths were off by one. * Unis 669 pan slide effect was too deep. * Several valid (but slightly corrupted possibly due to disk failures or data transfer errors) SoundTracker files were no longer loading since libopenmpt 0.2-beta18. ### libopenmpt 0.2-beta19 (2016-07-23) * [**Change**] libopenmpt now uses C++14 `[[deprecated]]` attribute instead of compiler-specific solutions when appropriate. * [**Change**] libopenmpt C++ header now uses C++11 `noexcept` instead of C++98 `throw()` exception specification when supported. `throw()` is deprecated since C++11. This does not change API or ABI as they are equivalent. Use `LIBOPENMPT_ASSUME_CPLUSPLUS_NOEXCEPT` to override the default. * [**Change**] The preprocessor macro `LIBOPENMPT_ANCIENT_COMPILER_STDINT` is gone. Please use `LIBOPENMPT_ASSUME_CPLUSPLUS_CSTDINT instead`. Additionally, the typedefs moved from illegal namespace ::std into somewhat less dangerous namespace ::openmpt::std. You can test `#ifdef LIBOPENMPT_QUIRK_NO_CSTDINT` client-side to check whether `libopenmpt.hpp` used the non-standard types. (Note: Of all supported compilers, this change only affects the 3 compilers with only limited support: MSVC 2008, GCC 4.1, GCC 4.2.) * [**Bug**] xmp-openmpt: Crash when viewing sample texts. * The public libopenmpt C++ header has auto-detection logic for the used C++ standard now. In case your client code compiler misreports the standard version or you want to override it for other reasons, `#define LIBOPENMPT_ASSUME_CPLUSPLUS` to the value of the standard version you desire to be used. There is also a macro for each individual aspect, like `LIBOPENMPT_ASSUME_CPLUSPLUS_CSTDINT`, `LIBOPENMPT_ASSUME_CPLUSPLUS_DEPRECATED`, `LIBOPENMPT_ASSUME_CPLUSPLUS_NOEXCEPT` which take precedence over the general macro. * Portamento with sample swap behaviour was wrong for ProTracker MODs. * Rewritten loader and various playback fixes for MDL files. * libopenmpt 0.2-beta18 broke import of many pattern commands in DBM, DMF and ULT files. * ADPCM samples in MOD files were broken since libopenmpt 0.2-beta17. ### libopenmpt 0.2-beta18 (2016-07-11) * [**Change**] openmpt123: Add PulseAudio output support. Autotools and `Makefile` build systems now depend on `libpulse` and `libpulse-simple` by default. Disable with `--without-pulseaudio` or `NO_PULSEAUDIO=1` respectively. When enabled, PulseAudio will be the default output driver, * [**Change**] xmp-openmpt: Settings are now stored in xmplay.ini like with every other plugin. * [**Regression**] openmpt123: Support for FLAC < 1.3.0 has been removed. FLAC before 1.3.0 is broken beyond repair as it provides `assert.h` in the include path. * [**Bug**] Generated pkg-config file libopenmpt.pc by both `Makefile` and Autotools build systems was totally broken. * [**Bug**] libopenmpt no longer uses the non-thread-safe global std::rand() function. * [**Bug**] Sample loops in GDM modules did not work when using Emscripten. * [**Bug**] XM and MO3 loaders could crash due to unaligned memory accesses. * [**Bug**] Fixed incorrect handling of custom MPTM tunings on big endian platforms. * [**Bug**] Fixed various problems found with clang 3.8 static analyzer, address sanitizer and undefined behaviour sanitizer. * [**Bug**] File header probing functionality was broken for most formats. * [**Bug**] With non-seekable streams, the entire file was almost always cached even if it was not of any supported module type. * Seeking in allsubsongs-mode now works correctly. * openmpt123: Added subsong support. * Various playback fixes for 669, IT, MT2 and MTM files. * Some MOD files with more than 128 patterns (e.g. NIETNU.MOD) were not loaded correctly. * A new example `libopenmpt_example_c_probe` has been added which demonstrates the usage and flexibility of openmpt_could_open_propability() in the C API under various constraints. ### libopenmpt 0.2-beta17 (2016-05-21) * [**Change**] The Makefile and Autotools build systems now require to explicitly specify `NO_LTDL=1` or `--without-ltdl` respectively if no support for dynamic loading of third party libraries via libtool libltdl is desired. * [**Change**] In the Makefile build system option `USE_MO3` and the Autotools build system option `--enable-mo3` are gone. Dynamic loading of un4seen unmo3 is now always enabled when dynamic loading is possible and built-in MO3 support is not possible because either a MP3 or a Vorbis decoder is missing. * [**Change**] The MSVC build system changed. The `libopenmptDLL` project is gone. Use the new `ReleaseShared` configuration of the `libopenmpt` project instead. libopenmpt now links against zlib by default. A separate project with smaller footprint linking against miniz is still available as `libopenmpt-small`. * [**Change**] The constants used to query library information from `openmpt_get_string()` and `openmpt::string::get()` (i.e. OPENMPT_STRING_FOO and openmpt::string::FOO) have been deprecated because having syntactic constants for theses keys makes extending the API in a backwards and forwards compatible way harder than it should be. Please just use the string literals directly. * [**Change**] Deprecated API identifiers will now cause deprecation warnings with MSVC, GCC and clang. `#define LIBOPENMPT_NO_DEPRECATE` to disable the warnings. * [**Change**] openmpt123: `--[no-]shuffle` option has been renamed to `--[no-]randomize`. A new `--[no-]shuffle` option has been added which shuffles randomly through the playlist as opposed to randomizing the playlist upfront. * [**Change**] Support for Un4seen unmo3 has generally been deprecated in favour of the new internal mo3 decoder. Un4seen unmo3 support will be removed on 2018-01-01. * [**Bug**] Memory consumption during loading has been reduced by about 1/3 in case a seekable input stream is provided (either via C API callback open functions or via C++ API iostream constructors). * [**Bug**] Some samples in AMS modules were detuned when using Emscripten. * [**Bug**] Possible crash with excessive portamento down in some formats. * [**Bug**] Possible crashes with malformed AMF, AMS, DBM, IT, MDL, MED, MPTM, MT2, PSM and MMCMP-, XPK- and PP20-compressed files. * [**Bug**] `openmpt::module::format_pattern_row_channel` with `width == 0` was returning an empty string instead of an string with unconstrained length. * Support for ProTracker 3.6 IFF-style modules and SoundFX / MultiMedia Sound (SFX / MMS) modules. * libopenmpt now has support for DMO plugins on Windows when built with MSVC. Additionally, the DMO Compression, Distortion, Echo, Gargle, ParamEQ and WavesReverb DSPs are emulated on on all other platforms. * libopenmpt now supports the DigiBooster Echo DSP. * To avoid any of the aforementioned plugins to be used, the load.skip_plugins ctl can be passed when loading a module. * libopenmpt got native MO3 support with MP3 decoding either via libmpg123 or MediaFoundation (on Windows 7 and up) and Vorbis decoding via libogg, libvorbis, libvorbisfile or stb_vorbis. * libopenmpt MSVC builds with Visual Studio 2010 or later on Windows 7 or later now use an internal MO3 decoder with libogg, libvorbis, libvorbisfile, and libmpg123 or minimp3 or MediaFoundation suppport by default. Visual Studio 2008 builds still use unmo3.dll by default but also support the built-in decoder in which case libmpg123 is required. * libopenmpt with Makefile or Autotools build system can now also use glibc/libdl instead of libtool/libltdl for dynamic loading of third-party libraries. Options `NO_DL=1` and `--without-dl` have been added respectively. * The `Makefile` build system got 4 new options NO_MPG123, NO_OGG, NO_VORBIS, NO_VORBISFILE. The default is to use the new dependencies automatically. * The `Autotools` build system got 4 new options --without-mpg123, --without-ogg, --without-vorbis, --without-vorbisfile. The default is to use the new dependencies automatically. * Makefile and Android builds got support for using minimp3 instead of libmpg123. For Android, use `Android-minimp3-stbvorbis.mk`, for Makefile use `USE_MINIMP3=1`. You have to download [minimp3](http://keyj.emphy.de/minimp3/) yourself and put its contents into `include/minimp3/`. * `"source_url"`, `"source_date"` and `"build_compiler"` keys have been added to `openmpt_string_get()` and `openmpt::string::get()`. * openmpt123: Add new `--[no-]restart]` option which restarts the playlist when finished. * Improved Ultimate SoundTracker version detection heuristics. * Playing a sample at a sample rate close to the mix rate could lead to small clicks when using vibrato. * More fine-grained internal legacy module compatibility settings to correctly play back modules made with older versions of OpenMPT and a few other trackers. * The tail of compressed MDL samples was slightly off. * Some probably hex-edited XM files (e.g. cybernostra weekend.xm) were not loaded correctly. * Countless other playback fixes for MOD, XM, S3M, IT and MT2 files. ### libopenmpt 0.2-beta16 (2015-11-22) * [**Change**] The Autotools build system does strict checking of all dependencies now. Instead of best effort auto-magic detection of all potentially optional dependencies, the default set of dependencies is now enforced unless each individual dependency gets explicitely disabled via `--without-foo` or `--disable-foo` `./configure` switches. Run `./configure --help` for the full list of options. * [**Bug**] Some MOD files were erroneously detected as 669 files. * [**Bug**] Some malformed AMF files could result in very long loading times. * [**Bug**] Fixed crashes in IMF and MT2 loaders. * [**Bug**] MTM files generated by UNMO3 were not loaded properly. * Improved MTM playback. * `make CONFIG=haiku` for Haiku has been added. * Language bindings for FreeBASIC have been added (see `libopenmpt/bindings/`). ### libopenmpt 0.2-beta15 (2015-10-31) * [**Change**] openmpt123: SDL2 is now supported and preferred to SDL1 if available with the `Makefile` build system. * [**Bug**] Emscripten support for older emscripten versions broke in -beta14. These are now supported again when using `make CONFIG=emscripten-old`. * [**Bug**] Fixed crashes in MED loader. * Playback improvements and loader fixes for MOD, MT2 and MED. ### libopenmpt 0.2-beta14 (2015-09-13) * [**Change**] The C++ API example now uses the PortAudio C++ bindings instead of the C API. * [**Change**] Default compiler options for Emscripten have been changed to more closely match the Emscripten recommendations. * [**Bug**] Client code compilation with C89 compilers was broken in beta13. * [**Bug**] Test suite failed on certain Emscripten/node.js combinations. * [**Bug**] Fixed various crashes or hangs in DMF, OKT, PLM, IT and MPTM loaders. * Implemented error handling in the libopenmpt API examples. * Various playback improvements and fixes for OKT, IT and MOD. ### libopenmpt 0.2-beta13 (2015-08-16) * [**Change**] The MSVC build system has been redone. Solutions are now located in `build/vsVERSION/`. * [**Bug**] get_current_channel_vu_left and get_current_channel_vu_right only return the volume of the front left and right channels now. get_current_channel_vu_rear_left and get_current_channel_vu_rear_right do now actually work and return non-zero values. * [**Bug**] Fix crashes and hangs in MED and MDL loaders and with some truncated compressed IT samples. * [**Bug**] Fix crash when playing extremely high-pitched samples. * Completed C and C++ documentation * Added new key for openmpt::module::get_metadata, "message_raw", which returns an empty string if there is no song message rather than a list of instrument names. * in_openmpt: Support for compiling with VS2008. * xmp-openmpt: Support for compiling with VS2008. * in_openmpt: Add a more readable file information window. ### libopenmpt 0.2-beta12 (2015-04-19) * Playback fix when row delay effect is used together with offset command. * A couple of fixes for the seek.sync_samples=1 case. * IT compatibility fix for IT note delay. * ProTracker MOD playback compatibility improvement. ### libopenmpt 0.2-beta11 (2015-04-18) * [**Change**] openmpt_stream_seek_func() now gets called with OPENMPT_STREAM_SEEK_SET, OPENMPT_STREAM_SEEK_CUR and OPENMPT_STREAM_SEEK_END whence parameter instead of SEEK_SET, SEEK_CUR and SEEK_END. These are defined to 0, 1 and 2 respectively which corresponds to the definition in all common C libraries. If your C library uses different constants, this theoretically breaks binary compatibility. The old libopenmpt code, however, never actually called the seek function, thus, there will be no problem in practice. * [**Change**] openmpt123: When both SDL1.2 and PortAudio are available, SDL is now the preferred backend because SDL is more widespread and better tested on all kinds of different platforms, and in general, SDL is just more reliable. * [**Bug**] libopenmpt now also compiles with GCC 4.3. * libopenmpt now supports PLM (Disorder Tracker 2) files. * Various playback improvements and fixes for IT, S3M, XM, MOD, PTM and 669 files. ### libopenmpt 0.2-beta10 (2015-02-17) * [**Change**] Makefile configuration filenames changed from `build/make/Makefile.config.*` to `build/make/config-*.mk`. * [**Change**] libopenmpt for Android now supports unmo3 from un4seen. See `build/android_ndk/README.AndroidNDK.txt` for details. * [**Bug**] Fix out-of-bounds read in mixer code for ProTracker-compatible MOD files which was introduced back in r4223 / beta6. * Vibrato effect was too weak in beta8 and beta9 in IT linear slide mode. * Very small fine portamento was wrong in beta8 and beta9 in IT linear slide mode. * Tiny IT playback compatibility improvements. * STM playback improvements. ### libopenmpt 0.2-beta9 (2014-12-21) * [**Bug**] libopenmpt_ext.hpp was missing from the Windows binary zip files. ### libopenmpt 0.2-beta8 (2014-12-21) * [**Change**] foo_openmpt: Settings are now accessible via foobar2000 advanced settings. * [**Change**] Autotools based build now supports libunmo3. Specify --enable-unmo3. * [**Change**] Support for dynamic loading of libunmo3 on MacOS X. * [**Change**] libopenmpt now uses libltld (from libtool) for dynamic loading of libunmo3 on all non-Windows platforms. * [**Change**] Support for older compilers: * GCC 4.1.x to 4.3.x (use `make ANCIENT=1`) * Microsoft Visual Studio 2008 (with latest Service Pack) (see `build/vs2008`) * [**Change**] libopenmpt_ext.hpp is now distributed by default. The API is still considered experimental and not guaranteed to stay API or ABI compatible. * [**Change**] xmp-openmpt / in_openmpt: No more libopenmpt_settings.dll. The settings dialog now uses a statically linked copy of MFC. * [**Bug**] The -autotools tarballs were not working at all. * Vastly improved MT2 loader. * Improved S3M playback compatibility. * Added openmpt::ext::interactive, an extension which adds a whole bunch of new functionality to change playback in some way or another. * Added possibility to sync sample playback when using openmpt::module::set_position_* by setting the ctl value seek.sync_samples=1 * Support for "hidden" subsongs has been added. They are accessible through the same interface as ordinary subsongs, i.e. use openmpt::module::select_subsong to switch between any kind of subsongs. * All subsongs can now be played consecutively by passing -1 as the subsong index in openmpt::module::select_subsong. * Added documentation for a couple of more functions. ### libopenmpt 0.2-beta7 (2014-09-07) * [**Change**] libopenmpt now has an GNU Autotools based build system (in addition to all previously supported ways of building libopenmpt). Autotools support is packaged separately as tarballs ending in `-autotools.tar.gz`. * [**Bug**] The distributed windows .zip file did not include pugixml. * [**Regression**] openmpt123: Support for writing WavPack (.wv) files has been removed. Reasoning: 1. WavPack support was incomplete and did not include support for writing WavPack metadata at all. 2. openmpt123 already supports libSndFile which can be used to write uncompressed lossless WAV files which can then be encoded to whatever format the user desires with other tools. ### libopenmpt 0.2-beta6 (2014-09-06) * [**Change**] openmpt123: SDL is now also used by default if availble, in addition to PortAudio. * [**Change**] Support for emscripten is no longer experimental. * [**Change**] libopenmpt itself can now also be compiled with VS2008. * [**Bug**] Fix all known crashes on platforms that do not support unaligned memory access. * [**Bug**] openmpt123: Effect column was always missing in pattern display. ### libopenmpt 0.2-beta5 (2014-06-15) * [**Change**] Add unmo3 support for non-Windows builds. * [**Change**] Namespace all internal functions in order to allow statically linking against libopenmpt without risking duplicate symbols. * [**Change**] Iconv is now completely optional and only used on Linux systems by default. * [**Change**] Added libopenmpt_example_c_stdout.c, an example without requiring PortAudio. * [**Change**] Add experimental support for building libopenmpt with emscripten. * [**Bug**] Fix ping-pong loop behaviour which broke in 0.2-beta3. * [**Bug**] Fix crashes when accessing invalid patterns through libopenmpt API. * [**Bug**] Makefile: Support building with missing optional dependencies without them being stated explicitely. * [**Bug**] openmpt123: Crash when quitting while playback is stopped. * [**Bug**] openmpt123: Crash when writing output to a file in interactive UI mode. * [**Bug**] openmpt123: Wrong FLAC output filename in --render mode. * Various smaller playback accuracy improvements. ### libopenmpt 0.2-beta4 (2014-02-25) * [**Bug**] Makefile: Dependency tracking for the test suite did not work. ### libopenmpt 0.2-beta3 (2014-02-21) * [**Change**] The test suite is now built by default with Makefile based builds. Use `TEST=0` to skip building the tests. `make check` runs the test suite. * [**Bug**] Crash in MOD and XM loaders on architectures not supporting unaligned memory access. * [**Bug**] MMCMP, PP20 and XPK unpackers should now work on non-x86 hardware and implement proper bounds checking. * [**Bug**] openmpt_module_get_num_samples() returned the wrong value. * [**Bug**] in_openmpt: DSP plugins did not work properly. * [**Bug**] in_openmpt/xmp-openmpt: Setting name for stereo separation was misspelled. This version will revert your stereo separation settings to default. * [**Bug**] Crash when loading some corrupted modules with stereo samples. * Support building on Android NDK. * Avoid clicks in sample loops when using interpolation. * IT filters are now done in integer instead of floating point. This improves performance, especially on architectures with no or a slow FPU. * MOD pattern break handling fixes. * Various XM playback improvements. * Improved and switchable dithering when using 16bit integer API. ### libopenmpt 0.2-beta2 (2014-01-12) * [**Bug**] MT2 loader crash. * [**Bug**] Saving settings in in_openmpt and xmp-openmpt did not work. * [**Bug**] Load libopenmpt_settings.dll also from below Plugins directory in Winamp. * DBM playback improvements. ### libopenmpt 0.2-beta1 (2013-12-31) * First release. libopenmpt-0.6.1+release.autotools/libopenmpt/dox/dependencies.md0000644000175000017500000001044414170042301022162 00000000000000 Dependencies {#dependencies} ============ Dependencies ------------ ### libopenmpt * Supported compilers for building libopenmpt: * **Microsoft Visual Studio 2017** or higher, running on a amd64 build system (other target systems are supported) Please note that we do not support building with a later Visual Studio installation with an earlier compiler version. This is because, while later Visual Studio versions allow installing earlier compilers to be available via the later version's environment, in this configuration, the earlier compiler will still use the later C and C++ runtime's headers and implementation, which significantly increases the matrix of possible configurations to test. * **GCC 8.1** or higher * **Clang 7** or higher * **MinGW-W64 8.1** or higher (it is recommended to preferably use posix threading model as opposed to win32 threading model, or at least have mingw-std-threads available otherwise) * **emscripten 1.39.1** or higher * **DJGPP GCC 8.1** or higher * any other **C++17 compliant** compiler libopenmpt makes the following assumptions about the C++ implementation used for building: * `std::numeric_limits::digits == 8` (enforced by static_assert) * existence of `std::uintptr_t` (enforced by static_assert) * in C++20 mode, `std::endian::little != std::endian::big` (enforced by static_assert) * `wchar_t` encoding is either UTF-16 or UTF-32 (implicitly assumed) * representation of basic source character set is ASCII (implicitly assumed) * representation of basic source character set is identical in char and `wchar_t` (implicitly assumed) libopenmpt does not rely on any specific implementation defined or undefined behaviour (if it does, that's a bug in libopenmpt). In particular: * `char` can be `signed` or `unsigned` * shifting signed values is implementation defined * `signed` integer overflow is undefined * `float` and `double` can be non-IEEE754 libopenmpt can optionally support certain incomplete C++ implementations: * platforms without `wchar_t` support (like DJGPP) * platforms without working `std::random_device` (like Emscripten when running in `AudioWorkletProcessor` context) * platforms without working `std::high_resolution_clock` (like Emscripten when running in `AudioWorkletProcessor` context) * Required compilers to use libopenmpt: * Any **C89** / **C99** / **C11** compatible compiler should work with the C API as long as a **C99** compatible **stdint.h** is available. * Any **C++17** compatible compiler should work with the C++ API. * **J2B** support requires an inflate (deflate decompression) implementation: * **zlib** (or **miniz** can be used internally) * **MO3** support requires: * **libmpg123 >= 1.14.0** (or **minimp3 by Lion (github.com/lieff)** can be used internally) * **libogg**, **libvorbis**, and **libvorbisfile** (or **stb_vorbis** can be used internally) * Building on Unix-like systems requires: * **GNU make** * **pkg-config** * The Autotools-based build system requires: * **pkg-config 0.24** or higher * **zlib** * **doxygen** ### openmpt123 * Live sound output requires one of: * **PulseAudio** * **SDL 2** * **PortAudio v19** * **Win32** * **liballegro 4.2** on DJGPP/DOS Optional dependencies --------------------- ### libopenmpt * **doxygen 1.8** or higher is required to build the documentation. ### openmpt123 * Rendering to PCM files can use: * **FLAC 1.2** or higher * **libsndfile** * **Win32** for WAVE * raw PCM has no external dependencies * **help2man** is required to build the documentation. Source packages --------------- Building the source packages additionally requires: * 7z (7-zip) * autoconf * autoconf-archive * automake * awk (mawk) * git * gzip * help2man * libtool * subversion * tar * xpath (libxml-xpath-perl) * zip libopenmpt-0.6.1+release.autotools/libopenmpt/dox/gettingstarted.md0000644000175000017500000001541014154653152022600 00000000000000 Getting Started {#gettingstarted} =============== How to compile -------------- ### libopenmpt and openmpt123 - Autotools Grab a `libopenmpt-VERSION-autotools.tar.gz` tarball. ./configure make make check sudo make install Cross-compilation is generally supported (although only tested for targetting MinGW-w64). Note that some MinGW-w64 distributions come with the `win32` threading model enabled by default instead of the `posix` threading model. The `win32` threading model lacks proper support for C++11 `` and `` as well as thread-safe magic statics. It is recommended to use the `posix` threading model for libopenmpt for this reason. On Debian, the appropriate configure command is `./configure --host=x86_64-w64-mingw32 CC=x86_64-w64-mingw32-gcc-posix CXX=x86_64-w64-mingw32-g++-posix` for 64bit, or `./configure --host=i686-w64-mingw32 CC=i686-w64-mingw32-gcc-posix CXX=i686-w64-mingw32-g++-posix` for 32bit. Other MinGW-w64 distributions may differ. - Visual Studio: - You will find solutions for Visual Studio in the matching `build/vsVERSIONwinWINDOWSVERSION/` folder. Minimal projects that target Windows 10 UWP are available in `build/vsVERSIONuwp/`. Most projects are supported with any of the mentioned Visual Studio verions, with the following exceptions: - in_openmpt: Requires Visual Studio with MFC. - xmp-openmpt: Requires Visual Studio with MFC. - libopenmpt requires the compile host system to be amd64 or ARM64 when building with Visual Studio. - In order to build libopenmpt for Windows XP, the Visual Studio 2017 XP targetting toolset as well as the Windows 8.1 SDK need to be installed. The SDK is optionally included with Visual Studio 2017, but must be separately installed with later Visual Studio versions. The Windows 8.1 SDK is available from or directly from . - You will need the Winamp 5 SDK and the XMPlay SDK if you want to compile the plugins for these 2 players. They can be downloaded automatically on Windows 7 or later by just running the `build/download_externals.cmd` script. If you do not want to or cannot use this script, you may follow these manual steps instead: - Winamp 5 SDK: To build libopenmpt as a winamp input plugin, copy the contents of `WA5.55_SDK.exe` to include/winamp/. Please visit [winamp.com](http://wiki.winamp.com/wiki/Plug-in_Developer) to download the SDK. You can disable in_openmpt in the solution configuration. - XMPlay SDK: To build libopenmpt with XMPlay input plugin support, copy the contents of xmp-sdk.zip into include/xmplay/. Please visit [un4seen.com](https://www.un4seen.com/xmplay.html) to download the SDK. You can disable xmp-openmpt in the solution configuration. - Makefile The makefile supports different build environments and targets via the `CONFIG=` parameter directly to the make invocation. Use `make CONFIG=$newconfig clean` when switching between different configs because the makefile cleans only intermediates and target that are active for the current config and no configuration state is kept around across invocations. - native build: Simply run make which will try to guess the compiler based on your operating system. - gcc or clang (on Unix-like systems, including Mac OS X with MacPorts, and Haiku (32-bit Hybrid and 64-bit)): The Makefile requires pkg-config for native builds. For sound output in openmpt123, PortAudio or SDL is required. openmpt123 can optionally use libflac and libsndfile to render PCM files to disk. When you want to use gcc, run: make CONFIG=gcc When you want to use clang, it is recommended to do: make CONFIG=clang - mingw-w64: make CONFIG=mingw64-win32 # for win32 make CONFIG=mingw64-win64 # for win64 - emscripten (on Unix-like systems): Run: # generates WebAssembly with JavaScript fallback make CONFIG=emscripten EMSCRIPTEN_TARGET=all or # generates WebAssembly make CONFIG=emscripten EMSCRIPTEN_TARGET=wasm or # generates JavaScript with compatibility for older VMs make CONFIG=emscripten EMSCRIPTEN_TARGET=js Running the test suite on the command line is also supported by using node.js. Depending on how your distribution calls the `node.js` binary, you might have to edit `build/make/config-emscripten.mk`. - DJGPP / DOS Cross-compilation from Linux systems is supported with DJGPP GCC via make CONFIG=djgpp `openmpt123` can use liballegro 4.2 for sound output on DJGPP/DOS. liballegro can either be installed system-wide in the DJGPP environment or downloaded into the `libopenmpt` source tree. make CONFIG=djgpp USE_ALLEGRO42=1 # use installed liballegro or ./build/download_externals.sh # download liballegro source make CONFIG=djgpp USE_ALLEGRO42=1 BUNDLED_ALLEGRO42=1 - American Fuzzy Lop: To compile libopenmpt with fuzzing instrumentation for afl-fuzz, run: make CONFIG=afl For more detailed instructions, read `contrib/fuzzing/readme.md`. - other compilers: To compile libopenmpt with other compliant compilers, run: make CONFIG=generic The `Makefile` supports some customizations. You might want to read the top which should get you some possible make settings, like e.g. `make DYNLINK=0` or similar. Cross compiling or different compiler would best be implemented via new `config-*.mk` files. The `Makefile` also supports building doxygen documentation by using make doc Binaries and documentation can be installed systen-wide with make PREFIX=/yourprefix install make PREFIX=/yourprefix install-doc Some systems (i.e. Linux) require running sudo ldconfig in order for the system linker to be able to pick up newly installed libraries. `PREFIX` defaults to `/usr/local`. A `DESTDIR=` parameter is also supported. - Android NDK See `build/android_ndk/README.AndroidNDK.txt`. libopenmpt-0.6.1+release.autotools/libopenmpt/dox/index.dox0000644000175000017500000000274713673126612021063 00000000000000 /*! * \mainpage Contents * * libopenmpt is a cross-platform C++ and C library to decode tracked music files (modules) into a raw PCM audio stream. * * libopenmpt is based on the player code of the Open ModPlug Tracker project (OpenMPT, https://openmpt.org/) * * \section toc Contents * - \ref md_README "README" * - \ref dependencies "Dependencies" * - \ref gettingstarted "Getting Started" * - \ref packaging "Packaging" * - \ref md_doc_contributing "Contributing" * - \ref md_doc_libopenmpt_styleguide "libopenmpt Style Guide" * - \ref md_doc_openmpt_styleguide "OpenMPT Style Guide" * - \ref tests "Tests" * - \ref changelog "Changelog" * - \ref md_doc_module_formats "Implementing new Module Formats" * - Issue Tracker * \subsection toc_apis APIs * - libopenmpt * - \ref libopenmpt "Common Details" * - libopenmpt C++ * - \ref libopenmpt_cpp_overview "Overview" * - \ref libopenmpt_cpp "Details" * - libopenmpt C * - \ref libopenmpt_c_overview "Overview" * - \ref libopenmpt_c "Details" * - libopenmpt_ext C++ * - \ref libopenmpt_ext_cpp_overview "Overview" * - \ref libopenmpt_ext_cpp "Details" * - libopenmpt_ext C * - \ref libopenmpt_ext_c_overview "Overview" * - \ref libopenmpt_ext_c "Details" * * \section toc_website Website * https://lib.openmpt.org/ * * \section toc_license License * \include LICENSE * */ libopenmpt-0.6.1+release.autotools/libopenmpt/dox/packaging.md0000644000175000017500000000350013234317352021466 00000000000000Packaging {#packaging} ========= Packaging --------- ### Packaging recommendations for distribution package maintainers * libopenmpt (since 0.3) uses SemVer 2.0.0 versioning. See [semver.org](https://semver.org/spec/v2.0.0.html). Clause 4 is ignored for libopenmpt, which means that libopenmpt will also provide API/ABI compatbility semantics for pre-1.0.0 versions as required by SemVer 2.0.0 only for post-1.0.0 versions. The SemVer versioning scheme is incompatible with Debian/Ubuntu package versions, however it can easily be processed to be compatible by replacing '-' (hyphen) with '~' (tilde). It is recommended that you use this exact transformation if required. * Use the autotools source package. * Use the default set of dependencies required by the autotools package. * Read \ref libopenmpt_c_staticlinking and thus possibly pass `CXXSTDLIB_PCLIBSPRIVATE` variable to `configure` if appropriate and/or desired. * Run the test suite in your build process. * Send any build system improvement patches upstream. * Do not include the libmodplug emulation layer in the default libopenmpt binary package. Either do not package it at all, or provide a separate package named libopenmpt-modplug or libmodplug-openmpt (or similar), which depends on libopenmpt, provides libmodplug, and conflicts with original libmodplug. * Split openmpt123 into its own binary package because it has more dependencies than libopenmpt itself. * Consider providing an additional openmpt123 package (in addition to the default openmpt123 package with all audio output drivers), built with fewer audio output drivers so it does not transitively depend on X11. Name that package and its executable openmpt123-nox (or whatever else may be common practice in your distribution). libopenmpt-0.6.1+release.autotools/libopenmpt/dox/tests.md0000644000175000017500000000155513135616236020717 00000000000000 Tests {#tests} ===== libopenmpt provides some basic unit tests that check the platform for general sanity and do some basic internal functionality testing. The test suite requires a special libopenmpt build that includes file saving functionality which is not included in normal builds. This is handled by all provided build systems automatically. ### Running Tests #### On Unix-like systems Compile with make $YOURMAKEOPTIONS clean make $YOURMAKEOPTIONS and run make $YOURMAKEOPTIONS check As the build system retains no state between make invocations, you have to provide your make options on every make invocation. #### Autotools-based build system ./configure make check #### On Windows Using Visual Studio 20??, compile build\vs20??\libopenmpt_test.sln and run bin\$ARCH\libopenmpt_test.exe from the root of the source tree. libopenmpt-0.6.1+release.autotools/libopenmpt/libopenmpt.h0000644000175000017500000026135414035630602020761 00000000000000/* * libopenmpt.h * ------------ * Purpose: libopenmpt public c interface * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #ifndef LIBOPENMPT_H #define LIBOPENMPT_H #include "libopenmpt_config.h" #include #include /*! * \page libopenmpt_c_overview C API * * \section libopenmpt_c_error Error Handling * * - Functions with no return value in the corresponding C++ API return 0 on * failure and 1 on success. * - Functions that return a string in the corresponding C++ API return a * dynamically allocated const char *. In case of failure or memory allocation * failure, a NULL pointer is returned. * - Functions that return integer values signal error condition by returning * an invalid value (-1 in most cases, 0 in some cases). * - All functions that work on an \ref openmpt_module object will call an * \ref openmpt_error_func and depending on the value returned by this function * log the error code and/xor/or store it inside the openmpt_module object. * Stored error codes can be accessed with the openmpt_module_error_get_last() * and openmpt_module_error_get_last_message(). Stored errors will not get * cleared automatically and should be reset with openmpt_module_error_clear(). * - Some functions not directly related to an \ref openmpt_module object take * an explicit \ref openmpt_error_func error function callback and a pointer to * an int and behave analog to the functions working on an \ref openmpt_module * object. * * \section libopenmpt_c_strings Strings * * - All strings returned from libopenmpt are encoded in UTF-8. * - All strings passed to libopenmpt should also be encoded in UTF-8. * Behaviour in case of invalid UTF-8 is unspecified. * - libopenmpt does not enforce or expect any particular Unicode * normalization form. * - All strings returned from libopenmpt are dynamically allocated and must * be freed with openmpt_free_string(). Do NOT use the C standard library * free() for libopenmpt strings as that would make your code invalid on * windows when dynamically linking against libopenmpt which itself statically * links to the C runtime. * - All strings passed to libopenmpt are copied. No ownership is assumed or * transferred. * * \section libopenmpt_c_fileio File I/O * * libopenmpt can use 3 different strategies for file I/O. * * - openmpt_module_create_from_memory2() will load the module from the provided * memory buffer, which will require loading all data upfront by the library * caller. * - openmpt_module_create2() with a seekable stream will load the module via * callbacks to the stream interface. libopenmpt will not implement an * additional buffering layer in this case which means the callbacks are assumed * to be performant even with small i/o sizes. * - openmpt_module_create2() with an unseekable stream will load the module via * callbacks to the stream interface. libopempt will make an internal copy as * it goes along, and sometimes have to pre-cache the whole file in case it * needs to know the complete file size. This strategy is intended to be used * if the file is located on a high latency network. * * | create function | speed | memory consumption | * | ----------------------------------------------: | :----: | :----------------: | * | openmpt_module_create_from_memory2() |

fast

|

medium

| * | openmpt_module_create2() with seekable stream |

slow

|

low

| * | openmpt_module_create2() with unseekable stream |

medium

|

high

| * * In all cases, the data or stream passed to the create function is no longer * needed after the openmpt_module has been created and can be freed by the * caller. * * \section libopenmpt_c_outputformat Output Format * * libopenmpt supports a wide range of PCM output formats: * [8000..192000]/[mono|stereo|quad]/[f32|i16]. * * Unless you have some very specific requirements demanding a particular aspect * of the output format, you should always prefer 48000/stereo/f32 as the * libopenmpt PCM format. * * - Please prefer 48000Hz unless the user explicitly demands something else. * Practically all audio equipment and file formats use 48000Hz nowadays. * - Practically all module formats are made for stereo output. Mono will not * give you any measurable speed improvements and can trivially be obtained from * the stereo output anyway. Quad is not expected by almost all modules and even * if they do use surround effects, they expect the effects to be mixed to * stereo. * - Floating point output provides headroom instead of hard clipping if the * module is louder than 0dBFs, will give you a better signal-to-noise ratio * than int16 output, and avoid the need to apply an additional dithering to the * output by libopenmpt. Unless your platform has no floating point unit at all, * floating point will thus also be slightly faster. * * \section libopenmpt_c_threads libopenmpt in multi-threaded environments * * - libopenmpt is thread-aware. * - Individual libopenmpt objects are not thread-safe. * - libopenmpt itself does not spawn any user-visible threads but may spawn * threads for internal use. * - You must ensure to only ever access a particular libopenmpt object from a * single thread at a time. * - Consecutive accesses can happen from different threads. * - Different objects can be accessed concurrently from different threads. * * \section libopenmpt_c_staticlinking Statically linking to libopenmpt * * libopenmpt is implemented in C++. This implies that linking to libopenmpt * statically requires linking to the C++ runtime and standard library. The * **highly preferred and recommended** way to do this is by using the C++ * compiler instead of the platform linker to do the linking. This will do all * necessary things that are C++ specific (in particular, it will pull in the * appropriate runtime and/or library). If for whatever reason it is not * possible to use the C++ compiler for statically linking against libopenmpt, * the libopenmpt build system can list the required libraries in the pkg-config * file `libopenmpt.pc`. However, there is no reliable way to determine the name * of the required library or libraries from within the build system. The * libopenmpt autotools `configure` and plain `Makefile` honor the custom * variable `CXXSTDLIB_PCLIBSPRIVATE` which serves the sole purpose of listing * the standard library (or libraries) required for static linking. The contents * of this variable will be put in `libopenmpt.pc` `Libs.private` and used for * nothing else. * * This problem is inherent to libraries implemented in C++ that can also be used * without a C++ compiler. Other libraries try to solve that by listing * `-lstdc++` unconditionally in `Libs.private`. However, that will break * platforms that use a different C++ standard library (in particular FreeBSD). * * See https://lists.freedesktop.org/archives/pkg-config/2016-August/001055.html . * * Dymically linking to libopenmpt does not require anything special and will * work as usual (and exactly as done for libraries implemented in C). * * Note: This section does not apply when using Microsoft Visual Studio or * Andriod NDK ndk-build build systems. * * \section libopenmpt_c_detailed Detailed documentation * * \ref libopenmpt_c * * In case a function is not documented here, you might want to look at the * \ref libopenmpt_cpp documentation. The C and C++ APIs are kept semantically * as close as possible. * * \section libopenmpt_c_examples Examples * * \subsection libopenmpt_c_example_unsafe Unsafe, simplified example without any error checking to get a first idea of the API * \include libopenmpt_example_c_unsafe.c * \subsection libopenmpt_c_example_file FILE* * \include libopenmpt_example_c.c * \subsection libopenmpt_c_example_inmemory in memory * \include libopenmpt_example_c_mem.c * \subsection libopenmpt_c_example_stdout reading FILE* and writing PCM data to STDOUT (usable without PortAudio) * \include libopenmpt_example_c_stdout.c * */ /*! \defgroup libopenmpt_c libopenmpt C */ /*! \addtogroup libopenmpt_c * @{ */ #ifdef __cplusplus extern "C" { #endif /*! \brief Get the libopenmpt version number * * Returns the libopenmpt version number. * \return The value represents (major << 24 + minor << 16 + patch << 0). * \remarks libopenmpt < 0.3.0-pre used the following scheme: (major << 24 + minor << 16 + revision). */ LIBOPENMPT_API uint32_t openmpt_get_library_version(void); /*! \brief Get the core version number * * Return the OpenMPT core version number. * \return The value represents (majormajor << 24 + major << 16 + minor << 8 + minorminor). */ LIBOPENMPT_API uint32_t openmpt_get_core_version(void); /*! Return a verbose library version string from openmpt_get_string(). \deprecated Please use `"library_version"` directly. */ #define OPENMPT_STRING_LIBRARY_VERSION LIBOPENMPT_DEPRECATED_STRING( "library_version" ) /*! Return a verbose library features string from openmpt_get_string(). \deprecated Please use `"library_features"` directly. */ #define OPENMPT_STRING_LIBRARY_FEATURES LIBOPENMPT_DEPRECATED_STRING( "library_features" ) /*! Return a verbose OpenMPT core version string from openmpt_get_string(). \deprecated Please use `"core_version"` directly. */ #define OPENMPT_STRING_CORE_VERSION LIBOPENMPT_DEPRECATED_STRING( "core_version" ) /*! Return information about the current build (e.g. the build date or compiler used) from openmpt_get_string(). \deprecated Please use `"build"` directly. */ #define OPENMPT_STRING_BUILD LIBOPENMPT_DEPRECATED_STRING( "build" ) /*! Return all contributors from openmpt_get_string(). \deprecated Please use `"credits"` directly. */ #define OPENMPT_STRING_CREDITS LIBOPENMPT_DEPRECATED_STRING( "credits" ) /*! Return contact information about libopenmpt from openmpt_get_string(). \deprecated Please use `"contact"` directly. */ #define OPENMPT_STRING_CONTACT LIBOPENMPT_DEPRECATED_STRING( "contact" ) /*! Return the libopenmpt license from openmpt_get_string(). \deprecated Please use `"license"` directly. */ #define OPENMPT_STRING_LICENSE LIBOPENMPT_DEPRECATED_STRING( "license" ) /*! \brief Free a string returned by libopenmpt * * Frees any string that got returned by libopenmpt. */ LIBOPENMPT_API void openmpt_free_string( const char * str ); /*! \brief Get library related metadata. * * \param key Key to query. * Possible keys are: * - "library_version": verbose library version string * - "library_version_is_release": "1" if the version is an officially released version * - "library_features": verbose library features string * - "core_version": verbose OpenMPT core version string * - "source_url": original source code URL * - "source_date": original source code date * - "source_revision": original source code revision * - "source_is_modified": "1" if the original source has been modified * - "source_has_mixed_revisions": "1" if the original source has been compiled from different various revision * - "source_is_package": "1" if the original source has been obtained from a source pacakge instead of source code version control * - "build": information about the current build (e.g. the build date or compiler used) * - "build_compiler": information about the compiler used to build libopenmpt * - "credits": all contributors * - "contact": contact information about libopenmpt * - "license": the libopenmpt license * - "url": libopenmpt website URL * - "support_forum_url": libopenmpt support and discussions forum URL * - "bugtracker_url": libopenmpt bug and issue tracker URL * \return A (possibly multi-line) string containing the queried information. If no information is available, the string is empty. */ LIBOPENMPT_API const char * openmpt_get_string( const char * key ); /*! \brief Get a list of supported file extensions * * \return The semicolon-separated list of extensions supported by this libopenmpt build. The extensions are returned lower-case without a leading dot. */ LIBOPENMPT_API const char * openmpt_get_supported_extensions(void); /*! \brief Query whether a file extension is supported * * \param extension file extension to query without a leading dot. The case is ignored. * \return 1 if the extension is supported by libopenmpt, 0 otherwise. */ LIBOPENMPT_API int openmpt_is_extension_supported( const char * extension ); /*! Seek to the given offset relative to the beginning of the file. */ #define OPENMPT_STREAM_SEEK_SET 0 /*! Seek to the given offset relative to the current position in the file. */ #define OPENMPT_STREAM_SEEK_CUR 1 /*! Seek to the given offset relative to the end of the file. */ #define OPENMPT_STREAM_SEEK_END 2 /*! \brief Read bytes from stream * * Read bytes data from stream to dst. * \param stream Stream to read data from * \param dst Target where to copy data. * \param bytes Number of bytes to read. * \return Number of bytes actually read and written to dst. * \retval 0 End of stream or error. * \remarks Short reads are allowed as long as they return at least 1 byte if EOF is not reached. */ typedef size_t (*openmpt_stream_read_func)( void * stream, void * dst, size_t bytes ); /*! \brief Seek stream position * * Seek to stream position offset at whence. * \param stream Stream to operate on. * \param offset Offset to seek to. * \param whence OPENMPT_STREAM_SEEK_SET, OPENMPT_STREAM_SEEK_CUR, OPENMPT_STREAM_SEEK_END. See C89 documentation. * \return Returns 0 on success. * \retval 0 Success. * \retval -1 Failure. Position does not get updated. * \remarks libopenmpt will not try to seek beyond the file size, thus it is not important whether you allow for virtual positioning after the file end, or return an error in that case. The position equal to the file size needs to be seekable to. */ typedef int (*openmpt_stream_seek_func)( void * stream, int64_t offset, int whence ); /*! \brief Tell stream position * * Tell position of stream. * \param stream Stream to operate on. * \return Current position in stream. * \retval -1 Failure. */ typedef int64_t (*openmpt_stream_tell_func)( void * stream ); /*! \brief Stream callbacks * * Stream callbacks used by libopenmpt for stream operations. * \sa openmpt_stream_get_file_callbacks * \sa openmpt_stream_get_fd_callbacks * \sa openmpt_stream_get_buffer_callbacks */ typedef struct openmpt_stream_callbacks { /*! \brief Read callback. * * \sa openmpt_stream_read_func */ openmpt_stream_read_func read; /*! \brief Seek callback. * * Seek callback can be NULL if seeking is not supported. * \sa openmpt_stream_seek_func */ openmpt_stream_seek_func seek; /*! \brief Tell callback. * * Tell callback can be NULL if seeking is not supported. * \sa openmpt_stream_tell_func */ openmpt_stream_tell_func tell; } openmpt_stream_callbacks; /*! \brief Logging function * * \param message UTF-8 encoded log message. * \param user User context that was passed to openmpt_module_create2(), openmpt_module_create_from_memory2() or openmpt_could_open_probability2(). */ typedef void (*openmpt_log_func)( const char * message, void * user ); /*! \brief Default logging function * * Default logging function that logs anything to stderr. */ LIBOPENMPT_API void openmpt_log_func_default( const char * message, void * user ); /*! \brief Silent logging function * * Silent logging function that throws any log message away. */ LIBOPENMPT_API void openmpt_log_func_silent( const char * message, void * user ); /*! No error. \since 0.3.0 */ #define OPENMPT_ERROR_OK 0 /*! Lowest value libopenmpt will use for any of its own error codes. \since 0.3.0 */ #define OPENMPT_ERROR_BASE 256 /*! Unknown internal error. \since 0.3.0 */ #define OPENMPT_ERROR_UNKNOWN ( OPENMPT_ERROR_BASE + 1 ) /*! Unknown internal C++ exception. \since 0.3.0 */ #define OPENMPT_ERROR_EXCEPTION ( OPENMPT_ERROR_BASE + 11 ) /*! Out of memory. \since 0.3.0 */ #define OPENMPT_ERROR_OUT_OF_MEMORY ( OPENMPT_ERROR_BASE + 21 ) /*! Runtime error. \since 0.3.0 */ #define OPENMPT_ERROR_RUNTIME ( OPENMPT_ERROR_BASE + 30 ) /*! Range error. \since 0.3.0 */ #define OPENMPT_ERROR_RANGE ( OPENMPT_ERROR_BASE + 31 ) /*! Arithmetic overflow. \since 0.3.0 */ #define OPENMPT_ERROR_OVERFLOW ( OPENMPT_ERROR_BASE + 32 ) /*! Arithmetic underflow. \since 0.3.0 */ #define OPENMPT_ERROR_UNDERFLOW ( OPENMPT_ERROR_BASE + 33 ) /*! Logic error. \since 0.3.0 */ #define OPENMPT_ERROR_LOGIC ( OPENMPT_ERROR_BASE + 40 ) /*! Value domain error. \since 0.3.0 */ #define OPENMPT_ERROR_DOMAIN ( OPENMPT_ERROR_BASE + 41 ) /*! Maximum supported size exceeded. \since 0.3.0 */ #define OPENMPT_ERROR_LENGTH ( OPENMPT_ERROR_BASE + 42 ) /*! Argument out of range. \since 0.3.0 */ #define OPENMPT_ERROR_OUT_OF_RANGE ( OPENMPT_ERROR_BASE + 43 ) /*! Invalid argument. \since 0.3.0 */ #define OPENMPT_ERROR_INVALID_ARGUMENT ( OPENMPT_ERROR_BASE + 44 ) /*! General libopenmpt error. \since 0.3.0 */ #define OPENMPT_ERROR_GENERAL ( OPENMPT_ERROR_BASE + 101 ) /*! openmpt_module * is invalid. \since 0.3.0 */ #define OPENMPT_ERROR_INVALID_MODULE_POINTER ( OPENMPT_ERROR_BASE + 102 ) /*! NULL pointer argument. \since 0.3.0 */ #define OPENMPT_ERROR_ARGUMENT_NULL_POINTER ( OPENMPT_ERROR_BASE + 103 ) /*! \brief Check whether the error is transient * * Checks whether an error code represents a transient error which may not occur again in a later try if for example memory has been freed up after an out-of-memory error. * \param error Error code. * \retval 0 Error is not transient. * \retval 1 Error is transient. * \sa OPENMPT_ERROR_OUT_OF_MEMORY * \since 0.3.0 */ LIBOPENMPT_API int openmpt_error_is_transient( int error ); /*! \brief Convert error code to text * * Converts an error code into a text string describing the error. * \param error Error code. * \return Allocated string describing the error. * \retval NULL Not enough memory to allocate the string. * \since 0.3.0 */ LIBOPENMPT_API const char * openmpt_error_string( int error ); /*! Do not log or store the error. \since 0.3.0 */ #define OPENMPT_ERROR_FUNC_RESULT_NONE 0 /*! Log the error. \since 0.3.0 */ #define OPENMPT_ERROR_FUNC_RESULT_LOG ( 1 << 0 ) /*! Store the error. \since 0.3.0 */ #define OPENMPT_ERROR_FUNC_RESULT_STORE ( 1 << 1 ) /*! Log and store the error. \since 0.3.0 */ #define OPENMPT_ERROR_FUNC_RESULT_DEFAULT ( OPENMPT_ERROR_FUNC_RESULT_LOG | OPENMPT_ERROR_FUNC_RESULT_STORE ) /*! \brief Error function * * \param error Error code. * \param user User context that was passed to openmpt_module_create2(), openmpt_module_create_from_memory2() or openmpt_could_open_probability2(). * \return Mask of OPENMPT_ERROR_FUNC_RESULT_LOG and OPENMPT_ERROR_FUNC_RESULT_STORE. * \retval OPENMPT_ERROR_FUNC_RESULT_NONE Do not log or store the error. * \retval OPENMPT_ERROR_FUNC_RESULT_LOG Log the error. * \retval OPENMPT_ERROR_FUNC_RESULT_STORE Store the error. * \retval OPENMPT_ERROR_FUNC_RESULT_DEFAULT Log and store the error. * \sa OPENMPT_ERROR_FUNC_RESULT_NONE * \sa OPENMPT_ERROR_FUNC_RESULT_LOG * \sa OPENMPT_ERROR_FUNC_RESULT_STORE * \sa OPENMPT_ERROR_FUNC_RESULT_DEFAULT * \sa openmpt_error_func_default * \sa openmpt_error_func_log * \sa openmpt_error_func_store * \sa openmpt_error_func_ignore * \sa openmpt_error_func_errno * \since 0.3.0 */ typedef int (*openmpt_error_func)( int error, void * user ); /*! \brief Default error function * * Causes all errors to be logged and stored. * \param error Error code. * \param user Ignored. * \retval OPENMPT_ERROR_FUNC_RESULT_DEFAULT Always. * \since 0.3.0 */ LIBOPENMPT_API int openmpt_error_func_default( int error, void * user ); /*! \brief Log error function * * Causes all errors to be logged. * \param error Error code. * \param user Ignored. * \retval OPENMPT_ERROR_FUNC_RESULT_LOG Always. * \since 0.3.0 */ LIBOPENMPT_API int openmpt_error_func_log( int error, void * user ); /*! \brief Store error function * * Causes all errors to be stored. * \param error Error code. * \param user Ignored. * \retval OPENMPT_ERROR_FUNC_RESULT_STORE Always. * \since 0.3.0 */ LIBOPENMPT_API int openmpt_error_func_store( int error, void * user ); /*! \brief Ignore error function * * Causes all errors to be neither logged nor stored. * \param error Error code. * \param user Ignored. * \retval OPENMPT_ERROR_FUNC_RESULT_NONE Always. * \since 0.3.0 */ LIBOPENMPT_API int openmpt_error_func_ignore( int error, void * user ); /*! \brief Errno error function * * Causes all errors to be stored in the pointer passed in as user. * \param error Error code. * \param user Pointer to an int as generated by openmpt_error_func_errno_userdata. * \retval OPENMPT_ERROR_FUNC_RESULT_NONE user is not NULL. * \retval OPENMPT_ERROR_FUNC_RESULT_DEFAULT user is NULL. * \since 0.3.0 */ LIBOPENMPT_API int openmpt_error_func_errno( int error, void * user ); /*! \brief User pointer for openmpt_error_func_errno * * Provides a suitable user pointer argument for openmpt_error_func_errno. * \param error Pointer to an integer value to be used as output by openmpt_error_func_errno. * \retval (void*)error. * \since 0.3.0 */ LIBOPENMPT_API void * openmpt_error_func_errno_userdata( int * error ); /*! \brief Roughly scan the input stream to find out whether libopenmpt might be able to open it * * \param stream_callbacks Input stream callback operations. * \param stream Input stream to scan. * \param effort Effort to make when validating stream. Effort 0.0 does not even look at stream at all and effort 1.0 completely loads the file from stream. A lower effort requires less data to be loaded but only gives a rough estimate answer. Use an effort of 0.25 to only verify the header data of the module file. * \param logfunc Logging function where warning and errors are written. May be NULL. * \param user Logging function user context. Used to pass any user-defined data associated with this module to the logging function. * \return Probability between 0.0 and 1.0. * \remarks openmpt_could_open_probability() can return any value between 0.0 and 1.0. Only 0.0 and 1.0 are definitive answers, all values in between are just estimates. In general, any return value >0.0 means that you should try loading the file, and any value below 1.0 means that loading may fail. If you want a threshold above which you can be reasonably sure that libopenmpt will be able to load the file, use >=0.5. If you see the need for a threshold below which you could reasonably outright reject a file, use <0.25 (Note: Such a threshold for rejecting on the lower end is not recommended, but may be required for better integration into some other framework's probe scoring.). * \remarks openmpt_could_open_probability() expects the complete file data to be eventually available to it, even if it is asked to just parse the header. Verification will be unreliable (both false positives and false negatives), if you pretend that the file is just some few bytes of initial data threshold in size. In order to really just access the first bytes of a file, check in your callback functions whether data or seeking is requested beyond your initial data threshold, and in that case, return an error. openmpt_could_open_probability() will treat this as any other I/O error and return 0.0. You must not expect the correct result in this case. You instead must remember that it asked for more data than you currently want to provide to it and treat this situation as if openmpt_could_open_probability() returned 0.5. * \sa \ref libopenmpt_c_fileio * \sa openmpt_stream_callbacks * \deprecated Please use openmpt_could_open_probability2(). * \since 0.3.0 */ LIBOPENMPT_API LIBOPENMPT_DEPRECATED double openmpt_could_open_probability( openmpt_stream_callbacks stream_callbacks, void * stream, double effort, openmpt_log_func logfunc, void * user ); /*! \brief Roughly scan the input stream to find out whether libopenmpt might be able to open it * * \param stream_callbacks Input stream callback operations. * \param stream Input stream to scan. * \param effort Effort to make when validating stream. Effort 0.0 does not even look at stream at all and effort 1.0 completely loads the file from stream. A lower effort requires less data to be loaded but only gives a rough estimate answer. Use an effort of 0.25 to only verify the header data of the module file. * \param logfunc Logging function where warning and errors are written. May be NULL. * \param user Logging function user context. Used to pass any user-defined data associated with this module to the logging function. * \return Probability between 0.0 and 1.0. * \remarks openmpt_could_open_probability() can return any value between 0.0 and 1.0. Only 0.0 and 1.0 are definitive answers, all values in between are just estimates. In general, any return value >0.0 means that you should try loading the file, and any value below 1.0 means that loading may fail. If you want a threshold above which you can be reasonably sure that libopenmpt will be able to load the file, use >=0.5. If you see the need for a threshold below which you could reasonably outright reject a file, use <0.25 (Note: Such a threshold for rejecting on the lower end is not recommended, but may be required for better integration into some other framework's probe scoring.). * \remarks openmpt_could_open_probability() expects the complete file data to be eventually available to it, even if it is asked to just parse the header. Verification will be unreliable (both false positives and false negatives), if you pretend that the file is just some few bytes of initial data threshold in size. In order to really just access the first bytes of a file, check in your callback functions whether data or seeking is requested beyond your initial data threshold, and in that case, return an error. openmpt_could_open_probability() will treat this as any other I/O error and return 0.0. You must not expect the correct result in this case. You instead must remember that it asked for more data than you currently want to provide to it and treat this situation as if openmpt_could_open_probability() returned 0.5. * \sa \ref libopenmpt_c_fileio * \sa openmpt_stream_callbacks * \deprecated Please use openmpt_could_open_probability2(). */ LIBOPENMPT_API LIBOPENMPT_DEPRECATED double openmpt_could_open_propability( openmpt_stream_callbacks stream_callbacks, void * stream, double effort, openmpt_log_func logfunc, void * user ); /*! \brief Roughly scan the input stream to find out whether libopenmpt might be able to open it * * \param stream_callbacks Input stream callback operations. * \param stream Input stream to scan. * \param effort Effort to make when validating stream. Effort 0.0 does not even look at stream at all and effort 1.0 completely loads the file from stream. A lower effort requires less data to be loaded but only gives a rough estimate answer. Use an effort of 0.25 to only verify the header data of the module file. * \param logfunc Logging function where warning and errors are written. May be NULL. * \param loguser Logging function user context. Used to pass any user-defined data associated with this module to the logging function. * \param errfunc Error function to define error behaviour. May be NULL. * \param erruser Error function user context. Used to pass any user-defined data associated with this module to the logging function. * \param error Pointer to an integer where an error may get stored. May be NULL. * \param error_message Pointer to a string pointer where an error message may get stored. May be NULL. * \return Probability between 0.0 and 1.0. * \remarks openmpt_probe_file_header() or openmpt_probe_file_header_without_filesize() provide a simpler and faster interface that fits almost all use cases better. It is recommended to use openmpt_probe_file_header() or openmpt_probe_file_header_without_filesize() instead of openmpt_could_open_probability(). * \remarks openmpt_could_open_probability2() can return any value between 0.0 and 1.0. Only 0.0 and 1.0 are definitive answers, all values in between are just estimates. In general, any return value >0.0 means that you should try loading the file, and any value below 1.0 means that loading may fail. If you want a threshold above which you can be reasonably sure that libopenmpt will be able to load the file, use >=0.5. If you see the need for a threshold below which you could reasonably outright reject a file, use <0.25 (Note: Such a threshold for rejecting on the lower end is not recommended, but may be required for better integration into some other framework's probe scoring.). * \remarks openmpt_could_open_probability2() expects the complete file data to be eventually available to it, even if it is asked to just parse the header. Verification will be unreliable (both false positives and false negatives), if you pretend that the file is just some few bytes of initial data threshold in size. In order to really just access the first bytes of a file, check in your callback functions whether data or seeking is requested beyond your initial data threshold, and in that case, return an error. openmpt_could_open_probability2() will treat this as any other I/O error and return 0.0. You must not expect the correct result in this case. You instead must remember that it asked for more data than you currently want to provide to it and treat this situation as if openmpt_could_open_probability2() returned 0.5. \include libopenmpt_example_c_probe.c * \sa \ref libopenmpt_c_fileio * \sa openmpt_stream_callbacks * \sa openmpt_probe_file_header * \sa openmpt_probe_file_header_without_filesize * \since 0.3.0 */ LIBOPENMPT_API double openmpt_could_open_probability2( openmpt_stream_callbacks stream_callbacks, void * stream, double effort, openmpt_log_func logfunc, void * loguser, openmpt_error_func errfunc, void * erruser, int * error, const char * * error_message ); /*! \brief Get recommended header size for successfull format probing * * \sa openmpt_probe_file_header() * \sa openmpt_probe_file_header_without_filesize() * \since 0.3.0 */ LIBOPENMPT_API size_t openmpt_probe_file_header_get_recommended_size(void); /*! Probe for module formats in openmpt_probe_file_header() or openmpt_probe_file_header_without_filesize(). \since 0.3.0 */ #define OPENMPT_PROBE_FILE_HEADER_FLAGS_MODULES 0x1ull /*! Probe for module-specific container formats in openmpt_probe_file_header() or openmpt_probe_file_header_without_filesize(). \since 0.3.0 */ #define OPENMPT_PROBE_FILE_HEADER_FLAGS_CONTAINERS 0x2ull /*! Probe for the default set of formats in openmpt_probe_file_header() or openmpt_probe_file_header_without_filesize(). \since 0.3.0 */ #define OPENMPT_PROBE_FILE_HEADER_FLAGS_DEFAULT ( OPENMPT_PROBE_FILE_HEADER_FLAGS_MODULES | OPENMPT_PROBE_FILE_HEADER_FLAGS_CONTAINERS ) /*! Probe for no formats in openmpt_probe_file_header() or openmpt_probe_file_header_without_filesize(). \since 0.3.0 */ #define OPENMPT_PROBE_FILE_HEADER_FLAGS_NONE 0x0ull /*! Possible return values fo openmpt_probe_file_header() and openmpt_probe_file_header_without_filesize(). \since 0.3.0 */ #define OPENMPT_PROBE_FILE_HEADER_RESULT_SUCCESS 1 /*! Possible return values fo openmpt_probe_file_header() and openmpt_probe_file_header_without_filesize(). \since 0.3.0 */ #define OPENMPT_PROBE_FILE_HEADER_RESULT_FAILURE 0 /*! Possible return values fo openmpt_probe_file_header() and openmpt_probe_file_header_without_filesize(). \since 0.3.0 */ #define OPENMPT_PROBE_FILE_HEADER_RESULT_WANTMOREDATA (-1) /*! Possible return values fo openmpt_probe_file_header() and openmpt_probe_file_header_without_filesize(). \since 0.3.0 */ #define OPENMPT_PROBE_FILE_HEADER_RESULT_ERROR (-255) /*! \brief Probe the provided bytes from the beginning of a file for supported file format headers to find out whether libopenmpt might be able to open it * * \param flags Bit mask of OPENMPT_PROBE_FILE_HEADER_FLAGS_MODULES and OPENMPT_PROBE_FILE_HEADER_FLAGS_CONTAINERS, or OPENMPT_PROBE_FILE_HEADER_FLAGS_DEFAULT. * \param data Beginning of the file data. * \param size Size of the beginning of the file data. * \param filesize Full size of the file data on disk. * \param logfunc Logging function where warning and errors are written. May be NULL. * \param loguser Logging function user context. Used to pass any user-defined data associated with this module to the logging function. * \param errfunc Error function to define error behaviour. May be NULL. * \param erruser Error function user context. Used to pass any user-defined data associated with this module to the logging function. * \param error Pointer to an integer where an error may get stored. May be NULL. * \param error_message Pointer to a string pointer where an error message may get stored. May be NULL. * \remarks It is recommended to provide openmpt_probe_file_header_get_recommended_size() bytes of data for data and size. If the file is smaller, only provide the filesize amount and set size and filesize to the file's size. * \remarks openmpt_could_open_probability2() provides a more elaborate interface that might be required for special use cases. It is recommended to use openmpt_probe_file_header() though, if possible. * \retval OPENMPT_PROBE_FILE_HEADER_RESULT_SUCCESS The file will most likely be supported by libopenmpt. * \retval OPENMPT_PROBE_FILE_HEADER_RESULT_FAILURE The file is not supported by libopenmpt. * \retval OPENMPT_PROBE_FILE_HEADER_RESULT_WANTMOREDATA An answer could not be determined with the amount of data provided. * \retval OPENMPT_PROBE_FILE_HEADER_RESULT_ERROR An internal error occurred. * \sa openmpt_probe_file_header_get_recommended_size() * \sa openmpt_probe_file_header_without_filesize() * \sa openmpt_probe_file_header_from_stream() * \sa openmpt_could_open_probability2() * \since 0.3.0 */ LIBOPENMPT_API int openmpt_probe_file_header( uint64_t flags, const void * data, size_t size, uint64_t filesize, openmpt_log_func logfunc, void * loguser, openmpt_error_func errfunc, void * erruser, int * error, const char * * error_message ); /*! \brief Probe the provided bytes from the beginning of a file for supported file format headers to find out whether libopenmpt might be able to open it * * \param flags Bit mask of OPENMPT_PROBE_FILE_HEADER_FLAGS_MODULES and OPENMPT_PROBE_FILE_HEADER_FLAGS_CONTAINERS, or OPENMPT_PROBE_FILE_HEADER_FLAGS_DEFAULT. * \param data Beginning of the file data. * \param size Size of the beginning of the file data. * \param logfunc Logging function where warning and errors are written. May be NULL. * \param loguser Logging function user context. Used to pass any user-defined data associated with this module to the logging function. * \param errfunc Error function to define error behaviour. May be NULL. * \param erruser Error function user context. Used to pass any user-defined data associated with this module to the logging function. * \param error Pointer to an integer where an error may get stored. May be NULL. * \param error_message Pointer to a string pointer where an error message may get stored. May be NULL. * \remarks It is recommended to use openmpt_probe_file_header() and provide the acutal file's size as a parameter if at all possible. libopenmpt can provide more accurate answers if the filesize is known. * \remarks It is recommended to provide openmpt_probe_file_header_get_recommended_size() bytes of data for data and size. If the file is smaller, only provide the filesize amount and set size to the file's size. * \remarks openmpt_could_open_probability2() provides a more elaborate interface that might be required for special use cases. It is recommended to use openmpt_probe_file_header() though, if possible. * \retval OPENMPT_PROBE_FILE_HEADER_RESULT_SUCCESS The file will most likely be supported by libopenmpt. * \retval OPENMPT_PROBE_FILE_HEADER_RESULT_FAILURE The file is not supported by libopenmpt. * \retval OPENMPT_PROBE_FILE_HEADER_RESULT_WANTMOREDATA An answer could not be determined with the amount of data provided. * \retval OPENMPT_PROBE_FILE_HEADER_RESULT_ERROR An internal error occurred. * \sa openmpt_probe_file_header_get_recommended_size() * \sa openmpt_probe_file_header() * \sa openmpt_probe_file_header_from_stream() * \sa openmpt_could_open_probability2() * \since 0.3.0 */ LIBOPENMPT_API int openmpt_probe_file_header_without_filesize( uint64_t flags, const void * data, size_t size, openmpt_log_func logfunc, void * loguser, openmpt_error_func errfunc, void * erruser, int * error, const char * * error_message ); /*! \brief Probe the provided bytes from the beginning of a file for supported file format headers to find out whether libopenmpt might be able to open it * * \param flags Bit mask of OPENMPT_PROBE_FILE_HEADER_FLAGS_MODULES and OPENMPT_PROBE_FILE_HEADER_FLAGS_CONTAINERS, or OPENMPT_PROBE_FILE_HEADER_FLAGS_DEFAULT. * \param stream_callbacks Input stream callback operations. * \param stream Input stream to scan. * \param logfunc Logging function where warning and errors are written. May be NULL. * \param loguser Logging function user context. Used to pass any user-defined data associated with this module to the logging function. * \param errfunc Error function to define error behaviour. May be NULL. * \param erruser Error function user context. Used to pass any user-defined data associated with this module to the logging function. * \param error Pointer to an integer where an error may get stored. May be NULL. * \param error_message Pointer to a string pointer where an error message may get stored. May be NULL. * \remarks The stream is left in an unspecified state when this function returns. * \remarks It is recommended to provide openmpt_probe_file_header_get_recommended_size() bytes of data for data and size. If the file is smaller, only provide the filesize amount and set size and filesize to the file's size. * \remarks openmpt_could_open_probability2() provides a more elaborate interface that might be required for special use cases. It is recommended to use openmpt_probe_file_header() though, if possible. * \retval OPENMPT_PROBE_FILE_HEADER_RESULT_SUCCESS The file will most likely be supported by libopenmpt. * \retval OPENMPT_PROBE_FILE_HEADER_RESULT_FAILURE The file is not supported by libopenmpt. * \retval OPENMPT_PROBE_FILE_HEADER_RESULT_WANTMOREDATA An answer could not be determined with the amount of data provided. * \retval OPENMPT_PROBE_FILE_HEADER_RESULT_ERROR An internal error occurred. * \sa openmpt_probe_file_header_get_recommended_size() * \sa openmpt_probe_file_header() * \sa openmpt_probe_file_header_without_filesize() * \sa openmpt_could_open_probability2() * \since 0.3.0 */ LIBOPENMPT_API int openmpt_probe_file_header_from_stream( uint64_t flags, openmpt_stream_callbacks stream_callbacks, void * stream, openmpt_log_func logfunc, void * loguser, openmpt_error_func errfunc, void * erruser, int * error, const char * * error_message ); /*! \brief Opaque type representing a libopenmpt module */ typedef struct openmpt_module openmpt_module; typedef struct openmpt_module_initial_ctl { const char * ctl; const char * value; } openmpt_module_initial_ctl; /*! \brief Construct an openmpt_module * * \param stream_callbacks Input stream callback operations. * \param stream Input stream to load the module from. * \param logfunc Logging function where warning and errors are written. The logging function may be called throughout the lifetime of openmpt_module. May be NULL. * \param loguser User-defined data associated with this module. This value will be passed to the logging callback function (logfunc) * \param ctls A map of initial ctl values. See openmpt_module_get_ctls() * \return A pointer to the constructed openmpt_module, or NULL on failure. * \remarks The input data can be discarded after an openmpt_module has been constructed successfully. * \sa openmpt_stream_callbacks * \sa \ref libopenmpt_c_fileio * \deprecated Please use openmpt_module_create2(). */ LIBOPENMPT_API LIBOPENMPT_DEPRECATED openmpt_module * openmpt_module_create( openmpt_stream_callbacks stream_callbacks, void * stream, openmpt_log_func logfunc, void * loguser, const openmpt_module_initial_ctl * ctls ); /*! \brief Construct an openmpt_module * * \param stream_callbacks Input stream callback operations. * \param stream Input stream to load the module from. * \param logfunc Logging function where warning and errors are written. The logging function may be called throughout the lifetime of openmpt_module. May be NULL. * \param loguser User-defined data associated with this module. This value will be passed to the logging callback function (logfunc) * \param errfunc Error function to define error behaviour. May be NULL. * \param erruser Error function user context. Used to pass any user-defined data associated with this module to the logging function. * \param error Pointer to an integer where an error may get stored. May be NULL. * \param error_message Pointer to a string pointer where an error message may get stored. May be NULL. * \param ctls A map of initial ctl values. See openmpt_module_get_ctls() * \return A pointer to the constructed openmpt_module, or NULL on failure. * \remarks The input data can be discarded after an openmpt_module has been constructed successfully. * \sa openmpt_stream_callbacks * \sa \ref libopenmpt_c_fileio * \since 0.3.0 */ LIBOPENMPT_API openmpt_module * openmpt_module_create2( openmpt_stream_callbacks stream_callbacks, void * stream, openmpt_log_func logfunc, void * loguser, openmpt_error_func errfunc, void * erruser, int * error, const char * * error_message, const openmpt_module_initial_ctl * ctls ); /*! \brief Construct an openmpt_module * * \param filedata Data to load the module from. * \param filesize Amount of data available. * \param logfunc Logging function where warning and errors are written. The logging function may be called throughout the lifetime of openmpt_module. * \param loguser User-defined data associated with this module. This value will be passed to the logging callback function (logfunc) * \param ctls A map of initial ctl values. See openmpt_module_get_ctls() * \return A pointer to the constructed openmpt_module, or NULL on failure. * \remarks The input data can be discarded after an openmpt_module has been constructed successfully. * \sa \ref libopenmpt_c_fileio * \deprecated Please use openmpt_module_create_from_memory2(). */ LIBOPENMPT_API LIBOPENMPT_DEPRECATED openmpt_module * openmpt_module_create_from_memory( const void * filedata, size_t filesize, openmpt_log_func logfunc, void * loguser, const openmpt_module_initial_ctl * ctls ); /*! \brief Construct an openmpt_module * * \param filedata Data to load the module from. * \param filesize Amount of data available. * \param logfunc Logging function where warning and errors are written. The logging function may be called throughout the lifetime of openmpt_module. * \param loguser User-defined data associated with this module. This value will be passed to the logging callback function (logfunc) * \param errfunc Error function to define error behaviour. May be NULL. * \param erruser Error function user context. Used to pass any user-defined data associated with this module to the logging function. * \param error Pointer to an integer where an error may get stored. May be NULL. * \param error_message Pointer to a string pointer where an error message may get stored. May be NULL. * \param ctls A map of initial ctl values. See openmpt_module_get_ctls() * \return A pointer to the constructed openmpt_module, or NULL on failure. * \remarks The input data can be discarded after an openmpt_module has been constructed successfully. * \sa \ref libopenmpt_c_fileio * \since 0.3.0 */ LIBOPENMPT_API openmpt_module * openmpt_module_create_from_memory2( const void * filedata, size_t filesize, openmpt_log_func logfunc, void * loguser, openmpt_error_func errfunc, void * erruser, int * error, const char * * error_message, const openmpt_module_initial_ctl * ctls ); /*! \brief Unload a previously created openmpt_module from memory. * * \param mod The module to unload. */ LIBOPENMPT_API void openmpt_module_destroy( openmpt_module * mod ); /*! \brief Set logging function. * * Set the logging function of an already constructed openmpt_module. * \param mod The module handle to work on. * \param logfunc Logging function where warning and errors are written. The logging function may be called throughout the lifetime of openmpt_module. * \param loguser User-defined data associated with this module. This value will be passed to the logging callback function (logfunc) * \since 0.3.0 */ LIBOPENMPT_API void openmpt_module_set_log_func( openmpt_module * mod, openmpt_log_func logfunc, void * loguser ); /*! \brief Set error function. * * Set the error function of an already constructed openmpt_module. * \param mod The module handle to work on. * \param errfunc Error function to define error behaviour. May be NULL. * \param erruser Error function user context. * \since 0.3.0 */ LIBOPENMPT_API void openmpt_module_set_error_func( openmpt_module * mod, openmpt_error_func errfunc, void * erruser ); /*! \brief Get last error. * * Return the error currently stored in an openmpt_module. The stored error is not cleared. * \param mod The module handle to work on. * \return The error currently stored. * \sa openmpt_module_error_get_last_message * \sa openmpt_module_error_set_last * \sa openmpt_module_error_clear * \since 0.3.0 */ LIBOPENMPT_API int openmpt_module_error_get_last( openmpt_module * mod ); /*! \brief Get last error message. * * Return the error message currently stored in an openmpt_module. The stored error is not cleared. * \param mod The module handle to work on. * \return The error message currently stored. * \sa openmpt_module_error_set_last * \sa openmpt_module_error_clear * \since 0.3.0 */ LIBOPENMPT_API const char * openmpt_module_error_get_last_message( openmpt_module * mod ); /*! \brief Set last error. * * Set the error currently stored in an openmpt_module. * \param mod The module handle to work on. * \param error Error to be stored. * \sa openmpt_module_error_get_last * \sa openmpt_module_error_clear * \since 0.3.0 */ LIBOPENMPT_API void openmpt_module_error_set_last( openmpt_module * mod, int error ); /*! \brief Clear last error. * * Set the error currently stored in an openmpt_module to OPPENMPT_ERROR_OK. * \param mod The module handle to work on. * \sa openmpt_module_error_get_last * \sa openmpt_module_error_set_last * \since 0.3.0 */ LIBOPENMPT_API void openmpt_module_error_clear( openmpt_module * mod ); /** * \defgroup openmpt_module_render_param Render param indices * * \brief Parameter index to use with openmpt_module_get_render_param() and openmpt_module_set_render_param() * @{ */ /*! \brief Master Gain * * The related value represents a relative gain in milliBel.\n * The default value is 0.\n * The supported value range is unlimited.\n */ #define OPENMPT_MODULE_RENDER_MASTERGAIN_MILLIBEL 1 /*! \brief Stereo Separation * * The related value represents the stereo separation generated by the libopenmpt mixer in percent.\n * The default value is 100.\n * The supported value range is [0,200].\n */ #define OPENMPT_MODULE_RENDER_STEREOSEPARATION_PERCENT 2 /*! \brief Interpolation Filter * * The related value represents the interpolation filter length used by the libopenmpt mixer.\n * The default value is 0, which indicates a recommended default value.\n * The supported value range is [0,inf). Values greater than the implementation limit are clamped to the maximum supported value.\n * Currently supported values: * - 0: internal default * - 1: no interpolation (zero order hold) * - 2: linear interpolation * - 4: cubic interpolation * - 8: windowed sinc with 8 taps */ #define OPENMPT_MODULE_RENDER_INTERPOLATIONFILTER_LENGTH 3 /*! \brief Volume Ramping Strength * * The related value represents the amount of volume ramping done by the libopenmpt mixer.\n * The default value is -1, which indicates a recommended default value.\n * The meaningful value range is [-1..10].\n * A value of 0 completely disables volume ramping. This might cause clicks in sound output.\n * Higher values imply slower/softer volume ramps. */ #define OPENMPT_MODULE_RENDER_VOLUMERAMPING_STRENGTH 4 /** @}*/ /** * \defgroup openmpt_module_command_index Pattern cell indices * * \brief Parameter index to use with openmpt_module_get_pattern_row_channel_command(), openmpt_module_format_pattern_row_channel_command() and openmpt_module_highlight_pattern_row_channel_command() * @{ */ #define OPENMPT_MODULE_COMMAND_NOTE 0 #define OPENMPT_MODULE_COMMAND_INSTRUMENT 1 #define OPENMPT_MODULE_COMMAND_VOLUMEEFFECT 2 #define OPENMPT_MODULE_COMMAND_EFFECT 3 #define OPENMPT_MODULE_COMMAND_VOLUME 4 #define OPENMPT_MODULE_COMMAND_PARAMETER 5 /** @}*/ /*! \brief Select a sub-song from a multi-song module * * \param mod The module handle to work on. * \param subsong Index of the sub-song. -1 plays all sub-songs consecutively. * \return 1 on success, 0 on failure. * \sa openmpt_module_get_num_subsongs, openmpt_module_get_selected_subsong, openmpt_module_get_subsong_name * \remarks Whether subsong -1 (all subsongs consecutively), subsong 0 or some other subsong is selected by default, is an implementation detail and subject to change. If you do not want to care about subsongs, it is recommended to just not call openmpt_module_select_subsong() at all. */ LIBOPENMPT_API int openmpt_module_select_subsong( openmpt_module * mod, int32_t subsong ); /*! \brief Get currently selected sub-song from a multi-song module * * \param mod The module handle to work on. * \return Currently selected sub-song. -1 for all subsongs consecutively, 0 or greater for the current sub-song index. * \sa openmpt_module_get_num_subsongs, openmpt_module_select_subsong, openmpt_module_get_subsong_name * \since 0.3.0 */ LIBOPENMPT_API int32_t openmpt_module_get_selected_subsong( openmpt_module * mod ); /*! \brief Set Repeat Count * * \param mod The module handle to work on. * \param repeat_count Repeat Count * - -1: repeat forever * - 0: play once, repeat zero times (the default) * - n>0: play once and repeat n times after that * \return 1 on success, 0 on failure. * \sa openmpt_module_get_repeat_count */ LIBOPENMPT_API int openmpt_module_set_repeat_count( openmpt_module * mod, int32_t repeat_count ); /*! \brief Get Repeat Count * * \param mod The module handle to work on. * \return Repeat Count * - -1: repeat forever * - 0: play once, repeat zero times (the default) * - n>0: play once and repeat n times after that * \sa openmpt_module_set_repeat_count */ LIBOPENMPT_API int32_t openmpt_module_get_repeat_count( openmpt_module * mod ); /*! \brief approximate song duration * * \param mod The module handle to work on. * \return Approximate duration of current sub-song in seconds. * \remarks The function may return infinity if the pattern data is too complex to evaluate. */ LIBOPENMPT_API double openmpt_module_get_duration_seconds( openmpt_module * mod ); /*! \brief Set approximate current song position * * \param mod The module handle to work on. * \param seconds Seconds to seek to. If seconds is out of range, the position gets set to song start or end respectively. * \return Approximate new song position in seconds. * \sa openmpt_module_get_position_seconds */ LIBOPENMPT_API double openmpt_module_set_position_seconds( openmpt_module * mod, double seconds ); /*! \brief Get current song position * * \param mod The module handle to work on. * \return Current song position in seconds. * \sa openmpt_module_set_position_seconds */ LIBOPENMPT_API double openmpt_module_get_position_seconds( openmpt_module * mod ); /*! \brief Set approximate current song position * * If order or row are out of range, to position is not modified and the current position is returned. * \param mod The module handle to work on. * \param order Pattern order number to seek to. * \param row Pattern row number to seek to. * \return Approximate new song position in seconds. * \sa openmpt_module_set_position_seconds * \sa openmpt_module_get_position_seconds */ LIBOPENMPT_API double openmpt_module_set_position_order_row( openmpt_module * mod, int32_t order, int32_t row ); /*! \brief Get render parameter * * \param mod The module handle to work on. * \param param Parameter to query. See \ref openmpt_module_render_param * \param value Pointer to the variable that receives the current value of the parameter. * \return 1 on success, 0 on failure (invalid param or value is NULL). * \sa OPENMPT_MODULE_RENDER_MASTERGAIN_MILLIBEL * \sa OPENMPT_MODULE_RENDER_STEREOSEPARATION_PERCENT * \sa OPENMPT_MODULE_RENDER_INTERPOLATIONFILTER_LENGTH * \sa OPENMPT_MODULE_RENDER_VOLUMERAMPING_STRENGTH * \sa openmpt_module_set_render_param */ LIBOPENMPT_API int openmpt_module_get_render_param( openmpt_module * mod, int param, int32_t * value ); /*! \brief Set render parameter * * \param mod The module handle to work on. * \param param Parameter to set. See \ref openmpt_module_render_param * \param value The value to set param to. * \return 1 on success, 0 on failure (invalid param). * \sa OPENMPT_MODULE_RENDER_MASTERGAIN_MILLIBEL * \sa OPENMPT_MODULE_RENDER_STEREOSEPARATION_PERCENT * \sa OPENMPT_MODULE_RENDER_INTERPOLATIONFILTER_LENGTH * \sa OPENMPT_MODULE_RENDER_VOLUMERAMPING_STRENGTH * \sa openmpt_module_get_render_param */ LIBOPENMPT_API int openmpt_module_set_render_param( openmpt_module * mod, int param, int32_t value ); /*@{*/ /*! \brief Render audio data * * \param mod The module handle to work on. * \param samplerate Sample rate to render output. Should be in [8000,192000], but this is not enforced. * \param count Number of audio frames to render per channel. * \param mono Pointer to a buffer of at least count elements that receives the mono/center output. * \return The number of frames actually rendered. * \retval 0 The end of song has been reached. * \remarks The output buffers are only written to up to the returned number of elements. * \remarks You can freely switch between any of the "openmpt_module_read*" variants if you see a need to do so. libopenmpt tries to introduce as little switching annoyances as possible. Normally, you would only use a single one of these functions for rendering a particular module. * \remarks It is recommended to use the floating point API because of the greater dynamic range and no implied clipping. * \sa \ref libopenmpt_c_outputformat */ LIBOPENMPT_API size_t openmpt_module_read_mono( openmpt_module * mod, int32_t samplerate, size_t count, int16_t * mono ); /*! \brief Render audio data * * \param mod The module handle to work on. * \param samplerate Sample rate to render output. Should be in [8000,192000], but this is not enforced. * \param count Number of audio frames to render per channel. * \param left Pointer to a buffer of at least count elements that receives the left output. * \param right Pointer to a buffer of at least count elements that receives the right output. * \return The number of frames actually rendered. * \retval 0 The end of song has been reached. * \remarks The output buffers are only written to up to the returned number of elements. * \remarks You can freely switch between any of the "openmpt_module_read*" variants if you see a need to do so. libopenmpt tries to introduce as little switching annoyances as possible. Normally, you would only use a single one of these functions for rendering a particular module. * \remarks It is recommended to use the floating point API because of the greater dynamic range and no implied clipping. * \sa \ref libopenmpt_c_outputformat */ LIBOPENMPT_API size_t openmpt_module_read_stereo( openmpt_module * mod, int32_t samplerate, size_t count, int16_t * left, int16_t * right ); /*! \brief Render audio data * * \param mod The module handle to work on. * \param samplerate Sample rate to render output. Should be in [8000,192000], but this is not enforced. * \param count Number of audio frames to render per channel. * \param left Pointer to a buffer of at least count elements that receives the left output. * \param right Pointer to a buffer of at least count elements that receives the right output. * \param rear_left Pointer to a buffer of at least count elements that receives the rear left output. * \param rear_right Pointer to a buffer of at least count elements that receives the rear right output. * \return The number of frames actually rendered. * \retval 0 The end of song has been reached. * \remarks The output buffers are only written to up to the returned number of elements. * \remarks You can freely switch between any of the "openmpt_module_read*" variants if you see a need to do so. libopenmpt tries to introduce as little switching annoyances as possible. Normally, you would only use a single one of these functions for rendering a particular module. * \remarks It is recommended to use the floating point API because of the greater dynamic range and no implied clipping. * \sa \ref libopenmpt_c_outputformat */ LIBOPENMPT_API size_t openmpt_module_read_quad( openmpt_module * mod, int32_t samplerate, size_t count, int16_t * left, int16_t * right, int16_t * rear_left, int16_t * rear_right ); /*! \brief Render audio data * * \param mod The module handle to work on. * \param samplerate Sample rate to render output. Should be in [8000,192000], but this is not enforced. * \param count Number of audio frames to render per channel. * \param mono Pointer to a buffer of at least count elements that receives the mono/center output. * \return The number of frames actually rendered. * \retval 0 The end of song has been reached. * \remarks The output buffers are only written to up to the returned number of elements. * \remarks You can freely switch between any of the "openmpt_module_read*" variants if you see a need to do so. libopenmpt tries to introduce as little switching annoyances as possible. Normally, you would only use a single one of these functions for rendering a particular module. * \remarks Floating point samples are in the [-1.0..1.0] nominal range. They are not clipped to that range though and thus might overshoot. * \sa \ref libopenmpt_c_outputformat */ LIBOPENMPT_API size_t openmpt_module_read_float_mono( openmpt_module * mod, int32_t samplerate, size_t count, float * mono ); /*! \brief Render audio data * * \param mod The module handle to work on. * \param samplerate Sample rate to render output. Should be in [8000,192000], but this is not enforced. * \param count Number of audio frames to render per channel. * \param left Pointer to a buffer of at least count elements that receives the left output. * \param right Pointer to a buffer of at least count elements that receives the right output. * \return The number of frames actually rendered. * \retval 0 The end of song has been reached. * \remarks The output buffers are only written to up to the returned number of elements. * \remarks You can freely switch between any of the "openmpt_module_read*" variants if you see a need to do so. libopenmpt tries to introduce as little switching annoyances as possible. Normally, you would only use a single one of these functions for rendering a particular module. * \remarks Floating point samples are in the [-1.0..1.0] nominal range. They are not clipped to that range though and thus might overshoot. * \sa \ref libopenmpt_c_outputformat */ LIBOPENMPT_API size_t openmpt_module_read_float_stereo( openmpt_module * mod, int32_t samplerate, size_t count, float * left, float * right ); /*! \brief Render audio data * * \param mod The module handle to work on. * \param samplerate Sample rate to render output. Should be in [8000,192000], but this is not enforced. * \param count Number of audio frames to render per channel. * \param left Pointer to a buffer of at least count elements that receives the left output. * \param right Pointer to a buffer of at least count elements that receives the right output. * \param rear_left Pointer to a buffer of at least count elements that receives the rear left output. * \param rear_right Pointer to a buffer of at least count elements that receives the rear right output. * \return The number of frames actually rendered. * \retval 0 The end of song has been reached. * \remarks The output buffers are only written to up to the returned number of elements. * \remarks You can freely switch between any of the "openmpt_module_read*" variants if you see a need to do so. libopenmpt tries to introduce as little switching annoyances as possible. Normally, you would only use a single one of these functions for rendering a particular module. * \remarks Floating point samples are in the [-1.0..1.0] nominal range. They are not clipped to that range though and thus might overshoot. * \sa \ref libopenmpt_c_outputformat */ LIBOPENMPT_API size_t openmpt_module_read_float_quad( openmpt_module * mod, int32_t samplerate, size_t count, float * left, float * right, float * rear_left, float * rear_right ); /*! \brief Render audio data * * \param mod The module handle to work on. * \param samplerate Sample rate to render output. Should be in [8000,192000], but this is not enforced. * \param count Number of audio frames to render per channel. * \param interleaved_stereo Pointer to a buffer of at least count*2 elements that receives the interleaved stereo output in the order (L,R). * \return The number of frames actually rendered. * \retval 0 The end of song has been reached. * \remarks The output buffers are only written to up to the returned number of elements. * \remarks You can freely switch between any of the "openmpt_module_read*" variants if you see a need to do so. libopenmpt tries to introduce as little switching annoyances as possible. Normally, you would only use a single one of these functions for rendering a particular module. * \remarks It is recommended to use the floating point API because of the greater dynamic range and no implied clipping. * \sa \ref libopenmpt_c_outputformat */ LIBOPENMPT_API size_t openmpt_module_read_interleaved_stereo( openmpt_module * mod, int32_t samplerate, size_t count, int16_t * interleaved_stereo ); /*! \brief Render audio data * * \param mod The module handle to work on. * \param samplerate Sample rate to render output. Should be in [8000,192000], but this is not enforced. * \param count Number of audio frames to render per channel. * \param interleaved_quad Pointer to a buffer of at least count*4 elements that receives the interleaved quad surround output in the order (L,R,RL,RR). * \return The number of frames actually rendered. * \retval 0 The end of song has been reached. * \remarks The output buffers are only written to up to the returned number of elements. * \remarks You can freely switch between any of the "openmpt_module_read*" variants if you see a need to do so. libopenmpt tries to introduce as little switching annoyances as possible. Normally, you would only use a single one of these functions for rendering a particular module. * \remarks It is recommended to use the floating point API because of the greater dynamic range and no implied clipping. * \sa \ref libopenmpt_c_outputformat */ LIBOPENMPT_API size_t openmpt_module_read_interleaved_quad( openmpt_module * mod, int32_t samplerate, size_t count, int16_t * interleaved_quad ); /*! \brief Render audio data * * \param mod The module handle to work on. * \param samplerate Sample rate to render output. Should be in [8000,192000], but this is not enforced. * \param count Number of audio frames to render per channel. * \param interleaved_stereo Pointer to a buffer of at least count*2 elements that receives the interleaved stereo output in the order (L,R). * \return The number of frames actually rendered. * \retval 0 The end of song has been reached. * \remarks The output buffers are only written to up to the returned number of elements. * \remarks You can freely switch between any of the "openmpt_module_read*" variants if you see a need to do so. libopenmpt tries to introduce as little switching annoyances as possible. Normally, you would only use a single one of these functions for rendering a particular module. * \remarks Floating point samples are in the [-1.0..1.0] nominal range. They are not clipped to that range though and thus might overshoot. * \sa \ref libopenmpt_c_outputformat */ LIBOPENMPT_API size_t openmpt_module_read_interleaved_float_stereo( openmpt_module * mod, int32_t samplerate, size_t count, float * interleaved_stereo ); /*! \brief Render audio data * * \param mod The module handle to work on. * \param samplerate Sample rate to render output. Should be in [8000,192000], but this is not enforced. * \param count Number of audio frames to render per channel. * \param interleaved_quad Pointer to a buffer of at least count*4 elements that receives the interleaved quad surround output in the order (L,R,RL,RR). * \return The number of frames actually rendered. * \retval 0 The end of song has been reached. * \remarks The output buffers are only written to up to the returned number of elements. * \remarks You can freely switch between any of the "openmpt_module_read*" variants if you see a need to do so. libopenmpt tries to introduce as little switching annoyances as possible. Normally, you would only use a single one of these functions for rendering a particular module. * \remarks Floating point samples are in the [-1.0..1.0] nominal range. They are not clipped to that range though and thus might overshoot. * \sa \ref libopenmpt_c_outputformat */ LIBOPENMPT_API size_t openmpt_module_read_interleaved_float_quad( openmpt_module * mod, int32_t samplerate, size_t count, float * interleaved_quad ); /*@}*/ /*! \brief Get the list of supported metadata item keys * * \param mod The module handle to work on. * \return Metadata item keys supported by openmpt_module_get_metadata, as a semicolon-separated list. * \sa openmpt_module_get_metadata */ LIBOPENMPT_API const char * openmpt_module_get_metadata_keys( openmpt_module * mod ); /*! \brief Get a metadata item value * * \param mod The module handle to work on. * \param key Metadata item key to query. Use openmpt_module_get_metadata_keys to check for available keys. * Possible keys are: * - type: Module format extension (e.g. it) or another similar identifier for modules formats that typically do not use a file extension * - type_long: Format name associated with the module format (e.g. Impulse Tracker) * - originaltype: Module format extension (e.g. it) of the original module in case the actual type is a converted format (e.g. mo3 or gdm) * - originaltype_long: Format name associated with the module format (e.g. Impulse Tracker) of the original module in case the actual type is a converted format (e.g. mo3 or gdm) * - container: Container format the module file is embedded in, if any (e.g. umx) * - container_long: Full container name if the module is embedded in a container (e.g. Unreal Music) * - tracker: Tracker that was (most likely) used to save the module file, if known * - artist: Author of the module * - title: Module title * - date: Date the module was last saved, in ISO-8601 format. * - message: Song message. If the song message is empty or the module format does not support song messages, a list of instrument and sample names is returned instead. * - message_raw: Song message. If the song message is empty or the module format does not support song messages, an empty string is returned. * - warnings: A list of warnings that were generated while loading the module. * \return The associated value for key. * \sa openmpt_module_get_metadata_keys */ LIBOPENMPT_API const char * openmpt_module_get_metadata( openmpt_module * mod, const char * key ); /*! Get the current estimated beats per minute (BPM). * * \param mod The module handle to work on. * \remarks Many module formats lack time signature metadata. It is common that this estimate is off by a factor of two, but other multipliers are also possible. * \remarks Due to the nature of how module tempo works, the estimate may change slightly after switching libopenmpt's output to a different sample rate. * \return The current estimated BPM. */ LIBOPENMPT_API double openmpt_module_get_current_estimated_bpm( openmpt_module * mod ); /*! \brief Get the current speed * * \param mod The module handle to work on. * \return The current speed in ticks per row. */ LIBOPENMPT_API int32_t openmpt_module_get_current_speed( openmpt_module * mod ); /*! \brief Get the current tempo * * \param mod The module handle to work on. * \return The current tempo in tracker units. The exact meaning of this value depends on the tempo mode being used. */ LIBOPENMPT_API int32_t openmpt_module_get_current_tempo( openmpt_module * mod ); /*! \brief Get the current order * * \param mod The module handle to work on. * \return The current order at which the module is being played back. */ LIBOPENMPT_API int32_t openmpt_module_get_current_order( openmpt_module * mod ); /*! \brief Get the current pattern * * \param mod The module handle to work on. * \return The current pattern that is being played. */ LIBOPENMPT_API int32_t openmpt_module_get_current_pattern( openmpt_module * mod ); /*! \brief Get the current row * * \param mod The module handle to work on. * \return The current row at which the current pattern is being played. */ LIBOPENMPT_API int32_t openmpt_module_get_current_row( openmpt_module * mod ); /*! \brief Get the current amount of playing channels. * * \param mod The module handle to work on. * \return The amount of sample channels that are currently being rendered. */ LIBOPENMPT_API int32_t openmpt_module_get_current_playing_channels( openmpt_module * mod ); /*! \brief Get an approximate indication of the channel volume. * * \param mod The module handle to work on. * \param channel The channel whose volume should be retrieved. * \return The approximate channel volume. * \remarks The returned value is solely based on the note velocity and does not take the actual waveform of the playing sample into account. */ LIBOPENMPT_API float openmpt_module_get_current_channel_vu_mono( openmpt_module * mod, int32_t channel ); /*! \brief Get an approximate indication of the channel volume on the front-left speaker. * * \param mod The module handle to work on. * \param channel The channel whose volume should be retrieved. * \return The approximate channel volume. * \remarks The returned value is solely based on the note velocity and does not take the actual waveform of the playing sample into account. */ LIBOPENMPT_API float openmpt_module_get_current_channel_vu_left( openmpt_module * mod, int32_t channel ); /*! \brief Get an approximate indication of the channel volume on the front-right speaker. * * \param mod The module handle to work on. * \param channel The channel whose volume should be retrieved. * \return The approximate channel volume. * \remarks The returned value is solely based on the note velocity and does not take the actual waveform of the playing sample into account. */ LIBOPENMPT_API float openmpt_module_get_current_channel_vu_right( openmpt_module * mod, int32_t channel ); /*! \brief Get an approximate indication of the channel volume on the rear-left speaker. * * \param mod The module handle to work on. * \param channel The channel whose volume should be retrieved. * \return The approximate channel volume. * \remarks The returned value is solely based on the note velocity and does not take the actual waveform of the playing sample into account. */ LIBOPENMPT_API float openmpt_module_get_current_channel_vu_rear_left( openmpt_module * mod, int32_t channel ); /*! \brief Get an approximate indication of the channel volume on the rear-right speaker. * * \param mod The module handle to work on. * \param channel The channel whose volume should be retrieved. * \return The approximate channel volume. * \remarks The returned value is solely based on the note velocity and does not take the actual waveform of the playing sample into account. */ LIBOPENMPT_API float openmpt_module_get_current_channel_vu_rear_right( openmpt_module * mod, int32_t channel ); /*! \brief Get the number of sub-songs * * \param mod The module handle to work on. * \return The number of sub-songs in the module. This includes any "hidden" songs (songs that share the same sequence, but start at different order indices) and "normal" sub-songs or "sequences" (if the format supports them). * \sa openmpt_module_get_subsong_name, openmpt_module_select_subsong, openmpt_module_get_selected_subsong */ LIBOPENMPT_API int32_t openmpt_module_get_num_subsongs( openmpt_module * mod ); /*! \brief Get the number of pattern channels * * \param mod The module handle to work on. * \return The number of pattern channels in the module. Not all channels do necessarily contain data. * \remarks The number of pattern channels is completely independent of the number of output channels. libopenmpt can render modules in mono, stereo or quad surround, but the choice of which of the three modes to use must not be made based on the return value of this function, which may be any positive integer amount. Only use this function for informational purposes. */ LIBOPENMPT_API int32_t openmpt_module_get_num_channels( openmpt_module * mod ); /*! \brief Get the number of orders * * \param mod The module handle to work on. * \return The number of orders in the current sequence of the module. */ LIBOPENMPT_API int32_t openmpt_module_get_num_orders( openmpt_module * mod ); /*! \brief Get the number of patterns * * \param mod The module handle to work on. * \return The number of distinct patterns in the module. */ LIBOPENMPT_API int32_t openmpt_module_get_num_patterns( openmpt_module * mod ); /*! \brief Get the number of instruments * * \param mod The module handle to work on. * \return The number of instrument slots in the module. Instruments are a layer on top of samples, and are not supported by all module formats. */ LIBOPENMPT_API int32_t openmpt_module_get_num_instruments( openmpt_module * mod ); /*! \brief Get the number of samples * * \param mod The module handle to work on. * \return The number of sample slots in the module. */ LIBOPENMPT_API int32_t openmpt_module_get_num_samples( openmpt_module * mod ); /*! \brief Get a sub-song name * * \param mod The module handle to work on. * \param index The sub-song whose name should be retrieved * \return The sub-song name. * \sa openmpt_module_get_num_subsongs, openmpt_module_select_subsong, openmpt_module_get_selected_subsong */ LIBOPENMPT_API const char * openmpt_module_get_subsong_name( openmpt_module * mod, int32_t index ); /*! \brief Get a channel name * * \param mod The module handle to work on. * \param index The channel whose name should be retrieved * \return The channel name. * \sa openmpt_module_get_num_channels */ LIBOPENMPT_API const char * openmpt_module_get_channel_name( openmpt_module * mod, int32_t index ); /*! \brief Get an order name * * \param mod The module handle to work on. * \param index The order whose name should be retrieved * \return The order name. * \sa openmpt_module_get_num_orders */ LIBOPENMPT_API const char * openmpt_module_get_order_name( openmpt_module * mod, int32_t index ); /*! \brief Get a pattern name * * \param mod The module handle to work on. * \param index The pattern whose name should be retrieved * \return The pattern name. * \sa openmpt_module_get_num_patterns */ LIBOPENMPT_API const char * openmpt_module_get_pattern_name( openmpt_module * mod, int32_t index ); /*! \brief Get an instrument name * * \param mod The module handle to work on. * \param index The instrument whose name should be retrieved * \return The instrument name. * \sa openmpt_module_get_num_instruments */ LIBOPENMPT_API const char * openmpt_module_get_instrument_name( openmpt_module * mod, int32_t index ); /*! \brief Get a sample name * * \param mod The module handle to work on. * \param index The sample whose name should be retrieved * \return The sample name. * \sa openmpt_module_get_num_samples */ LIBOPENMPT_API const char * openmpt_module_get_sample_name( openmpt_module * mod, int32_t index ); /*! \brief Get pattern at order position * * \param mod The module handle to work on. * \param order The order item whose pattern index should be retrieved. * \return The pattern index found at the given order position of the current sequence. */ LIBOPENMPT_API int32_t openmpt_module_get_order_pattern( openmpt_module * mod, int32_t order ); /*! \brief Get the number of rows in a pattern * * \param mod The module handle to work on. * \param pattern The pattern whose row count should be retrieved. * \return The number of rows in the given pattern. If the pattern does not exist, 0 is returned. */ LIBOPENMPT_API int32_t openmpt_module_get_pattern_num_rows( openmpt_module * mod, int32_t pattern ); /*! \brief Get raw pattern content * * \param mod The module handle to work on. * \param pattern The pattern whose data should be retrieved. * \param row The row from which the data should be retrieved. * \param channel The channel from which the data should be retrieved. * \param command The cell index at which the data should be retrieved. See \ref openmpt_module_command_index * \return The internal, raw pattern data at the given pattern position. */ LIBOPENMPT_API uint8_t openmpt_module_get_pattern_row_channel_command( openmpt_module * mod, int32_t pattern, int32_t row, int32_t channel, int command ); /*! \brief Get formatted (human-readable) pattern content * * \param mod The module handle to work on. * \param pattern The pattern whose data should be retrieved. * \param row The row from which the data should be retrieved. * \param channel The channel from which the data should be retrieved. * \param command The cell index at which the data should be retrieved. * \return The formatted pattern data at the given pattern position. See \ref openmpt_module_command_index * \sa openmpt_module_highlight_pattern_row_channel_command */ LIBOPENMPT_API const char * openmpt_module_format_pattern_row_channel_command( openmpt_module * mod, int32_t pattern, int32_t row, int32_t channel, int command ); /*! \brief Get highlighting information for formatted pattern content * * \param mod The module handle to work on. * \param pattern The pattern whose data should be retrieved. * \param row The row from which the data should be retrieved. * \param channel The channel from which the data should be retrieved. * \param command The cell index at which the data should be retrieved. See \ref openmpt_module_command_index * \return The highlighting string for the formatted pattern data as retrieved by openmpt_module_get_pattern_row_channel_command at the given pattern position. * \remarks The returned string will map each character position of the string returned by openmpt_module_get_pattern_row_channel_command to a highlighting instruction. * Possible highlighting characters are: * - " " : empty/space * - "." : empty/dot * - "n" : generic note * - "m" : special note * - "i" : generic instrument * - "u" : generic volume column effect * - "v" : generic volume column parameter * - "e" : generic effect column effect * - "f" : generic effect column parameter * \sa openmpt_module_get_pattern_row_channel_command */ LIBOPENMPT_API const char * openmpt_module_highlight_pattern_row_channel_command( openmpt_module * mod, int32_t pattern, int32_t row, int32_t channel, int command ); /*! \brief Get formatted (human-readable) pattern content * * \param mod The module handle to work on. * \param pattern The pattern whose data should be retrieved. * \param row The row from which the data should be retrieved. * \param channel The channel from which the data should be retrieved. * \param width The maximum number of characters the string should contain. 0 means no limit. * \param pad If true, the string will be resized to the exact length provided in the width parameter. * \return The formatted pattern data at the given pattern position. * \sa openmpt_module_highlight_pattern_row_channel */ LIBOPENMPT_API const char * openmpt_module_format_pattern_row_channel( openmpt_module * mod, int32_t pattern, int32_t row, int32_t channel, size_t width, int pad ); /*! \brief Get highlighting information for formatted pattern content * * \param mod The module handle to work on. * \param pattern The pattern whose data should be retrieved. * \param row The row from which the data should be retrieved. * \param channel The channel from which the data should be retrieved. * \param width The maximum number of characters the string should contain. 0 means no limit. * \param pad If true, the string will be resized to the exact length provided in the width parameter. * \return The highlighting string for the formatted pattern data as retrieved by openmpt_module_format_pattern_row_channel at the given pattern position. * \sa openmpt_module_format_pattern_row_channel */ LIBOPENMPT_API const char * openmpt_module_highlight_pattern_row_channel( openmpt_module * mod, int32_t pattern, int32_t row, int32_t channel, size_t width, int pad ); /*! \brief Retrieve supported ctl keys * * \param mod The module handle to work on. * \return A semicolon-separated list containing all supported ctl keys. * \remarks Currently supported ctl values are: * - load.skip_samples (boolean): Set to "1" to avoid loading samples into memory * - load.skip_patterns (boolean): Set to "1" to avoid loading patterns into memory * - load.skip_plugins (boolean): Set to "1" to avoid loading plugins * - load.skip_subsongs_init (boolean): Set to "1" to avoid pre-initializing sub-songs. Skipping results in faster module loading but slower seeking. * - seek.sync_samples (boolean): Set to "1" to sync sample playback when using openmpt_module_set_position_seconds or openmpt_module_set_position_order_row. * - subsong (integer): The current subsong. Setting it has identical semantics as openmpt_module_select_subsong(), getting it returns the currently selected subsong. * - play.at_end (text): Chooses the behaviour when the end of song is reached: * - "fadeout": Fades the module out for a short while. Subsequent reads after the fadeout will return 0 rendered frames. * - "continue": Returns 0 rendered frames when the song end is reached. Subsequent reads will continue playing from the song start or loop start. * - "stop": Returns 0 rendered frames when the song end is reached. Subsequent reads will return 0 rendered frames. * - play.tempo_factor (floatingpoint): Set a floating point tempo factor. "1.0" is the default tempo. * - play.pitch_factor (floatingpoint): Set a floating point pitch factor. "1.0" is the default pitch. * - render.resampler.emulate_amiga (boolean): Set to "1" to enable the Amiga resampler for Amiga modules. This emulates the sound characteristics of the Paula chip and overrides the selected interpolation filter. Non-Amiga module formats are not affected by this setting. * - render.resampler.emulate_amiga_type (string): Configures the filter type to use for the Amiga resampler. Supported values are: * - "auto": Filter type is chosen by the library and might change. This is the default. * - "a500": Amiga A500 filter. * - "a1200": Amiga A1200 filter. * - "unfiltered": BLEP synthesis without model-specific filters. The LED filter is ignored by this setting. This filter mode is considered to be experimental and might change in the future. * - render.opl.volume_factor (floatingpoint): Set volume factor applied to synthesized OPL sounds, relative to the default OPL volume. * - dither (integer): Set the dither algorithm that is used for the 16 bit versions of openmpt_module_read. Supported values are: * - 0: No dithering. * - 1: Default mode. Chosen by OpenMPT code, might change. * - 2: Rectangular, 0.5 bit depth, no noise shaping (original ModPlug Tracker). * - 3: Rectangular, 1 bit depth, simple 1st order noise shaping */ LIBOPENMPT_API const char * openmpt_module_get_ctls( openmpt_module * mod ); /*! \brief Get current ctl value * * \param mod The module handle to work on. * \param ctl The ctl key whose value should be retrieved. * \return The associated ctl value, or NULL on failure. * \sa openmpt_module_get_ctls * \deprecated Please use openmpt_module_ctl_get_boolean(), openmpt_module_ctl_get_integer(), openmpt_module_ctl_get_floatingpoint(), or openmpt_module_ctl_get_text(). */ LIBOPENMPT_API LIBOPENMPT_DEPRECATED const char * openmpt_module_ctl_get( openmpt_module * mod, const char * ctl ); /*! \brief Get current ctl boolean value * * \param mod The module handle to work on. * \param ctl The ctl key whose value should be retrieved. * \return The associated ctl value, or NULL on failure. * \sa openmpt_module_get_ctls * \since 0.5.0 */ LIBOPENMPT_API int openmpt_module_ctl_get_boolean( openmpt_module * mod, const char * ctl ); /*! \brief Get current ctl integer value * * \param mod The module handle to work on. * \param ctl The ctl key whose value should be retrieved. * \return The associated ctl value, or NULL on failure. * \sa openmpt_module_get_ctls * \since 0.5.0 */ LIBOPENMPT_API int64_t openmpt_module_ctl_get_integer( openmpt_module * mod, const char * ctl ); /*! \brief Get current ctl floatingpoint value * * \param mod The module handle to work on. * \param ctl The ctl key whose value should be retrieved. * \return The associated ctl value, or NULL on failure. * \sa openmpt_module_get_ctls * \since 0.5.0 */ LIBOPENMPT_API double openmpt_module_ctl_get_floatingpoint( openmpt_module * mod, const char * ctl ); /*! \brief Get current ctl string value * * \param mod The module handle to work on. * \param ctl The ctl key whose value should be retrieved. * \return The associated ctl value, or NULL on failure. * \sa openmpt_module_get_ctls * \since 0.5.0 */ LIBOPENMPT_API const char * openmpt_module_ctl_get_text( openmpt_module * mod, const char * ctl ); /*! \brief Set ctl value * * \param mod The module handle to work on. * \param ctl The ctl key whose value should be set. * \param value The value that should be set. * \return 1 if successful, 0 in case the value is not sensible (e.g. negative tempo factor) or the ctl is not recognized. * \sa openmpt_module_get_ctls * \deprecated Please use openmpt_module_ctl_set_boolean(), openmpt_module_ctl_set_integer(), openmpt_module_ctl_set_floatingpoint(), or openmpt_module_ctl_set_text(). */ LIBOPENMPT_API LIBOPENMPT_DEPRECATED int openmpt_module_ctl_set( openmpt_module * mod, const char * ctl, const char * value ); /*! \brief Set ctl boolean value * * \param mod The module handle to work on. * \param ctl The ctl key whose value should be set. * \param value The value that should be set. * \return 1 if successful, 0 in case the value is not sensible (e.g. negative tempo factor) or the ctl is not recognized. * \sa openmpt_module_get_ctls * \since 0.5.0 */ LIBOPENMPT_API int openmpt_module_ctl_set_boolean( openmpt_module * mod, const char * ctl, int value ); /*! \brief Set ctl integer value * * \param mod The module handle to work on. * \param ctl The ctl key whose value should be set. * \param value The value that should be set. * \return 1 if successful, 0 in case the value is not sensible (e.g. negative tempo factor) or the ctl is not recognized. * \sa openmpt_module_get_ctls * \since 0.5.0 */ LIBOPENMPT_API int openmpt_module_ctl_set_integer( openmpt_module * mod, const char * ctl, int64_t value ); /*! \brief Set ctl floatingpoint value * * \param mod The module handle to work on. * \param ctl The ctl key whose value should be set. * \param value The value that should be set. * \return 1 if successful, 0 in case the value is not sensible (e.g. negative tempo factor) or the ctl is not recognized. * \sa openmpt_module_get_ctls * \since 0.5.0 */ LIBOPENMPT_API int openmpt_module_ctl_set_floatingpoint( openmpt_module * mod, const char * ctl, double value ); /*! \brief Set ctl string value * * \param mod The module handle to work on. * \param ctl The ctl key whose value should be set. * \param value The value that should be set. * \return 1 if successful, 0 in case the value is not sensible (e.g. negative tempo factor) or the ctl is not recognized. * \sa openmpt_module_get_ctls * \since 0.5.0 */ LIBOPENMPT_API int openmpt_module_ctl_set_text( openmpt_module * mod, const char * ctl, const char * value ); /* remember to add new functions to both C and C++ interfaces and to increase OPENMPT_API_VERSION_MINOR */ #ifdef __cplusplus } #endif /*! * @} */ #endif /* LIBOPENMPT_H */ libopenmpt-0.6.1+release.autotools/libopenmpt/libopenmpt.hpp0000644000175000017500000020762514040523544021324 00000000000000/* * libopenmpt.hpp * -------------- * Purpose: libopenmpt public c++ interface * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #ifndef LIBOPENMPT_HPP #define LIBOPENMPT_HPP #include "libopenmpt_config.h" #include #include #include #include #include #include #include #include #include /*! * \page libopenmpt_cpp_overview C++ API * * \section libopenmpt_cpp_error Error Handling * * libopenmpt C++ uses C++ exception handling for errror reporting. * * Unless otherwise noted, any libopenmpt function may throw exceptions and * all exceptions thrown by libopenmpt itself are derived from * openmpt::exception. * In addition, any libopenmpt function may also throw any exception specified * by the C++ language and C++ standard library. These are all derived from * std::exception. * * \section libopenmpt_cpp_strings Strings * * - All strings returned from libopenmpt are encoded in UTF-8. * - All strings passed to libopenmpt should also be encoded in UTF-8. * Behaviour in case of invalid UTF-8 is unspecified. * - libopenmpt does not enforce or expect any particular Unicode * normalization form. * * \section libopenmpt_cpp_fileio File I/O * * libopenmpt can use 3 different strategies for file I/O. * * - openmpt::module::module() with any kind of memory buffer as parameter will * load the module from the provided memory buffer, which will require loading * all data upfront by the library * caller. * - openmpt::module::module() with a seekable std::istream as parameter will * load the module via the stream interface. libopenmpt will not implement an * additional buffering layer in this case whih means the callbacks are assumed * to be performant even with small i/o sizes. * - openmpt::module::module() with an unseekable std::istream as parameter * will load the module via the stream interface. libopempt will make an * internal copy as it goes along, and sometimes have to pre-cache the whole * file in case it needs to know the complete file size. This strategy is * intended to be used if the file is located on a high latency network. * * | constructor | speed | memory consumption | * | ----------------: | :----: | :----------------: | * | memory buffer |

fast

|

medium

| * | seekable stream |

slow

|

low

| * | unseekable stream |

medium

|

high

| * * In all cases, the data or stream passed to the constructor is no longer * needed after the openmpt::module has been constructed and can be destroyed * by the caller. * * \section libopenmpt_cpp_outputformat Output Format * * libopenmpt supports a wide range of PCM output formats: * [8000..192000]/[mono|stereo|quad]/[f32|i16]. * * Unless you have some very specific requirements demanding a particular aspect * of the output format, you should always prefer 48000/stereo/f32 as the * libopenmpt PCM format. * * - Please prefer 48000Hz unless the user explicitly demands something else. * Practically all audio equipment and file formats use 48000Hz nowadays. * - Practically all module formats are made for stereo output. Mono will not * give you any measurable speed improvements and can trivially be obtained from * the stereo output anyway. Quad is not expected by almost all modules and even * if they do use surround effects, they expect the effects to be mixed to * stereo. * - Floating point output provides headroom instead of hard clipping if the * module is louder than 0dBFs, will give you a better signal-to-noise ratio * than int16 output, and avoid the need to apply an additional dithering to the * output by libopenmpt. Unless your platform has no floating point unit at all, * floating point will thus also be slightly faster. * * \section libopenmpt_cpp_threads libopenmpt in multi-threaded environments * * - libopenmpt is thread-aware. * - Individual libopenmpt objects are not thread-safe. * - libopenmpt itself does not spawn any user-visible threads but may spawn * threads for internal use. * - You must ensure to only ever access a particular libopenmpt object via * non-const member functions from a single thread at a time. * - You may access a particular libopenmpt object concurrently from different * threads when using only const member functions from all threads. * - Consecutive accesses can happen from different threads. * - Different objects can be accessed concurrently from different threads. * * \section libopenmpt-cpp-windows Windows support * * Using the libopenmpt C++ API when libopenmpt is compiled as a DLL on Windows * requires `#define LIBOPENMPT_USE_DLL` (or some equivalent build system * configuration) before `#include ` in order to * correctly import the symbols from the DLL. * * \section libopenmpt-cpp-detailed Detailed documentation * * \ref libopenmpt_cpp * * \section libopenmpt_cpp_examples Example * * \include libopenmpt_example_cxx.cpp * */ /*! \defgroup libopenmpt_cpp libopenmpt C++ */ /*! \addtogroup libopenmpt_cpp @{ */ namespace openmpt { #if defined(_MSC_VER) #pragma warning(push) #pragma warning(disable:4275) #endif //! libopenmpt exception base class /*! Base class used for all exceptions that are thrown by libopenmpt itself. Libopenmpt may additionally throw any exception thrown by the standard library which are all derived from std::exception. \sa \ref libopenmpt_cpp_error */ class LIBOPENMPT_CXX_API exception : public std::exception { private: char * text; public: exception( const std::string & text ) noexcept; exception( const exception & other ) noexcept; exception( exception && other ) noexcept; exception & operator = ( const exception & other ) noexcept; exception & operator = ( exception && other ) noexcept; virtual ~exception() noexcept; const char * what() const noexcept override; }; // class exception #if defined(_MSC_VER) #pragma warning(pop) #endif //! Get the libopenmpt version number /*! Returns the libopenmpt version number. \return The value represents (major << 24 + minor << 16 + patch << 0). \remarks libopenmpt < 0.3.0-pre used the following scheme: (major << 24 + minor << 16 + revision). */ LIBOPENMPT_CXX_API std::uint32_t get_library_version(); //! Get the core version number /*! Return the OpenMPT core version number. \return The value represents (majormajor << 24 + major << 16 + minor << 8 + minorminor). */ LIBOPENMPT_CXX_API std::uint32_t get_core_version(); namespace string { //! Return a verbose library version string from openmpt::string::get(). \deprecated Please use `"library_version"` directly. static const char library_version LIBOPENMPT_ATTR_DEPRECATED [] = "library_version"; //! Return a verbose library features string from openmpt::string::get(). \deprecated Please use `"library_features"` directly. static const char library_features LIBOPENMPT_ATTR_DEPRECATED [] = "library_features"; //! Return a verbose OpenMPT core version string from openmpt::string::get(). \deprecated Please use `"core_version"` directly. static const char core_version LIBOPENMPT_ATTR_DEPRECATED [] = "core_version"; //! Return information about the current build (e.g. the build date or compiler used) from openmpt::string::get(). \deprecated Please use `"build"` directly. static const char build LIBOPENMPT_ATTR_DEPRECATED [] = "build"; //! Return all contributors from openmpt::string::get(). \deprecated Please use `"credits"` directly. static const char credits LIBOPENMPT_ATTR_DEPRECATED [] = "credits"; //! Return contact information about libopenmpt from openmpt::string::get(). \deprecated Please use `"contact"` directly. static const char contact LIBOPENMPT_ATTR_DEPRECATED [] = "contact"; //! Return the libopenmpt license from openmpt::string::get(). \deprecated Please use `"license"` directly. static const char license LIBOPENMPT_ATTR_DEPRECATED [] = "license"; //! Get library related metadata. /*! \param key Key to query. Possible keys are: - "library_version": verbose library version string - "library_version_major": libopenmpt major version number - "library_version_minor": libopenmpt minor version number - "library_version_patch": libopenmpt patch version number - "library_version_prerel": libopenmpt pre-release version string - "library_version_is_release": "1" if the version is an officially released version - "library_features": verbose library features string - "core_version": verbose OpenMPT core version string - "source_url": original source code URL - "source_date": original source code date - "source_revision": original source code revision - "source_is_modified": "1" if the original source has been modified - "source_has_mixed_revisions": "1" if the original source has been compiled from different various revision - "source_is_package": "1" if the original source has been obtained from a source pacakge instead of source code version control - "build": information about the current build (e.g. the build date or compiler used) - "build_compiler": information about the compiler used to build libopenmpt - "credits": all contributors - "contact": contact information about libopenmpt - "license": the libopenmpt license - "url": libopenmpt website URL - "support_forum_url": libopenmpt support and discussions forum URL - "bugtracker_url": libopenmpt bug and issue tracker URL \return A (possibly multi-line) string containing the queried information. If no information is available, the string is empty. */ LIBOPENMPT_CXX_API std::string get( const std::string & key ); } // namespace string //! Get a list of supported file extensions /*! \return The list of extensions supported by this libopenmpt build. The extensions are returned lower-case without a leading dot. */ LIBOPENMPT_CXX_API std::vector get_supported_extensions(); //! Query whether a file extension is supported /*! \param extension file extension to query without a leading dot. The case is ignored. \return true if the extension is supported by libopenmpt, false otherwise. \deprecated Please use openmpt::is_extension_supported2(). */ LIBOPENMPT_ATTR_DEPRECATED LIBOPENMPT_CXX_API bool is_extension_supported( const std::string & extension ); //! Query whether a file extension is supported /*! \param extension file extension to query without a leading dot. The case is ignored. \return true if the extension is supported by libopenmpt, false otherwise. \since 0.5.0 */ LIBOPENMPT_CXX_API bool is_extension_supported2( std::string_view extension ); //! Roughly scan the input stream to find out whether libopenmpt might be able to open it /*! \param stream Input stream to scan. \param effort Effort to make when validating stream. Effort 0.0 does not even look at stream at all and effort 1.0 completely loads the file from stream. A lower effort requires less data to be loaded but only gives a rough estimate answer. Use an effort of 0.25 to only verify the header data of the module file. \param log Log where warning and errors are written. \return Probability between 0.0 and 1.0. \remarks openmpt::probe_file_header() provides a simpler and faster interface that fits almost all use cases better. It is recommended to use openmpt::probe_file_header() instead of openmpt::could_open_probability(). \remarks openmpt::could_open_probability() can return any value between 0.0 and 1.0. Only 0.0 and 1.0 are definitive answers, all values in between are just estimates. In general, any return value >0.0 means that you should try loading the file, and any value below 1.0 means that loading may fail. If you want a threshold above which you can be reasonably sure that libopenmpt will be able to load the file, use >=0.5. If you see the need for a threshold below which you could reasonably outright reject a file, use <0.25 (Note: Such a threshold for rejecting on the lower end is not recommended, but may be required for better integration into some other framework's probe scoring.). \remarks openmpt::could_open_probability() expects the complete file data to be eventually available to it, even if it is asked to just parse the header. Verification will be unreliable (both false positives and false negatives), if you pretend that the file is just some few bytes of initial data threshold in size. In order to really just access the first bytes of a file, check in your std::istream implementation whether data or seeking is requested beyond your initial data threshold, and in that case, return an error. openmpt::could_open_probability() will treat this as any other I/O error and return 0.0. You must not expect the correct result in this case. You instead must remember that it asked for more data than you currently want to provide to it and treat this situation as if openmpt::could_open_probability() returned 0.5. \sa \ref libopenmpt_c_fileio \sa openmpt::probe_file_header() \since 0.3.0 */ LIBOPENMPT_CXX_API double could_open_probability( std::istream & stream, double effort = 1.0, std::ostream & log = std::clog ); //! Roughly scan the input stream to find out whether libopenmpt might be able to open it /*! \deprecated Please use openmpt::could_open_probability(). */ LIBOPENMPT_ATTR_DEPRECATED LIBOPENMPT_CXX_API double could_open_propability( std::istream & stream, double effort = 1.0, std::ostream & log = std::clog ); //! Get recommended header size for successfull format probing /*! \sa openmpt::probe_file_header() \since 0.3.0 */ LIBOPENMPT_CXX_API std::size_t probe_file_header_get_recommended_size(); //! Probe for module formats in openmpt::probe_file_header(). \since 0.3.0 \deprecated Please use openmpt::probe_file_header_flags_modules2. static const std::uint64_t probe_file_header_flags_modules LIBOPENMPT_ATTR_DEPRECATED = 0x1ull; //! Probe for module-specific container formats in openmpt::probe_file_header(). \since 0.3.0 \deprecated Please use openmpt::probe_file_header_flags_containers2. static const std::uint64_t probe_file_header_flags_containers LIBOPENMPT_ATTR_DEPRECATED = 0x2ull; //! Probe for the default set of formats in openmpt::probe_file_header(). \since 0.3.0 \deprecated Please use openmpt::probe_file_header_flags_default2. static const std::uint64_t probe_file_header_flags_default LIBOPENMPT_ATTR_DEPRECATED = 0x1ull | 0x2ull; //! Probe for no formats in openmpt::probe_file_header(). \since 0.3.0 \deprecated Please use openmpt::probe_file_header_flags_none2. static const std::uint64_t probe_file_header_flags_none LIBOPENMPT_ATTR_DEPRECATED = 0x0ull; //! Possible values for openmpt::probe_file_header() flags parameter. \since 0.6.0 enum probe_file_header_flags : std::uint64_t { //! Probe for module formats in openmpt::probe_file_header(). \since 0.6.0 probe_file_header_flags_modules2 = 0x1ull, //! Probe for module-specific container formats in openmpt::probe_file_header(). \since 0.6.0 probe_file_header_flags_containers2 = 0x2ull, //! Probe for the default set of formats in openmpt::probe_file_header(). \since 0.6.0 probe_file_header_flags_default2 = probe_file_header_flags_modules2 | probe_file_header_flags_containers2, //! Probe for no formats in openmpt::probe_file_header(). \since 0.6.0 probe_file_header_flags_none2 = 0x0ull }; //! Possible return values for openmpt::probe_file_header(). \since 0.3.0 enum probe_file_header_result { probe_file_header_result_success = 1, probe_file_header_result_failure = 0, probe_file_header_result_wantmoredata = -1 }; //! Probe the provided bytes from the beginning of a file for supported file format headers to find out whether libopenmpt might be able to open it /*! \param flags Bit mask of openmpt::probe_file_header_flags_modules2 and openmpt::probe_file_header_flags_containers2, or openmpt::probe_file_header_flags_default2. \param data Beginning of the file data. \param size Size of the beginning of the file data. \param filesize Full size of the file data on disk. \remarks It is recommended to provide openmpt::probe_file_header_get_recommended_size() bytes of data for data and size. If the file is smaller, only provide the filesize amount and set size and filesize to the file's size. \remarks openmpt::could_open_probability() provides a more elaborate interface that might be required for special use cases. It is recommended to use openmpt::probe_file_header() though, if possible. \retval probe_file_header_result_success The file will most likely be supported by libopenmpt. \retval probe_file_header_result_failure The file is not supported by libopenmpt. \retval probe_file_header_result_wantmoredata An answer could not be determined with the amount of data provided. \sa openmpt::probe_file_header_get_recommended_size() \sa openmpt::could_open_probability() \since 0.5.0 */ LIBOPENMPT_CXX_API int probe_file_header( std::uint64_t flags, const std::byte * data, std::size_t size, std::uint64_t filesize ); //! Probe the provided bytes from the beginning of a file for supported file format headers to find out whether libopenmpt might be able to open it /*! \param flags Bit mask of openmpt::probe_file_header_flags_modules2 and openmpt::probe_file_header_flags_containers2, or openmpt::probe_file_header_flags_default2. \param data Beginning of the file data. \param size Size of the beginning of the file data. \param filesize Full size of the file data on disk. \remarks It is recommended to provide openmpt::probe_file_header_get_recommended_size() bytes of data for data and size. If the file is smaller, only provide the filesize amount and set size and filesize to the file's size. \remarks openmpt::could_open_probability() provides a more elaborate interface that might be required for special use cases. It is recommended to use openmpt::probe_file_header() though, if possible. \retval probe_file_header_result_success The file will most likely be supported by libopenmpt. \retval probe_file_header_result_failure The file is not supported by libopenmpt. \retval probe_file_header_result_wantmoredata An answer could not be determined with the amount of data provided. \sa openmpt::probe_file_header_get_recommended_size() \sa openmpt::could_open_probability() \since 0.3.0 */ LIBOPENMPT_CXX_API int probe_file_header( std::uint64_t flags, const std::uint8_t * data, std::size_t size, std::uint64_t filesize ); //! Probe the provided bytes from the beginning of a file for supported file format headers to find out whether libopenmpt might be able to open it /*! \param flags Bit mask of openmpt::probe_file_header_flags_modules2 and openmpt::probe_file_header_flags_containers2, or openmpt::probe_file_header_flags_default2. \param data Beginning of the file data. \param size Size of the beginning of the file data. \remarks It is recommended to use the overload of this function that also takes the filesize as parameter if at all possile. libopenmpt can provide more accurate answers if the filesize is known. \remarks It is recommended to provide openmpt::probe_file_header_get_recommended_size() bytes of data for data and size. If the file is smaller, only provide the filesize amount and set size to the file's size. \remarks openmpt::could_open_probability() provides a more elaborate interface that might be required for special use cases. It is recommended to use openmpt::probe_file_header() though, if possible. \retval probe_file_header_result_success The file will most likely be supported by libopenmpt. \retval probe_file_header_result_failure The file is not supported by libopenmpt. \retval probe_file_header_result_wantmoredata An answer could not be determined with the amount of data provided. \sa openmpt::probe_file_header_get_recommended_size() \sa openmpt::could_open_probability() \since 0.5.0 */ LIBOPENMPT_CXX_API int probe_file_header( std::uint64_t flags, const std::byte * data, std::size_t size ); //! Probe the provided bytes from the beginning of a file for supported file format headers to find out whether libopenmpt might be able to open it /*! \param flags Bit mask of openmpt::probe_file_header_flags_modules2 and openmpt::probe_file_header_flags_containers2, or openmpt::probe_file_header_flags_default2. \param data Beginning of the file data. \param size Size of the beginning of the file data. \remarks It is recommended to use the overload of this function that also takes the filesize as parameter if at all possile. libopenmpt can provide more accurate answers if the filesize is known. \remarks It is recommended to provide openmpt::probe_file_header_get_recommended_size() bytes of data for data and size. If the file is smaller, only provide the filesize amount and set size to the file's size. \remarks openmpt::could_open_probability() provides a more elaborate interface that might be required for special use cases. It is recommended to use openmpt::probe_file_header() though, if possible. \retval probe_file_header_result_success The file will most likely be supported by libopenmpt. \retval probe_file_header_result_failure The file is not supported by libopenmpt. \retval probe_file_header_result_wantmoredata An answer could not be determined with the amount of data provided. \sa openmpt::probe_file_header_get_recommended_size() \sa openmpt::could_open_probability() \since 0.3.0 */ LIBOPENMPT_CXX_API int probe_file_header( std::uint64_t flags, const std::uint8_t * data, std::size_t size ); //! Probe the provided bytes from the beginning of a file for supported file format headers to find out whether libopenmpt might be able to open it /*! \param flags Bit mask of openmpt::probe_file_header_flags_modules2 and openmpt::probe_file_header_flags_containers2, or openmpt::probe_file_header_flags_default2. \param stream Input stream to scan. \remarks stream is left in an unspecified state when this function returns. \remarks openmpt::could_open_probability() provides a more elaborate interface that might be required for special use cases. It is recommended to use openmpt::probe_file_header() though, if possible. \retval probe_file_header_result_success The file will most likely be supported by libopenmpt. \retval probe_file_header_result_failure The file is not supported by libopenmpt. \retval probe_file_header_result_wantmoredata An answer could not be determined with the amount of data provided. \sa openmpt::probe_file_header_get_recommended_size() \sa openmpt::could_open_probability() \since 0.3.0 */ LIBOPENMPT_CXX_API int probe_file_header( std::uint64_t flags, std::istream & stream ); class module_impl; class module_ext; namespace detail { typedef std::map< std::string, std::string > initial_ctls_map; } // namespace detail class LIBOPENMPT_CXX_API module { friend class module_ext; public: //! Parameter index to use with openmpt::module::get_render_param and openmpt::module::set_render_param enum render_param { //! Master Gain /*! The related value represents a relative gain in milliBel.\n The default value is 0.\n The supported value range is unlimited.\n */ RENDER_MASTERGAIN_MILLIBEL = 1, //! Stereo Separation /*! The related value represents the stereo separation generated by the libopenmpt mixer in percent.\n The default value is 100.\n The supported value range is [0,200].\n */ RENDER_STEREOSEPARATION_PERCENT = 2, //! Interpolation Filter /*! The related value represents the interpolation filter length used by the libopenmpt mixer.\n The default value is 0, which indicates a recommended default value.\n The supported value range is [0,inf). Values greater than the implementation limit are clamped to the maximum supported value.\n Currently supported values: - 0: internal default - 1: no interpolation (zero order hold) - 2: linear interpolation - 4: cubic interpolation - 8: windowed sinc with 8 taps */ RENDER_INTERPOLATIONFILTER_LENGTH = 3, //! Volume Ramping Strength /*! The related value represents the amount of volume ramping done by the libopenmpt mixer.\n The default value is -1, which indicates a recommended default value.\n The meaningful value range is [-1..10].\n A value of 0 completely disables volume ramping. This might cause clicks in sound output.\n Higher values imply slower/softer volume ramps. */ RENDER_VOLUMERAMPING_STRENGTH = 4 }; //! Parameter index to use with openmpt::module::get_pattern_row_channel_command, openmpt::module::format_pattern_row_channel_command and openmpt::module::highlight_pattern_row_channel_command enum command_index { command_note = 0, command_instrument = 1, command_volumeffect = 2, command_effect = 3, command_volume = 4, command_parameter = 5 }; private: module_impl * impl; private: // non-copyable module( const module & ); void operator = ( const module & ); private: // for module_ext module(); void set_impl( module_impl * i ); public: //! Construct an openmpt::module /*! \param stream Input stream from which the module is loaded. After the constructor has finished successfully, the input position of stream is set to the byte after the last byte that has been read. If the constructor fails, the state of the input position of stream is undefined. \param log Log where any warnings or errors are printed to. The lifetime of the reference has to be as long as the lifetime of the module instance. \param ctls A map of initial ctl values, see openmpt::module::get_ctls. \throws openmpt::exception Throws an exception derived from openmpt::exception in case the provided file cannot be opened. \remarks The input data can be discarded after an openmpt::module has been constructed successfully. \sa \ref libopenmpt_cpp_fileio */ module( std::istream & stream, std::ostream & log = std::clog, const std::map< std::string, std::string > & ctls = detail::initial_ctls_map() ); /*! \param data Data to load the module from. \param log Log where any warnings or errors are printed to. The lifetime of the reference has to be as long as the lifetime of the module instance. \param ctls A map of initial ctl values, see openmpt::module::get_ctls. \throws openmpt::exception Throws an exception derived from openmpt::exception in case the provided file cannot be opened. \remarks The input data can be discarded after an openmpt::module has been constructed successfully. \sa \ref libopenmpt_cpp_fileio \since 0.5.0 */ module( const std::vector & data, std::ostream & log = std::clog, const std::map< std::string, std::string > & ctls = detail::initial_ctls_map() ); /*! \param beg Begin of data to load the module from. \param end End of data to load the module from. \param log Log where any warnings or errors are printed to. The lifetime of the reference has to be as long as the lifetime of the module instance. \param ctls A map of initial ctl values, see openmpt::module::get_ctls. \throws openmpt::exception Throws an exception derived from openmpt::exception in case the provided file cannot be opened. \remarks The input data can be discarded after an openmpt::module has been constructed successfully. \sa \ref libopenmpt_cpp_fileio \since 0.5.0 */ module( const std::byte * beg, const std::byte * end, std::ostream & log = std::clog, const std::map< std::string, std::string > & ctls = detail::initial_ctls_map() ); /*! \param data Data to load the module from. \param size Amount of data available. \param log Log where any warnings or errors are printed to. The lifetime of the reference has to be as long as the lifetime of the module instance. \param ctls A map of initial ctl values, see openmpt::module::get_ctls. \throws openmpt::exception Throws an exception derived from openmpt::exception in case the provided file cannot be opened. \remarks The input data can be discarded after an openmpt::module has been constructed successfully. \sa \ref libopenmpt_cpp_fileio \since 0.5.0 */ module( const std::byte * data, std::size_t size, std::ostream & log = std::clog, const std::map< std::string, std::string > & ctls = detail::initial_ctls_map() ); /*! \param data Data to load the module from. \param log Log where any warnings or errors are printed to. The lifetime of the reference has to be as long as the lifetime of the module instance. \param ctls A map of initial ctl values, see openmpt::module::get_ctls. \throws openmpt::exception Throws an exception derived from openmpt::exception in case the provided file cannot be opened. \remarks The input data can be discarded after an openmpt::module has been constructed successfully. \sa \ref libopenmpt_cpp_fileio */ module( const std::vector & data, std::ostream & log = std::clog, const std::map< std::string, std::string > & ctls = detail::initial_ctls_map() ); /*! \param beg Begin of data to load the module from. \param end End of data to load the module from. \param log Log where any warnings or errors are printed to. The lifetime of the reference has to be as long as the lifetime of the module instance. \param ctls A map of initial ctl values, see openmpt::module::get_ctls. \throws openmpt::exception Throws an exception derived from openmpt::exception in case the provided file cannot be opened. \remarks The input data can be discarded after an openmpt::module has been constructed successfully. \sa \ref libopenmpt_cpp_fileio */ module( const std::uint8_t * beg, const std::uint8_t * end, std::ostream & log = std::clog, const std::map< std::string, std::string > & ctls = detail::initial_ctls_map() ); /*! \param data Data to load the module from. \param size Amount of data available. \param log Log where any warnings or errors are printed to. The lifetime of the reference has to be as long as the lifetime of the module instance. \param ctls A map of initial ctl values, see openmpt::module::get_ctls. \throws openmpt::exception Throws an exception derived from openmpt::exception in case the provided file cannot be opened. \remarks The input data can be discarded after an openmpt::module has been constructed successfully. \sa \ref libopenmpt_cpp_fileio */ module( const std::uint8_t * data, std::size_t size, std::ostream & log = std::clog, const std::map< std::string, std::string > & ctls = detail::initial_ctls_map() ); /*! \param data Data to load the module from. \param log Log where any warnings or errors are printed to. The lifetime of the reference has to be as long as the lifetime of the module instance. \param ctls A map of initial ctl values, see openmpt::module::get_ctls. \throws openmpt::exception Throws an exception derived from openmpt::exception in case the provided file cannot be opened. \remarks The input data can be discarded after an openmpt::module has been constructed successfully. \sa \ref libopenmpt_cpp_fileio */ module( const std::vector & data, std::ostream & log = std::clog, const std::map< std::string, std::string > & ctls = detail::initial_ctls_map() ); /*! \param beg Begin of data to load the module from. \param end End of data to load the module from. \param log Log where any warnings or errors are printed to. The lifetime of the reference has to be as long as the lifetime of the module instance. \param ctls A map of initial ctl values, see openmpt::module::get_ctls. \throws openmpt::exception Throws an exception derived from openmpt::exception in case the provided file cannot be opened. \remarks The input data can be discarded after an openmpt::module has been constructed successfully. \sa \ref libopenmpt_cpp_fileio */ module( const char * beg, const char * end, std::ostream & log = std::clog, const std::map< std::string, std::string > & ctls = detail::initial_ctls_map() ); /*! \param data Data to load the module from. \param size Amount of data available. \param log Log where any warnings or errors are printed to. The lifetime of the reference has to be as long as the lifetime of the module instance. \param ctls A map of initial ctl values, see openmpt::module::get_ctls. \throws openmpt::exception Throws an exception derived from openmpt::exception in case the provided file cannot be opened. \remarks The input data can be discarded after an openmpt::module has been constructed successfully. \sa \ref libopenmpt_cpp_fileio */ module( const char * data, std::size_t size, std::ostream & log = std::clog, const std::map< std::string, std::string > & ctls = detail::initial_ctls_map() ); /*! \param data Data to load the module from. \param size Amount of data available. \param log Log where any warnings or errors are printed to. The lifetime of the reference has to be as long as the lifetime of the module instance. \param ctls A map of initial ctl values, see openmpt::module::get_ctls. \throws openmpt::exception Throws an exception derived from openmpt::exception in case the provided file cannot be opened. \remarks The input data can be discarded after an openmpt::module has been constructed successfully. \sa \ref libopenmpt_cpp_fileio */ module( const void * data, std::size_t size, std::ostream & log = std::clog, const std::map< std::string, std::string > & ctls = detail::initial_ctls_map() ); virtual ~module(); public: //! Select a sub-song from a multi-song module /*! \param subsong Index of the sub-song. -1 plays all sub-songs consecutively. \throws openmpt::exception Throws an exception derived from openmpt::exception if sub-song is not in range [-1,openmpt::module::get_num_subsongs()[ \sa openmpt::module::get_num_subsongs, openmpt::module::get_selected_subsong, openmpt::module::get_subsong_names \remarks Whether subsong -1 (all subsongs consecutively), subsong 0 or some other subsong is selected by default, is an implementation detail and subject to change. If you do not want to care about subsongs, it is recommended to just not call openmpt::module::select_subsong() at all. */ void select_subsong( std::int32_t subsong ); //! Get currently selected sub-song from a multi-song module /*! \return Currently selected sub-song. -1 for all subsongs consecutively, 0 or greater for the current sub-song index. \sa openmpt::module::get_num_subsongs, openmpt::module::select_subsong, openmpt::module::get_subsong_names \since 0.3.0 */ std::int32_t get_selected_subsong() const; //! Set Repeat Count /*! \param repeat_count Repeat Count - -1: repeat forever - 0: play once, repeat zero times (the default) - n>0: play once and repeat n times after that \sa openmpt::module::get_repeat_count */ void set_repeat_count( std::int32_t repeat_count ); //! Get Repeat Count /*! \return Repeat Count - -1: repeat forever - 0: play once, repeat zero times (the default) - n>0: play once and repeat n times after that \sa openmpt::module::set_repeat_count */ std::int32_t get_repeat_count() const; //! Get approximate song duration /*! \return Approximate duration of current sub-song in seconds. \remarks The function may return infinity if the pattern data is too complex to evaluate. */ double get_duration_seconds() const; //! Set approximate current song position /*! \param seconds Seconds to seek to. If seconds is out of range, the position gets set to song start or end respectively. \return Approximate new song position in seconds. \sa openmpt::module::get_position_seconds */ double set_position_seconds( double seconds ); //! Get current song position /*! \return Current song position in seconds. \sa openmpt::module::set_position_seconds */ double get_position_seconds() const; //! Set approximate current song position /*! If order or row are out of range, to position is not modified and the current position is returned. \param order Pattern order number to seek to. \param row Pattern row number to seek to. \return Approximate new song position in seconds. \sa openmpt::module::set_position_seconds \sa openmpt::module::get_position_seconds */ double set_position_order_row( std::int32_t order, std::int32_t row ); //! Get render parameter /*! \param param Parameter to query. See openmpt::module::render_param. \return The current value of the parameter. \throws openmpt::exception Throws an exception derived from openmpt::exception if param is invalid. \sa openmpt::module::render_param \sa openmpt::module::set_render_param */ std::int32_t get_render_param( int param ) const; //! Set render parameter /*! \param param Parameter to set. See openmpt::module::render_param. \param value The value to set param to. \throws openmpt::exception Throws an exception derived from openmpt::exception if param is invalid or value is out of range. \sa openmpt::module::render_param \sa openmpt::module::get_render_param */ void set_render_param( int param, std::int32_t value ); /*@{*/ //! Render audio data /*! \param samplerate Sample rate to render output. Should be in [8000,192000], but this is not enforced. \param count Number of audio frames to render per channel. \param mono Pointer to a buffer of at least count elements that receives the mono/center output. \return The number of frames actually rendered. \retval 0 The end of song has been reached. \remarks The output buffers are only written to up to the returned number of elements. \remarks You can freely switch between any of the "read*" variants if you see a need to do so. libopenmpt tries to introduce as little switching annoyances as possible. Normally, you would only use a single one of these functions for rendering a particular module. \remarks It is recommended to use the floating point API because of the greater dynamic range and no implied clipping. \sa \ref libopenmpt_cpp_outputformat */ std::size_t read( std::int32_t samplerate, std::size_t count, std::int16_t * mono ); //! Render audio data /*! \param samplerate Sample rate to render output. Should be in [8000,192000], but this is not enforced. \param count Number of audio frames to render per channel. \param left Pointer to a buffer of at least count elements that receives the left output. \param right Pointer to a buffer of at least count elements that receives the right output. \return The number of frames actually rendered. \retval 0 The end of song has been reached. \remarks The output buffers are only written to up to the returned number of elements. \remarks You can freely switch between any of the "read*" variants if you see a need to do so. libopenmpt tries to introduce as little switching annoyances as possible. Normally, you would only use a single one of these functions for rendering a particular module. \remarks It is recommended to use the floating point API because of the greater dynamic range and no implied clipping. \sa \ref libopenmpt_cpp_outputformat */ std::size_t read( std::int32_t samplerate, std::size_t count, std::int16_t * left, std::int16_t * right ); //! Render audio data /*! \param samplerate Sample rate to render output. Should be in [8000,192000], but this is not enforced. \param count Number of audio frames to render per channel. \param left Pointer to a buffer of at least count elements that receives the left output. \param right Pointer to a buffer of at least count elements that receives the right output. \param rear_left Pointer to a buffer of at least count elements that receives the rear left output. \param rear_right Pointer to a buffer of at least count elements that receives the rear right output. \return The number of frames actually rendered. \retval 0 The end of song has been reached. \remarks The output buffers are only written to up to the returned number of elements. \remarks You can freely switch between any of the "read*" variants if you see a need to do so. libopenmpt tries to introduce as little switching annoyances as possible. Normally, you would only use a single one of these functions for rendering a particular module. \remarks It is recommended to use the floating point API because of the greater dynamic range and no implied clipping. \sa \ref libopenmpt_cpp_outputformat */ std::size_t read( std::int32_t samplerate, std::size_t count, std::int16_t * left, std::int16_t * right, std::int16_t * rear_left, std::int16_t * rear_right ); //! Render audio data /*! \param samplerate Sample rate to render output. Should be in [8000,192000], but this is not enforced. \param count Number of audio frames to render per channel. \param mono Pointer to a buffer of at least count elements that receives the mono/center output. \return The number of frames actually rendered. \retval 0 The end of song has been reached. \remarks The output buffers are only written to up to the returned number of elements. \remarks You can freely switch between any of the "read*" variants if you see a need to do so. libopenmpt tries to introduce as little switching annoyances as possible. Normally, you would only use a single one of these functions for rendering a particular module. \remarks Floating point samples are in the [-1.0..1.0] nominal range. They are not clipped to that range though and thus might overshoot. \sa \ref libopenmpt_cpp_outputformat */ std::size_t read( std::int32_t samplerate, std::size_t count, float * mono ); //! Render audio data /*! \param samplerate Sample rate to render output. Should be in [8000,192000], but this is not enforced. \param count Number of audio frames to render per channel. \param left Pointer to a buffer of at least count elements that receives the left output. \param right Pointer to a buffer of at least count elements that receives the right output. \return The number of frames actually rendered. \retval 0 The end of song has been reached. \remarks The output buffers are only written to up to the returned number of elements. \remarks You can freely switch between any of the "read*" variants if you see a need to do so. libopenmpt tries to introduce as little switching annoyances as possible. Normally, you would only use a single one of these functions for rendering a particular module. \remarks Floating point samples are in the [-1.0..1.0] nominal range. They are not clipped to that range though and thus might overshoot. \sa \ref libopenmpt_cpp_outputformat */ std::size_t read( std::int32_t samplerate, std::size_t count, float * left, float * right ); //! Render audio data /*! \param samplerate Sample rate to render output. Should be in [8000,192000], but this is not enforced. \param count Number of audio frames to render per channel. \param left Pointer to a buffer of at least count elements that receives the left output. \param right Pointer to a buffer of at least count elements that receives the right output. \param rear_left Pointer to a buffer of at least count elements that receives the rear left output. \param rear_right Pointer to a buffer of at least count elements that receives the rear right output. \return The number of frames actually rendered. \retval 0 The end of song has been reached. \remarks The output buffers are only written to up to the returned number of elements. \remarks You can freely switch between any of the "read*" variants if you see a need to do so. libopenmpt tries to introduce as little switching annoyances as possible. Normally, you would only use a single one of these functions for rendering a particular module. \remarks Floating point samples are in the [-1.0..1.0] nominal range. They are not clipped to that range though and thus might overshoot. \sa \ref libopenmpt_cpp_outputformat */ std::size_t read( std::int32_t samplerate, std::size_t count, float * left, float * right, float * rear_left, float * rear_right ); //! Render audio data /*! \param samplerate Sample rate to render output. Should be in [8000,192000], but this is not enforced. \param count Number of audio frames to render per channel. \param interleaved_stereo Pointer to a buffer of at least count*2 elements that receives the interleaved stereo output in the order (L,R). \return The number of frames actually rendered. \retval 0 The end of song has been reached. \remarks The output buffers are only written to up to the returned number of elements. \remarks You can freely switch between any of the "read*" variants if you see a need to do so. libopenmpt tries to introduce as little switching annoyances as possible. Normally, you would only use a single one of these functions for rendering a particular module. \remarks It is recommended to use the floating point API because of the greater dynamic range and no implied clipping. \sa \ref libopenmpt_cpp_outputformat */ std::size_t read_interleaved_stereo( std::int32_t samplerate, std::size_t count, std::int16_t * interleaved_stereo ); //! Render audio data /*! \param samplerate Sample rate to render output. Should be in [8000,192000], but this is not enforced. \param count Number of audio frames to render per channel. \param interleaved_quad Pointer to a buffer of at least count*4 elements that receives the interleaved quad surround output in the order (L,R,RL,RR). \return The number of frames actually rendered. \retval 0 The end of song has been reached. \remarks The output buffers are only written to up to the returned number of elements. \remarks You can freely switch between any of the "read*" variants if you see a need to do so. libopenmpt tries to introduce as little switching annoyances as possible. Normally, you would only use a single one of these functions for rendering a particular module. \remarks It is recommended to use the floating point API because of the greater dynamic range and no implied clipping. \sa \ref libopenmpt_cpp_outputformat */ std::size_t read_interleaved_quad( std::int32_t samplerate, std::size_t count, std::int16_t * interleaved_quad ); //! Render audio data /*! \param samplerate Sample rate to render output. Should be in [8000,192000], but this is not enforced. \param count Number of audio frames to render per channel. \param interleaved_stereo Pointer to a buffer of at least count*2 elements that receives the interleaved stereo output in the order (L,R). \return The number of frames actually rendered. \retval 0 The end of song has been reached. \remarks The output buffers are only written to up to the returned number of elements. \remarks You can freely switch between any of the "read*" variants if you see a need to do so. libopenmpt tries to introduce as little switching annoyances as possible. Normally, you would only use a single one of these functions for rendering a particular module. \remarks Floating point samples are in the [-1.0..1.0] nominal range. They are not clipped to that range though and thus might overshoot. \sa \ref libopenmpt_cpp_outputformat */ std::size_t read_interleaved_stereo( std::int32_t samplerate, std::size_t count, float * interleaved_stereo ); //! Render audio data /*! \param samplerate Sample rate to render output. Should be in [8000,192000], but this is not enforced. \param count Number of audio frames to render per channel. \param interleaved_quad Pointer to a buffer of at least count*4 elements that receives the interleaved quad surround output in the order (L,R,RL,RR). \return The number of frames actually rendered. \retval 0 The end of song has been reached. \remarks The output buffers are only written to up to the returned number of elements. \remarks You can freely switch between any of the "read*" variants if you see a need to do so. libopenmpt tries to introduce as little switching annoyances as possible. Normally, you would only use a single one of these functions for rendering a particular module. \remarks Floating point samples are in the [-1.0..1.0] nominal range. They are not clipped to that range though and thus might overshoot. \sa \ref libopenmpt_cpp_outputformat */ std::size_t read_interleaved_quad( std::int32_t samplerate, std::size_t count, float * interleaved_quad ); /*@}*/ //! Get the list of supported metadata item keys /*! \return Metadata item keys supported by openmpt::module::get_metadata \sa openmpt::module::get_metadata */ std::vector get_metadata_keys() const; //! Get a metadata item value /*! \param key Metadata item key to query. Use openmpt::module::get_metadata_keys to check for available keys. Possible keys are: - type: Module format extension (e.g. it) or another similar identifier for modules formats that typically do not use a file extension - type_long: Format name associated with the module format (e.g. Impulse Tracker) - originaltype: Module format extension (e.g. it) of the original module in case the actual type is a converted format (e.g. mo3 or gdm) - originaltype_long: Format name associated with the module format (e.g. Impulse Tracker) of the original module in case the actual type is a converted format (e.g. mo3 or gdm) - container: Container format the module file is embedded in, if any (e.g. umx) - container_long: Full container name if the module is embedded in a container (e.g. Unreal Music) - tracker: Tracker that was (most likely) used to save the module file, if known - artist: Author of the module - title: Module title - date: Date the module was last saved, in ISO-8601 format. - message: Song message. If the song message is empty or the module format does not support song messages, a list of instrument and sample names is returned instead. - message_raw: Song message. If the song message is empty or the module format does not support song messages, an empty string is returned. - warnings: A list of warnings that were generated while loading the module. \return The associated value for key. \sa openmpt::module::get_metadata_keys */ std::string get_metadata( const std::string & key ) const; //! Get the current estimated beats per minute (BPM). /*! \remarks Many module formats lack time signature metadata. It is common that this estimate is off by a factor of two, but other multipliers are also possible. \remarks Due to the nature of how module tempo works, the estimate may change slightly after switching libopenmpt's output to a different sample rate. \return The current estimated BPM. */ double get_current_estimated_bpm() const; //! Get the current speed /*! \return The current speed in ticks per row. */ std::int32_t get_current_speed() const; //! Get the current tempo /*! \return The current tempo in tracker units. The exact meaning of this value depends on the tempo mode being used. */ std::int32_t get_current_tempo() const; //! Get the current order /*! \return The current order at which the module is being played back. */ std::int32_t get_current_order() const; //! Get the current pattern /*! \return The current pattern that is being played. */ std::int32_t get_current_pattern() const; //! Get the current row /*! \return The current row at which the current pattern is being played. */ std::int32_t get_current_row() const; //! Get the current amount of playing channels. /*! \return The amount of sample channels that are currently being rendered. */ std::int32_t get_current_playing_channels() const; //! Get an approximate indication of the channel volume. /*! \param channel The channel whose volume should be retrieved. \return The approximate channel volume. \remarks The returned value is solely based on the note velocity and does not take the actual waveform of the playing sample into account. */ float get_current_channel_vu_mono( std::int32_t channel ) const; //! Get an approximate indication of the channel volume on the front-left speaker. /*! \param channel The channel whose volume should be retrieved. \return The approximate channel volume. \remarks The returned value is solely based on the note velocity and does not take the actual waveform of the playing sample into account. */ float get_current_channel_vu_left( std::int32_t channel ) const; //! Get an approximate indication of the channel volume on the front-right speaker. /*! \param channel The channel whose volume should be retrieved. \return The approximate channel volume. \remarks The returned value is solely based on the note velocity and does not take the actual waveform of the playing sample into account. */ float get_current_channel_vu_right( std::int32_t channel ) const; //! Get an approximate indication of the channel volume on the rear-left speaker. /*! \param channel The channel whose volume should be retrieved. \return The approximate channel volume. \remarks The returned value is solely based on the note velocity and does not take the actual waveform of the playing sample into account. */ float get_current_channel_vu_rear_left( std::int32_t channel ) const; //! Get an approximate indication of the channel volume on the rear-right speaker. /*! \param channel The channel whose volume should be retrieved. \return The approximate channel volume. \remarks The returned value is solely based on the note velocity and does not take the actual waveform of the playing sample into account. */ float get_current_channel_vu_rear_right( std::int32_t channel ) const; //! Get the number of sub-songs /*! \return The number of sub-songs in the module. This includes any "hidden" songs (songs that share the same sequence, but start at different order indices) and "normal" sub-songs or "sequences" (if the format supports them). \sa openmpt::module::get_subsong_names, openmpt::module::select_subsong, openmpt::module::get_selected_subsong */ std::int32_t get_num_subsongs() const; //! Get the number of pattern channels /*! \return The number of pattern channels in the module. Not all channels do necessarily contain data. \remarks The number of pattern channels is completely independent of the number of output channels. libopenmpt can render modules in mono, stereo or quad surround, but the choice of which of the three modes to use must not be made based on the return value of this function, which may be any positive integer amount. Only use this function for informational purposes. */ std::int32_t get_num_channels() const; //! Get the number of orders /*! \return The number of orders in the current sequence of the module. */ std::int32_t get_num_orders() const; //! Get the number of patterns /*! \return The number of distinct patterns in the module. */ std::int32_t get_num_patterns() const; //! Get the number of instruments /*! \return The number of instrument slots in the module. Instruments are a layer on top of samples, and are not supported by all module formats. */ std::int32_t get_num_instruments() const; //! Get the number of samples /*! \return The number of sample slots in the module. */ std::int32_t get_num_samples() const; //! Get a list of sub-song names /*! \return All sub-song names. \sa openmpt::module::get_num_subsongs, openmpt::module::select_subsong, openmpt::module::get_selected_subsong */ std::vector get_subsong_names() const; //! Get a list of channel names /*! \return All channel names. \sa openmpt::module::get_num_channels */ std::vector get_channel_names() const; //! Get a list of order names /*! \return All order names. \sa openmpt::module::get_num_orders */ std::vector get_order_names() const; //! Get a list of pattern names /*! \return All pattern names. \sa openmpt::module::get_num_patterns */ std::vector get_pattern_names() const; //! Get a list of instrument names /*! \return All instrument names. \sa openmpt::module::get_num_instruments */ std::vector get_instrument_names() const; //! Get a list of sample names /*! \return All sample names. \sa openmpt::module::get_num_samples */ std::vector get_sample_names() const; //! Get pattern at order position /*! \param order The order item whose pattern index should be retrieved. \return The pattern index found at the given order position of the current sequence. */ std::int32_t get_order_pattern( std::int32_t order ) const; //! Get the number of rows in a pattern /*! \param pattern The pattern whose row count should be retrieved. \return The number of rows in the given pattern. If the pattern does not exist, 0 is returned. */ std::int32_t get_pattern_num_rows( std::int32_t pattern ) const; //! Get raw pattern content /*! \param pattern The pattern whose data should be retrieved. \param row The row from which the data should be retrieved. \param channel The channel from which the data should be retrieved. \param command The cell index at which the data should be retrieved. See openmpt::module::command_index \return The internal, raw pattern data at the given pattern position. */ std::uint8_t get_pattern_row_channel_command( std::int32_t pattern, std::int32_t row, std::int32_t channel, int command ) const; //! Get formatted (human-readable) pattern content /*! \param pattern The pattern whose data should be retrieved. \param row The row from which the data should be retrieved. \param channel The channel from which the data should be retrieved. \param command The cell index at which the data should be retrieved. \return The formatted pattern data at the given pattern position. See openmpt::module::command_index \sa openmpt::module::highlight_pattern_row_channel_command */ std::string format_pattern_row_channel_command( std::int32_t pattern, std::int32_t row, std::int32_t channel, int command ) const; //! Get highlighting information for formatted pattern content /*! \param pattern The pattern whose data should be retrieved. \param row The row from which the data should be retrieved. \param channel The channel from which the data should be retrieved. \param command The cell index at which the data should be retrieved. See openmpt::module::command_index \return The highlighting string for the formatted pattern data as retrieved by openmpt::module::get_pattern_row_channel_command at the given pattern position. \remarks The returned string will map each character position of the string returned by openmpt::module::get_pattern_row_channel_command to a highlighting instruction. Possible highlighting characters are: - " " : empty/space - "." : empty/dot - "n" : generic note - "m" : special note - "i" : generic instrument - "u" : generic volume column effect - "v" : generic volume column parameter - "e" : generic effect column effect - "f" : generic effect column parameter \sa openmpt::module::get_pattern_row_channel_command */ std::string highlight_pattern_row_channel_command( std::int32_t pattern, std::int32_t row, std::int32_t channel, int command ) const; //! Get formatted (human-readable) pattern content /*! \param pattern The pattern whose data should be retrieved. \param row The row from which the data should be retrieved. \param channel The channel from which the data should be retrieved. \param width The maximum number of characters the string should contain. 0 means no limit. \param pad If true, the string will be resized to the exact length provided in the width parameter. \return The formatted pattern data at the given pattern position. \sa openmpt::module::highlight_pattern_row_channel */ std::string format_pattern_row_channel( std::int32_t pattern, std::int32_t row, std::int32_t channel, std::size_t width = 0, bool pad = true ) const; //! Get highlighting information for formatted pattern content /*! \param pattern The pattern whose data should be retrieved. \param row The row from which the data should be retrieved. \param channel The channel from which the data should be retrieved. \param width The maximum number of characters the string should contain. 0 means no limit. \param pad If true, the string will be resized to the exact length provided in the width parameter. \return The highlighting string for the formatted pattern data as retrieved by openmpt::module::format_pattern_row_channel at the given pattern position. \sa openmpt::module::format_pattern_row_channel */ std::string highlight_pattern_row_channel( std::int32_t pattern, std::int32_t row, std::int32_t channel, std::size_t width = 0, bool pad = true ) const; //! Retrieve supported ctl keys /*! \return A vector containing all supported ctl keys. \remarks Currently supported ctl values are: - load.skip_samples (boolean): Set to "1" to avoid loading samples into memory - load.skip_patterns (boolean): Set to "1" to avoid loading patterns into memory - load.skip_plugins (boolean): Set to "1" to avoid loading plugins - load.skip_subsongs_init (boolean): Set to "1" to avoid pre-initializing sub-songs. Skipping results in faster module loading but slower seeking. - seek.sync_samples (boolean): Set to "1" to sync sample playback when using openmpt::module::set_position_seconds or openmpt::module::set_position_order_row. - subsong (integer): The current subsong. Setting it has identical semantics as openmpt::module::select_subsong(), getting it returns the currently selected subsong. - play.at_end (text): Chooses the behaviour when the end of song is reached: - "fadeout": Fades the module out for a short while. Subsequent reads after the fadeout will return 0 rendered frames. - "continue": Returns 0 rendered frames when the song end is reached. Subsequent reads will continue playing from the song start or loop start. - "stop": Returns 0 rendered frames when the song end is reached. Subsequent reads will return 0 rendered frames. - play.tempo_factor (floatingpoint): Set a floating point tempo factor. "1.0" is the default tempo. - play.pitch_factor (floatingpoint): Set a floating point pitch factor. "1.0" is the default pitch. - render.resampler.emulate_amiga (boolean): Set to "1" to enable the Amiga resampler for Amiga modules. This emulates the sound characteristics of the Paula chip and overrides the selected interpolation filter. Non-Amiga module formats are not affected by this setting. - render.resampler.emulate_amiga_type (string): Configures the filter type to use for the Amiga resampler. Supported values are: - "auto": Filter type is chosen by the library and might change. This is the default. - "a500": Amiga A500 filter. - "a1200": Amiga A1200 filter. - "unfiltered": BLEP synthesis without model-specific filters. The LED filter is ignored by this setting. This filter mode is considered to be experimental and might change in the future. - render.opl.volume_factor (floatingpoint): Set volume factor applied to synthesized OPL sounds, relative to the default OPL volume. - dither (integer): Set the dither algorithm that is used for the 16 bit versions of openmpt::module::read. Supported values are: - 0: No dithering. - 1: Default mode. Chosen by OpenMPT code, might change. - 2: Rectangular, 0.5 bit depth, no noise shaping (original ModPlug Tracker). - 3: Rectangular, 1 bit depth, simple 1st order noise shaping An exclamation mark ("!") or a question mark ("?") can be appended to any ctl key in order to influence the behaviour in case of an unknown ctl key. "!" causes an exception to be thrown; "?" causes the ctl to be silently ignored. In case neither is appended to the key name, unknown init_ctls are ignored by default and other ctls throw an exception by default. */ std::vector get_ctls() const; //! Get current ctl value /*! \param ctl The ctl key whose value should be retrieved. \return The associated ctl value. \sa openmpt::module::get_ctls \deprecated Please use openmpt::module::ctl_get_boolean(), openmpt::module::ctl_get_integer(), openmpt::module::ctl_get_floatingpoint(), or openmpt::module::ctl_get_text(). */ LIBOPENMPT_ATTR_DEPRECATED std::string ctl_get( const std::string & ctl ) const; //! Get current ctl boolean value /*! \param ctl The ctl key whose value should be retrieved. \return The associated ctl value. \sa openmpt::module::get_ctls \since 0.5.0 */ bool ctl_get_boolean( std::string_view ctl ) const; //! Get current ctl integer value /*! \param ctl The ctl key whose value should be retrieved. \return The associated ctl value. \sa openmpt::module::get_ctls \since 0.5.0 */ std::int64_t ctl_get_integer( std::string_view ctl ) const; //! Get current ctl floatingpoint value /*! \param ctl The ctl key whose value should be retrieved. \return The associated ctl value. \sa openmpt::module::get_ctls \since 0.5.0 */ double ctl_get_floatingpoint( std::string_view ctl ) const; //! Get current ctl text value /*! \param ctl The ctl key whose value should be retrieved. \return The associated ctl value. \sa openmpt::module::get_ctls \since 0.5.0 */ std::string ctl_get_text( std::string_view ctl ) const; //! Set ctl value /*! \param ctl The ctl key whose value should be set. \param value The value that should be set. \throws openmpt::exception Throws an exception derived from openmpt::exception in case the value is not sensible (e.g. negative tempo factor) or under the circumstances outlined in openmpt::module::get_ctls. \sa openmpt::module::get_ctls \deprecated Please use openmpt::module::ctl_set_bool(), openmpt::module::ctl_set_int(), openmpt::module::ctl_set_floatingpoint(), or openmpt::module::ctl_set_string(). */ LIBOPENMPT_ATTR_DEPRECATED void ctl_set( const std::string & ctl, const std::string & value ); //! Set ctl boolean value /*! \param ctl The ctl key whose value should be set. \param value The value that should be set. \throws openmpt::exception Throws an exception derived from openmpt::exception in case the value is not sensible (e.g. negative tempo factor) or under the circumstances outlined in openmpt::module::get_ctls. \sa openmpt::module::get_ctls \since 0.5.0 */ void ctl_set_boolean( std::string_view ctl, bool value ); //! Set ctl integer value /*! \param ctl The ctl key whose value should be set. \param value The value that should be set. \throws openmpt::exception Throws an exception derived from openmpt::exception in case the value is not sensible (e.g. negative tempo factor) or under the circumstances outlined in openmpt::module::get_ctls. \sa openmpt::module::get_ctls \since 0.5.0 */ void ctl_set_integer( std::string_view ctl, std::int64_t value ); //! Set ctl floatingpoint value /*! \param ctl The ctl key whose value should be set. \param value The value that should be set. \throws openmpt::exception Throws an exception derived from openmpt::exception in case the value is not sensible (e.g. negative tempo factor) or under the circumstances outlined in openmpt::module::get_ctls. \sa openmpt::module::get_ctls \since 0.5.0 */ void ctl_set_floatingpoint( std::string_view ctl, double value ); //! Set ctl text value /*! \param ctl The ctl key whose value should be set. \param value The value that should be set. \throws openmpt::exception Throws an exception derived from openmpt::exception in case the value is not sensible (e.g. negative tempo factor) or under the circumstances outlined in openmpt::module::get_ctls. \sa openmpt::module::get_ctls \since 0.5.0 */ void ctl_set_text( std::string_view ctl, std::string_view value ); // remember to add new functions to both C and C++ interfaces and to increase OPENMPT_API_VERSION_MINOR }; // class module } // namespace openmpt /*! @} */ #endif // LIBOPENMPT_HPP libopenmpt-0.6.1+release.autotools/libopenmpt/libopenmpt_version.h0000644000175000017500000000622414175540615022530 00000000000000/* * libopenmpt_version.h * -------------------- * Purpose: libopenmpt public interface version * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #ifndef LIBOPENMPT_VERSION_H #define LIBOPENMPT_VERSION_H /* clang-format off */ /*! \addtogroup libopenmpt @{ */ /*! \brief libopenmpt major version number */ #define OPENMPT_API_VERSION_MAJOR 0 /*! \brief libopenmpt minor version number */ #define OPENMPT_API_VERSION_MINOR 6 /*! \brief libopenmpt patch version number */ #define OPENMPT_API_VERSION_PATCH 1 /*! \brief libopenmpt pre-release tag */ #define OPENMPT_API_VERSION_PREREL "" /*! \brief libopenmpt pre-release flag */ #define OPENMPT_API_VERSION_IS_PREREL 0 /*! \brief libopenmpt version number as a single integer value * \since 0.3 * \remarks Use the following shim if you need to support earlier libopenmpt versions: * \code * #include * #if !defined(OPENMPT_API_VERSION_MAKE) * #define OPENMPT_API_VERSION_MAKE(major, minor, patch) (((major)<<24)|((minor)<<16)|((patch)<<0)) * #endif * \endcode */ #define OPENMPT_API_VERSION_MAKE(major, minor, patch) (((major)<<24)|((minor)<<16)|((patch)<<0)) /*! \brief libopenmpt API version number */ #define OPENMPT_API_VERSION OPENMPT_API_VERSION_MAKE(OPENMPT_API_VERSION_MAJOR, OPENMPT_API_VERSION_MINOR, OPENMPT_API_VERSION_PATCH) /*! \brief Check whether the libopenmpt API is at least the provided version * \since 0.3 * \remarks Use the following shim if you need to support earlier libopenmpt versions: * \code * #include * #if !defined(OPENMPT_API_VERSION_AT_LEAST) * #define OPENMPT_API_VERSION_AT_LEAST(major, minor, patch) (OPENMPT_API_VERSION >= OPENMPT_API_VERSION_MAKE((major), (minor), (patch))) * #endif * \endcode */ #define OPENMPT_API_VERSION_AT_LEAST(major, minor, patch) (OPENMPT_API_VERSION >= OPENMPT_API_VERSION_MAKE((major), (minor), (patch))) /*! \brief Check whether the libopenmpt API is before the provided version * \since 0.3 * \remarks Use the following shim if you need to support earlier libopenmpt versions: * \code * #include * #if !defined(OPENMPT_API_VERSION_BEFORE) * #define OPENMPT_API_VERSION_BEFORE(major, minor, patch) (OPENMPT_API_VERSION < OPENMPT_API_VERSION_MAKE((major), (minor), (patch))) * #endif * \endcode */ #define OPENMPT_API_VERSION_BEFORE(major, minor, patch) (OPENMPT_API_VERSION < OPENMPT_API_VERSION_MAKE((major), (minor), (patch))) #define OPENMPT_API_VERSION_HELPER_STRINGIZE(x) #x #define OPENMPT_API_VERSION_STRINGIZE(x) OPENMPT_API_VERSION_HELPER_STRINGIZE(x) #define OPENMPT_API_VERSION_STRING OPENMPT_API_VERSION_STRINGIZE(OPENMPT_API_VERSION_MAJOR) "." OPENMPT_API_VERSION_STRINGIZE(OPENMPT_API_VERSION_MINOR) "." OPENMPT_API_VERSION_STRINGIZE(OPENMPT_API_VERSION_PATCH) OPENMPT_API_VERSION_PREREL /*! @} */ /* clang-format on */ #endif /* LIBOPENMPT_VERSION_H */ libopenmpt-0.6.1+release.autotools/libopenmpt/libopenmpt_config.h0000644000175000017500000001253014164574426022312 00000000000000/* * libopenmpt_config.h * ------------------- * Purpose: libopenmpt public interface configuration * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #ifndef LIBOPENMPT_CONFIG_H #define LIBOPENMPT_CONFIG_H /* clang-format off */ /*! \defgroup libopenmpt libopenmpt */ /*! \addtogroup libopenmpt @{ */ /* provoke warnings if already defined */ #define LIBOPENMPT_API #undef LIBOPENMPT_API #define LIBOPENMPT_CXX_API #undef LIBOPENMPT_CXX_API /*! \brief Defined if libopenmpt/libopenmpt_stream_callbacks_buffer.h exists. */ #define LIBOPENMPT_STREAM_CALLBACKS_BUFFER /*! \brief Defined if libopenmpt/libopenmpt_stream_callbacks_fd.h exists. * \since 0.3 * \remarks * Use the following to check for availability: * \code * #include * #if defined(LIBOPENMPT_STREAM_CALLBACKS_FD) || ((OPENMPT_API_VERSION_MAJOR == 0) && ((OPENMPT_API_VERSION_MINOR == 2) || (OPENMPT_API_VERSION_MINOR == 1))) * #include * #endif * \endcode */ #define LIBOPENMPT_STREAM_CALLBACKS_FD /*! \brief Defined if libopenmpt/libopenmpt_stream_callbacks_file.h exists. * \since 0.3 * \remarks * Use the following to check for availability: * \code * #include * #if defined(LIBOPENMPT_STREAM_CALLBACKS_FILE) || ((OPENMPT_API_VERSION_MAJOR == 0) && ((OPENMPT_API_VERSION_MINOR == 2) || (OPENMPT_API_VERSION_MINOR == 1))) * #include * #endif * \endcode */ #define LIBOPENMPT_STREAM_CALLBACKS_FILE #if defined(__DOXYGEN__) #define LIBOPENMPT_API_HELPER_EXPORT #define LIBOPENMPT_API_HELPER_IMPORT #define LIBOPENMPT_API_HELPER_PUBLIC #define LIBOPENMPT_API_HELPER_LOCAL #elif defined(_MSC_VER) #define LIBOPENMPT_API_HELPER_EXPORT __declspec(dllexport) #define LIBOPENMPT_API_HELPER_IMPORT __declspec(dllimport) #define LIBOPENMPT_API_HELPER_PUBLIC #define LIBOPENMPT_API_HELPER_LOCAL #elif defined(__EMSCRIPTEN__) #define LIBOPENMPT_API_HELPER_EXPORT __attribute__((visibility("default"))) __attribute__((used)) #define LIBOPENMPT_API_HELPER_IMPORT __attribute__((visibility("default"))) __attribute__((used)) #define LIBOPENMPT_API_HELPER_PUBLIC __attribute__((visibility("default"))) __attribute__((used)) #define LIBOPENMPT_API_HELPER_LOCAL __attribute__((visibility("hidden"))) #elif (defined(__GNUC__) || defined(__clang__)) && defined(_WIN32) #define LIBOPENMPT_API_HELPER_EXPORT __declspec(dllexport) #define LIBOPENMPT_API_HELPER_IMPORT __declspec(dllimport) #define LIBOPENMPT_API_HELPER_PUBLIC __attribute__((visibility("default"))) #define LIBOPENMPT_API_HELPER_LOCAL __attribute__((visibility("hidden"))) #elif defined(__GNUC__) || defined(__clang__) #define LIBOPENMPT_API_HELPER_EXPORT __attribute__((visibility("default"))) #define LIBOPENMPT_API_HELPER_IMPORT __attribute__((visibility("default"))) #define LIBOPENMPT_API_HELPER_PUBLIC __attribute__((visibility("default"))) #define LIBOPENMPT_API_HELPER_LOCAL __attribute__((visibility("hidden"))) #elif defined(_WIN32) #define LIBOPENMPT_API_HELPER_EXPORT __declspec(dllexport) #define LIBOPENMPT_API_HELPER_IMPORT __declspec(dllimport) #define LIBOPENMPT_API_HELPER_PUBLIC #define LIBOPENMPT_API_HELPER_LOCAL #else #define LIBOPENMPT_API_HELPER_EXPORT #define LIBOPENMPT_API_HELPER_IMPORT #define LIBOPENMPT_API_HELPER_PUBLIC #define LIBOPENMPT_API_HELPER_LOCAL #endif #if defined(LIBOPENMPT_BUILD_DLL) #define LIBOPENMPT_API LIBOPENMPT_API_HELPER_EXPORT #elif defined(LIBOPENMPT_USE_DLL) #define LIBOPENMPT_API LIBOPENMPT_API_HELPER_IMPORT #else #define LIBOPENMPT_API LIBOPENMPT_API_HELPER_PUBLIC #endif #ifdef __cplusplus #define LIBOPENMPT_CXX_API LIBOPENMPT_API #if defined(LIBOPENMPT_USE_DLL) #if defined(_MSC_VER) && !defined(_DLL) #error "C++ interface is disabled if libopenmpt is built as a DLL and the runtime is statically linked. This is not supported by microsoft and cannot possibly work. Ever." #undef LIBOPENMPT_CXX_API #define LIBOPENMPT_CXX_API LIBOPENMPT_API_HELPER_LOCAL #endif #endif #if defined(__EMSCRIPTEN__) /* Only the C API is supported for emscripten. Disable the C++ API. */ #undef LIBOPENMPT_CXX_API #define LIBOPENMPT_CXX_API LIBOPENMPT_API_HELPER_LOCAL #endif #endif /*! @} */ /* C */ #if !defined(LIBOPENMPT_NO_DEPRECATE) #if defined(__clang__) #define LIBOPENMPT_DEPRECATED __attribute__((deprecated)) #elif defined(__GNUC__) #define LIBOPENMPT_DEPRECATED __attribute__((deprecated)) #elif defined(_MSC_VER) #define LIBOPENMPT_DEPRECATED __declspec(deprecated) #else #define LIBOPENMPT_DEPRECATED #endif #else #define LIBOPENMPT_DEPRECATED #endif #ifndef __cplusplus #if !defined(LIBOPENMPT_NO_DEPRECATE) LIBOPENMPT_DEPRECATED static const int LIBOPENMPT_DEPRECATED_STRING_CONSTANT = 0; #define LIBOPENMPT_DEPRECATED_STRING( str ) ( LIBOPENMPT_DEPRECATED_STRING_CONSTANT ? ( str ) : ( str ) ) #else #define LIBOPENMPT_DEPRECATED_STRING( str ) str #endif #else #define LIBOPENMPT_DEPRECATED_STRING( str ) str #endif /* C++ */ #ifdef __cplusplus #if defined(LIBOPENMPT_ASSUME_CPLUSPLUS) #endif #if !defined(LIBOPENMPT_NO_DEPRECATE) #define LIBOPENMPT_ATTR_DEPRECATED [[deprecated]] #else #define LIBOPENMPT_ATTR_DEPRECATED #endif #endif /* clang-format on */ #include "libopenmpt_version.h" #endif /* LIBOPENMPT_CONFIG_H */ libopenmpt-0.6.1+release.autotools/libopenmpt/libopenmpt_stream_callbacks_buffer.h0000644000175000017500000001174213413055520025655 00000000000000/* * libopenmpt_stream_callbacks_buffer.h * ------------------------------------ * Purpose: libopenmpt public c interface * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #ifndef LIBOPENMPT_STREAM_CALLBACKS_BUFFER_H #define LIBOPENMPT_STREAM_CALLBACKS_BUFFER_H #include "libopenmpt.h" /* The use of this header requires: #include #if defined( LIBOPENMPT_STREAM_CALLBACKS_BUFFER ) #include #else #error "libopenmpt too old." #endif */ #include #include #include /*! \addtogroup libopenmpt_c * @{ */ #ifdef __cplusplus extern "C" { #endif typedef struct openmpt_stream_buffer { const void * file_data; /* or prefix data IFF prefix_size < file_size */ int64_t file_size; int64_t file_pos; int64_t prefix_size; int overflow; } openmpt_stream_buffer; static size_t openmpt_stream_buffer_read_func( void * stream, void * dst, size_t bytes ) { openmpt_stream_buffer * s = (openmpt_stream_buffer*)stream; int64_t offset = 0; int64_t begpos = 0; int64_t endpos = 0; size_t valid_bytes = 0; if ( !s ) { return 0; } offset = bytes; begpos = s->file_pos; endpos = s->file_pos; valid_bytes = 0; endpos = (uint64_t)endpos + (uint64_t)offset; if ( ( offset > 0 ) && !( (uint64_t)endpos > (uint64_t)begpos ) ) { /* integer wrapped */ return 0; } if ( bytes == 0 ) { return 0; } if ( begpos >= s->file_size ) { return 0; } if ( endpos > s->file_size ) { /* clip to eof */ bytes = bytes - (size_t)( endpos - s->file_size ); endpos = endpos - ( endpos - s->file_size ); } memset( dst, 0, bytes ); if ( begpos >= s->prefix_size ) { s->overflow = 1; valid_bytes = 0; } else if ( endpos > s->prefix_size ) { s->overflow = 1; valid_bytes = bytes - (size_t)( endpos - s->prefix_size ); } else { valid_bytes = bytes; } memcpy( dst, (const char*)s->file_data + s->file_pos, valid_bytes ); s->file_pos = s->file_pos + bytes; return bytes; } static int openmpt_stream_buffer_seek_func( void * stream, int64_t offset, int whence ) { openmpt_stream_buffer * s = (openmpt_stream_buffer*)stream; int result = -1; if ( !s ) { return -1; } switch ( whence ) { case OPENMPT_STREAM_SEEK_SET: if ( offset < 0 ) { return -1; } if ( offset > s->file_size ) { return -1; } s->file_pos = offset; result = 0; break; case OPENMPT_STREAM_SEEK_CUR: do { int64_t oldpos = s->file_pos; int64_t pos = s->file_pos; pos = (uint64_t)pos + (uint64_t)offset; if ( ( offset > 0 ) && !( (uint64_t)pos > (uint64_t)oldpos ) ) { /* integer wrapped */ return -1; } if ( ( offset < 0 ) && !( (uint64_t)pos < (uint64_t)oldpos ) ) { /* integer wrapped */ return -1; } s->file_pos = pos; } while(0); result = 0; break; case OPENMPT_STREAM_SEEK_END: if ( offset > 0 ) { return -1; } do { int64_t oldpos = s->file_pos; int64_t pos = s->file_pos; pos = s->file_size; pos = (uint64_t)pos + (uint64_t)offset; if ( ( offset < 0 ) && !( (uint64_t)pos < (uint64_t)oldpos ) ) { /* integer wrapped */ return -1; } s->file_pos = pos; } while(0); result = 0; break; } return result; } static int64_t openmpt_stream_buffer_tell_func( void * stream ) { openmpt_stream_buffer * s = (openmpt_stream_buffer*)stream; if ( !s ) { return -1; } return s->file_pos; } static void openmpt_stream_buffer_init( openmpt_stream_buffer * buffer, const void * file_data, int64_t file_size ) { memset( buffer, 0, sizeof( openmpt_stream_buffer ) ); buffer->file_data = file_data; buffer->file_size = file_size; buffer->file_pos = 0; buffer->prefix_size = file_size; buffer->overflow = 0; } #define openmpt_stream_buffer_init_prefix_only( buffer_, prefix_data_, prefix_size_, file_size_ ) do { \ openmpt_stream_buffer_init( (buffer_), (prefix_data_), (file_size_) ); \ (buffer_)->prefix_size = (prefix_size_); \ } while(0) #define openmpt_stream_buffer_overflowed( buffer_ ) ( (buffer_)->overflow ) /*! \brief Provide openmpt_stream_callbacks for in-memoy buffers * * Fills openmpt_stream_callbacks suitable for passing an in-memory buffer as a stream parameter to functions doing file input/output. * * \remarks The stream argument must be passed as `(void*)(openmpt_stream_buffer*)stream_buffer`. * \sa \ref libopenmpt_c_fileio * \sa openmpt_stream_callbacks * \sa openmpt_could_open_probability2 * \sa openmpt_probe_file_header_from_stream * \sa openmpt_module_create2 */ static openmpt_stream_callbacks openmpt_stream_get_buffer_callbacks(void) { openmpt_stream_callbacks retval; memset( &retval, 0, sizeof( openmpt_stream_callbacks ) ); retval.read = openmpt_stream_buffer_read_func; retval.seek = openmpt_stream_buffer_seek_func; retval.tell = openmpt_stream_buffer_tell_func; return retval; } #ifdef __cplusplus } #endif /*! * @} */ #endif /* LIBOPENMPT_STREAM_CALLBACKS_BUFFER_H */ libopenmpt-0.6.1+release.autotools/libopenmpt/libopenmpt_stream_callbacks_fd.h0000644000175000017500000000433713413055520024777 00000000000000/* * libopenmpt_stream_callbacks_fd.h * -------------------------------- * Purpose: libopenmpt public c interface * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #ifndef LIBOPENMPT_STREAM_CALLBACKS_FD_H #define LIBOPENMPT_STREAM_CALLBACKS_FD_H #include "libopenmpt.h" #ifdef _MSC_VER #include #endif #include #include #include #include #ifndef _MSC_VER #include #endif /*! \addtogroup libopenmpt_c * @{ */ #ifdef __cplusplus extern "C" { #endif /* This stuff has to be in a header file because of possibly different MSVC CRTs which cause problems for fd crossing CRT boundaries. */ static size_t openmpt_stream_fd_read_func( void * stream, void * dst, size_t bytes ) { int fd = 0; #if defined(_MSC_VER) size_t retval = 0; int to_read = 0; int ret_read = 0; #else ssize_t retval = 0; #endif fd = (int)(uintptr_t)stream; if ( fd < 0 ) { return 0; } #if defined(_MSC_VER) retval = 0; while ( bytes > 0 ) { to_read = 0; if ( bytes < (size_t)INT_MAX ) { to_read = (int)bytes; } else { to_read = INT_MAX; } ret_read = _read( fd, dst, to_read ); if ( ret_read <= 0 ) { return retval; } bytes -= ret_read; retval += ret_read; } #else retval = read( fd, dst, bytes ); #endif if ( retval <= 0 ) { return 0; } return retval; } /*! \brief Provide openmpt_stream_callbacks for standard POSIX file descriptors * * Fills openmpt_stream_callbacks suitable for passing a POSIX filer descriptor as a stream parameter to functions doing file input/output. * * \remarks The stream argument must be passed as `(void*)(uintptr_t)(int)fd`. * \sa \ref libopenmpt_c_fileio * \sa openmpt_stream_callbacks * \sa openmpt_could_open_probability2 * \sa openmpt_probe_file_header_from_stream * \sa openmpt_module_create2 */ static openmpt_stream_callbacks openmpt_stream_get_fd_callbacks(void) { openmpt_stream_callbacks retval; memset( &retval, 0, sizeof( openmpt_stream_callbacks ) ); retval.read = openmpt_stream_fd_read_func; return retval; } #ifdef __cplusplus } #endif /*! * @} */ #endif /* LIBOPENMPT_STREAM_CALLBACKS_FD_H */ libopenmpt-0.6.1+release.autotools/libopenmpt/libopenmpt_stream_callbacks_file.h0000644000175000017500000000571413413055520025325 00000000000000/* * libopenmpt_stream_callbacks_file.h * ---------------------------------- * Purpose: libopenmpt public c interface * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #ifndef LIBOPENMPT_STREAM_CALLBACKS_FILE_H #define LIBOPENMPT_STREAM_CALLBACKS_FILE_H #include "libopenmpt.h" #include #include #include #include #ifdef _MSC_VER #include /* off_t */ #endif /*! \addtogroup libopenmpt_c * @{ */ #ifdef __cplusplus extern "C" { #endif /* This stuff has to be in a header file because of possibly different MSVC CRTs which cause problems for FILE * crossing CRT boundaries. */ static size_t openmpt_stream_file_read_func( void * stream, void * dst, size_t bytes ) { FILE * f = 0; size_t retval = 0; f = (FILE*)stream; if ( !f ) { return 0; } retval = fread( dst, 1, bytes, f ); if ( retval <= 0 ) { return 0; } return retval; } static int openmpt_stream_file_seek_func( void * stream, int64_t offset, int whence ) { FILE * f = 0; int fwhence = 0; f = (FILE*)stream; if ( !f ) { return -1; } switch ( whence ) { #if defined(SEEK_SET) case OPENMPT_STREAM_SEEK_SET: fwhence = SEEK_SET; break; #endif #if defined(SEEK_CUR) case OPENMPT_STREAM_SEEK_CUR: fwhence = SEEK_CUR; break; #endif #if defined(SEEK_END) case OPENMPT_STREAM_SEEK_END: fwhence = SEEK_END; break; #endif default: return -1; break; } #if defined(_MSC_VER) return _fseeki64( f, offset, fwhence ) ? -1 : 0; #elif defined(_POSIX_SOURCE) && (_POSIX_SOURCE == 1) return fseeko( f, offset, fwhence ) ? -1 : 0; #else return fseek( f, offset, fwhence ) ? -1 : 0; #endif } static int64_t openmpt_stream_file_tell_func( void * stream ) { FILE * f = 0; int64_t retval = 0; f = (FILE*)stream; if ( !f ) { return -1; } #if defined(_MSC_VER) retval = _ftelli64( f ); #elif defined(_POSIX_SOURCE) && (_POSIX_SOURCE == 1) retval = ftello( f ); #else retval = ftell( f ); #endif if ( retval < 0 ) { return -1; } return retval; } /*! \brief Provide openmpt_stream_callbacks for standard C FILE objects * * Fills openmpt_stream_callbacks suitable for passing a standard C FILE object as a stream parameter to functions doing file input/output. * * \remarks The stream argument must be passed as `(void*)(FILE*)file`. * \sa \ref libopenmpt_c_fileio * \sa openmpt_stream_callbacks * \sa openmpt_could_open_probability2 * \sa openmpt_probe_file_header_from_stream * \sa openmpt_module_create2 */ static openmpt_stream_callbacks openmpt_stream_get_file_callbacks(void) { openmpt_stream_callbacks retval; memset( &retval, 0, sizeof( openmpt_stream_callbacks ) ); retval.read = openmpt_stream_file_read_func; retval.seek = openmpt_stream_file_seek_func; retval.tell = openmpt_stream_file_tell_func; return retval; } #ifdef __cplusplus } #endif /*! * @} */ #endif /* LIBOPENMPT_STREAM_CALLBACKS_FILE_H */ libopenmpt-0.6.1+release.autotools/libopenmpt/libopenmpt_ext.h0000644000175000017500000005053314160652110021631 00000000000000/* * libopenmpt_ext.h * ---------------- * Purpose: libopenmpt public c interface for libopenmpt extensions * Notes : * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #ifndef LIBOPENMPT_EXT_H #define LIBOPENMPT_EXT_H #include "libopenmpt_config.h" #include "libopenmpt.h" #ifdef __cplusplus extern "C" { #endif /*! * \page libopenmpt_ext_c_overview libopenmpt_ext C API * * libopenmpt_ext is included in all builds by default. * * \section libopenmpt-ext-c-detailed Detailed documentation * * \ref libopenmpt_ext_c * */ /*! \defgroup libopenmpt_ext_c libopenmpt_ext C */ /*! \addtogroup libopenmpt_ext_c * @{ */ /*! \brief Opaque type representing a libopenmpt extension module */ typedef struct openmpt_module_ext openmpt_module_ext; /*! \brief Construct an openmpt_module_ext * * \param stream_callbacks Input stream callback operations. * \param stream Input stream to load the module from. * \param logfunc Logging function where warning and errors are written. The logging function may be called throughout the lifetime of openmpt_module_ext. May be NULL. * \param loguser User-defined data associated with this module. This value will be passed to the logging callback function (logfunc) * \param errfunc Error function to define error behaviour. May be NULL. * \param erruser Error function user context. Used to pass any user-defined data associated with this module to the logging function. * \param error Pointer to an integer where an error may get stored. May be NULL. * \param error_message Pointer to a string pointer where an error message may get stored. May be NULL. * \param ctls A map of initial ctl values, see openmpt_module_get_ctls. * \return A pointer to the constructed openmpt_module_ext, or NULL on failure. * \remarks The input data can be discarded after an openmpt_module_ext has been constructed successfully. * \sa openmpt_stream_callbacks * \sa \ref libopenmpt_c_fileio * \since 0.3.0 */ LIBOPENMPT_API openmpt_module_ext * openmpt_module_ext_create( openmpt_stream_callbacks stream_callbacks, void * stream, openmpt_log_func logfunc, void * loguser, openmpt_error_func errfunc, void * erruser, int * error, const char * * error_message, const openmpt_module_initial_ctl * ctls ); /*! \brief Construct an openmpt_module_ext * * \param filedata Data to load the module from. * \param filesize Amount of data available. * \param logfunc Logging function where warning and errors are written. The logging function may be called throughout the lifetime of openmpt_module_ext. * \param loguser User-defined data associated with this module. This value will be passed to the logging callback function (logfunc) * \param errfunc Error function to define error behaviour. May be NULL. * \param erruser Error function user context. Used to pass any user-defined data associated with this module to the logging function. * \param error Pointer to an integer where an error may get stored. May be NULL. * \param error_message Pointer to a string pointer where an error message may get stored. May be NULL. * \param ctls A map of initial ctl values, see openmpt_module_get_ctls. * \return A pointer to the constructed openmpt_module_ext, or NULL on failure. * \remarks The input data can be discarded after an openmpt_module_ext has been constructed successfully. * \sa \ref libopenmpt_c_fileio * \since 0.3.0 */ LIBOPENMPT_API openmpt_module_ext * openmpt_module_ext_create_from_memory( const void * filedata, size_t filesize, openmpt_log_func logfunc, void * loguser, openmpt_error_func errfunc, void * erruser, int * error, const char * * error_message, const openmpt_module_initial_ctl * ctls ); /*! \brief Unload a previously created openmpt_module_ext from memory. * * \param mod_ext The module to unload. */ LIBOPENMPT_API void openmpt_module_ext_destroy( openmpt_module_ext * mod_ext ); /*! \brief Retrieve the openmpt_module handle from an openmpt_module_ext handle. * * \param mod_ext The extension module handle to convert * \return An equivalent openmpt_module handle to pass to standard libopenmpt functions * \since 0.3.0 */ LIBOPENMPT_API openmpt_module * openmpt_module_ext_get_module( openmpt_module_ext * mod_ext ); /*! Retrieve a libopenmpt extension. * * \param mod_ext The module handle to work on. * \param interface_id The name of the extension interface to retrieve (e.g. LIBOPENMPT_EXT_C_INTERFACE_PATTERN_VIS). * \param interface Appropriate structure of interface function pointers which is to be filled by this function (e.g. a pointer to a openmpt_module_ext_interface_pattern_vis structure). * \param interface_size Size of the interface's structure of function pointers (e.g. sizeof(openmpt_module_ext_interface_pattern_vis)). * \return 1 on success, 0 if the interface was not found. * \since 0.3.0 */ LIBOPENMPT_API int openmpt_module_ext_get_interface( openmpt_module_ext * mod_ext, const char * interface_id, void * interface, size_t interface_size ); #ifndef LIBOPENMPT_EXT_C_INTERFACE_PATTERN_VIS #define LIBOPENMPT_EXT_C_INTERFACE_PATTERN_VIS "pattern_vis" #endif /*! Pattern command type */ #define OPENMPT_MODULE_EXT_INTERFACE_PATTERN_VIS_EFFECT_TYPE_UNKNOWN 0 #define OPENMPT_MODULE_EXT_INTERFACE_PATTERN_VIS_EFFECT_TYPE_GENERAL 1 #define OPENMPT_MODULE_EXT_INTERFACE_PATTERN_VIS_EFFECT_TYPE_GLOBAL 2 #define OPENMPT_MODULE_EXT_INTERFACE_PATTERN_VIS_EFFECT_TYPE_VOLUME 3 #define OPENMPT_MODULE_EXT_INTERFACE_PATTERN_VIS_EFFECT_TYPE_PANNING 4 #define OPENMPT_MODULE_EXT_INTERFACE_PATTERN_VIS_EFFECT_TYPE_PITCH 5 typedef struct openmpt_module_ext_interface_pattern_vis { /*! Get pattern command type for pattern highlighting * * \param mod_ext The module handle to work on. * \param pattern The pattern whose data should be retrieved. * \param row The row from which the data should be retrieved. * \param channel The channel from which the data should be retrieved. * \return The command type in the effect column at the given pattern position (see OPENMPT_MODULE_EXT_INTERFACE_PATTERN_VIS_EFFECT_TYPE_*) * \sa openmpt_module_ext_interface_pattern_vis::get_pattern_row_channel_volume_effect_type */ int ( * get_pattern_row_channel_volume_effect_type ) ( openmpt_module_ext * mod_ext, int32_t pattern, int32_t row, int32_t channel ); /*! Get pattern command type for pattern highlighting * * \param mod_ext The module handle to work on. * \param pattern The pattern whose data should be retrieved. * \param row The row from which the data should be retrieved. * \param channel The channel from which the data should be retrieved. * \return The command type in the effect column at the given pattern position (see OPENMPT_MODULE_EXT_INTERFACE_PATTERN_VIS_EFFECT_TYPE_*) * \sa openmpt_module_ext_interface_pattern_vis::get_pattern_row_channel_volume_effect_type */ int ( * get_pattern_row_channel_effect_type ) ( openmpt_module_ext * mod_ext, int32_t pattern, int32_t row, int32_t channel ); } openmpt_module_ext_interface_pattern_vis; #ifndef LIBOPENMPT_EXT_C_INTERFACE_INTERACTIVE #define LIBOPENMPT_EXT_C_INTERFACE_INTERACTIVE "interactive" #endif typedef struct openmpt_module_ext_interface_interactive { /*! Set the current ticks per row (speed) * * \param mod_ext The module handle to work on. * \param speed The new tick count in range [1, 65535]. * \return 1 on success, 0 on failure. * \remarks The tick count may be reset by pattern commands at any time. * \sa openmpt_module_get_current_speed */ int ( * set_current_speed ) ( openmpt_module_ext * mod_ext, int32_t speed ); /*! Set the current module tempo * * \param mod_ext The module handle to work on. * \param tempo The new tempo in range [32, 512]. The exact meaning of the value depends on the tempo mode used by the module. * \return 1 on success, 0 on failure. * \remarks The tempo may be reset by pattern commands at any time. Use openmpt_module_ext_interface_interactive::set_tempo_factor to apply a tempo factor that is independent of pattern commands. * \sa openmpt_module_get_current_tempo */ int ( * set_current_tempo ) ( openmpt_module_ext * mod_ext, int32_t tempo ); /*! Set the current module tempo factor without affecting playback pitch * * \param mod_ext The module handle to work on. * \param factor The new tempo factor in range ]0.0, 4.0] - 1.0 means unmodified tempo. * \return 1 on success, 0 on failure. * \remarks Modifying the tempo without applying the same pitch factor using openmpt_module_ext_interface_interactive::set_pitch_factor may cause rhythmic samples (e.g. drum loops) to go out of sync. * \sa openmpt_module_ext_interface_interactive::get_tempo_factor */ int ( * set_tempo_factor ) ( openmpt_module_ext * mod_ext, double factor ); /*! Gets the current module tempo factor * * \param mod_ext The module handle to work on. * \return The current tempo factor. * \sa openmpt_module_ext_interface_interactive::set_tempo_factor */ double ( * get_tempo_factor ) ( openmpt_module_ext * mod_ext ); /*! Set the current module pitch factor without affecting playback speed * * \param mod_ext The module handle to work on. * \param factor The new pitch factor in range ]0.0, 4.0] - 1.0 means unmodified pitch. * \return 1 on success, 0 on failure. * \remarks Modifying the pitch without applying the the same tempo factor using openmpt_module_ext_interface_interactive::set_tempo_factor may cause rhythmic samples (e.g. drum loops) to go out of sync. * \remarks To shift the pich by `n` semitones, the parameter can be calculated as follows: `pow( 2.0, n / 12.0 )` * \sa openmpt_module_ext_interface_interactive::get_pitch_factor */ int ( * set_pitch_factor ) ( openmpt_module_ext * mod_ext, double factor ); /*! Gets the current module pitch factor * * \param mod_ext The module handle to work on. * \return The current pitch factor. * \sa openmpt_module_ext_interface_interactive::set_pitch_factor */ double ( * get_pitch_factor ) ( openmpt_module_ext * mod_ext ); /*! Set the current global volume * * \param mod_ext The module handle to work on. * \param volume The new global volume in range [0.0, 1.0] * \return 1 on success, 0 on failure. * \remarks The global volume may be reset by pattern commands at any time. Use openmpt_module_set_render_param to apply a global overall volume factor that is independent of pattern commands. * \sa openmpt_module_ext_interface_interactive::get_global_volume */ int ( * set_global_volume ) ( openmpt_module_ext * mod_ext, double volume ); /*! Get the current global volume * * \param mod_ext The module handle to work on. * \return The current global volume in range [0.0, 1.0] * \sa openmpt_module_ext_interface_interactive::set_global_volume */ double ( * get_global_volume ) ( openmpt_module_ext * mod_ext ); /*! Set the current channel volume for a channel * * \param mod_ext The module handle to work on. * \param channel The channel whose volume should be set, in range [0, openmpt_module_get_num_channels()[ * \param volume The new channel volume in range [0.0, 1.0] * \return 1 on success, 0 on failure (channel out of range). * \remarks The channel volume may be reset by pattern commands at any time. * \sa openmpt_module_ext_interface_interactive::get_channel_volume */ int ( * set_channel_volume ) ( openmpt_module_ext * mod_ext, int32_t channel, double volume ); /*! Get the current channel volume for a channel * * \param mod_ext The module handle to work on. * \param channel The channel whose volume should be retrieved, in range [0, openmpt_module_get_num_channels()[ * \return The current channel volume in range [0.0, 1.0] * \sa openmpt_module_ext_interface_interactive::set_channel_volume */ double ( * get_channel_volume ) ( openmpt_module_ext * mod_ext, int32_t channel ); /*! Set the current mute status for a channel * * \param mod_ext The module handle to work on. * \param channel The channel whose mute status should be set, in range [0, openmpt_module_get_num_channels()[ * \param mute The new mute status. true is muted, false is unmuted. * \return 1 on success, 0 on failure (channel out of range). * \sa openmpt_module_ext_interface_interactive::get_channel_mute_status */ int ( * set_channel_mute_status ) ( openmpt_module_ext * mod_ext, int32_t channel, int mute ); /*! Get the current mute status for a channel * * \param mod_ext The module handle to work on. * \param channel The channel whose mute status should be retrieved, in range [0, openmpt_module_get_num_channels()[ * \return The current channel mute status. 1 is muted, 0 is unmuted, -1 means the instrument was out of range * \sa openmpt_module_ext_interface_interactive::set_channel_mute_status */ int ( * get_channel_mute_status ) ( openmpt_module_ext * mod_ext, int32_t channel ); /*! Set the current mute status for an instrument * * \param mod_ext The module handle to work on. * \param instrument The instrument whose mute status should be set, in range [0, openmpt_module_get_num_instruments()[ if openmpt_module_get_num_instruments is not 0, otherwise in [0, openmpt_module_get_num_samples()[ * \param mute The new mute status. true is muted, false is unmuted. * \return 1 on success, 0 on failure (instrument out of range). * \sa openmpt_module_ext_interface_interactive::get_instrument_mute_status */ int ( * set_instrument_mute_status ) ( openmpt_module_ext * mod_ext, int32_t instrument, int mute ); /*! Get the current mute status for an instrument * * \param mod_ext The module handle to work on. * \param instrument The instrument whose mute status should be retrieved, in range [0, openmpt_module_get_num_instruments()[ if openmpt_module_get_num_instruments is not 0, otherwise in [0, openmpt_module_get_num_samples()[ * \return The current instrument mute status. 1 is muted, 0 is unmuted, -1 means the instrument was out of range * \sa openmpt_module_ext_interface_interactive::set_instrument_mute_status */ int ( * get_instrument_mute_status ) ( openmpt_module_ext * mod_ext, int32_t instrument ); /*! Play a note using the specified instrument * * \param mod_ext The module handle to work on. * \param instrument The instrument that should be played, in range [0, openmpt_module_get_num_instruments()[ if openmpt_module_get_num_instruments is not 0, otherwise in [0, openmpt_module_get_num_samples()[ * \param note The note to play, in rage [0, 119]. 60 is the middle C. * \param volume The volume at which the note should be triggered, in range [0.0, 1.0] * \param panning The panning position at which the note should be triggered, in range [-1.0, 1.0], 0.0 is center. * \return The channel on which the note is played. This can pe be passed to openmpt_module_ext_interface_interactive::stop_note to stop the note. -1 means that no channel could be allocated and the note is not played. * \sa openmpt_module_ext_interface_interactive::stop_note * \sa openmpt_module_ext_interface_interactive2::note_off * \sa openmpt_module_ext_interface_interactive2::note_fade */ int32_t ( * play_note ) ( openmpt_module_ext * mod_ext, int32_t instrument, int32_t note, double volume, double panning ); /*! Stop the note playing on the specified channel * * \param mod_ext The module handle to work on. * \param channel The channel on which the note should be stopped. This is the value returned by a previous play_note call. * \return 1 on success, 0 on failure (channel out of range). * \sa openmpt_module_ext_interface_interactive::play_note * \sa openmpt_module_ext_interface_interactive2::note_off * \sa openmpt_module_ext_interface_interactive2::note_fade */ int ( * stop_note ) ( openmpt_module_ext * mod_ext, int32_t channel ); } openmpt_module_ext_interface_interactive; #ifndef LIBOPENMPT_EXT_C_INTERFACE_INTERACTIVE2 #define LIBOPENMPT_EXT_C_INTERFACE_INTERACTIVE2 "interactive2" #endif typedef struct openmpt_module_ext_interface_interactive2 { //! Sends a key-off command for the note playing on the specified channel /*! * \param mod_ext The module handle to work on. * \param channel The channel on which the key-off event should be triggered. This is the value returned by a previous play_note call. * \return 1 on success, 0 on failure (channel out of range). * \remarks This method releases envelopes and sample sustain loops. If the sample has no sustain loop, or if the module does not use instruments, it does nothing. * \sa openmpt_module_ext_interface_interactive::play_note * \sa openmpt_module_ext_interface_interactive::stop_note * \sa openmpt_module_ext_interface_interactive2::note_fade * \since 0.6.0 */ int ( *note_off ) ( openmpt_module_ext * mod_ext, int32_t channel ); //! Sends a note fade command for the note playing on the specified channel /*! * \param mod_ext The module handle to work on. * \param channel The channel on which the note should be faded. This is the value returned by a previous play_note call. * \return 1 on success, 0 on failure (channel out of range). * \remarks This method uses the instrument's fade-out value. If the module does not use instruments, or the instrument's fade-out value is 0, it does nothing. * \sa openmpt_module_ext_interface_interactive::play_note * \sa openmpt_module_ext_interface_interactive::stop_note * \sa openmpt_module_ext_interface_interactive2::note_fade * \since 0.6.0 */ int ( *note_fade ) ( openmpt_module_ext * mod_ext, int32_t channel ); //! Set the current panning for a channel /*! * \param mod_ext The module handle to work on. * \param channel The channel that should be panned. This is the value returned by a previous play_note call. * \param panning The panning position to set on the channel, in range [-1.0, 1.0], 0.0 is center. * \return 1 on success, 0 on failure (channel out of range). * \remarks This command affects subsequent notes played on the same channel, and may itself be overridden by subsequent panning commands encountered in the module itself. * \sa openmpt_module_ext_interface_interactive2::get_channel_panning * \since 0.6.0 */ int ( *set_channel_panning) ( openmpt_module_ext * mod_ext, int32_t channel, double panning ); //! Get the current panning position for a channel /*! * \param mod_ext The module handle to work on. * \param channel The channel whose panning should be retrieved. This is the value returned by a previous play_note call. * \return The current channel panning, in range [-1.0, 1.0], 0.0 is center. * \sa openmpt_module_ext_interface_interactive2::set_channel_panning * \since 0.6.0 */ double (*get_channel_panning) ( openmpt_module_ext * mod_ext, int32_t channel ); //! Set the finetune for the currently playing note on a channel /*! * \param mod_ext The module handle to work on. * \param channel The channel whose finetune will be changed, in range [0, openmpt::module::get_num_channels()[ * \param finetune The finetune to set on the channel, in range [-1.0, 1.0], 0.0 is center. * \throws openmpt::exception Throws an exception derived from openmpt::exception if the channel index is invalid. * \remarks The finetune range depends on the pitch wheel depth of the instrument playing on the current channel; for sample-based modules, the depth of this command is fixed to +/-1 semitone. * \remarks This command does not affect subsequent notes played on the same channel, but may itself be overridden by subsequent finetune commands encountered in the module itself. * \sa openmpt_module_ext_interface_interactive2::get_note_finetune * \since 0.6.0 */ int ( *set_note_finetune) ( openmpt_module_ext * mod_ext, int32_t channel, double finetune ); //! Get the finetune for the currently playing note on a channel /*! * \param mod_ext The module handle to work on. * \param channel The channel whose finetune should be retrieved, in range [0, openmpt::module::get_num_channels()[ * \return The current channel finetune, in range [-1.0, 1.0], 0.0 is center. * \remarks The finetune range depends on the pitch wheel depth of the instrument playing on the current channel; for sample-based modules, the depth of this command is fixed to +/-1 semitone. * \throws openmpt::exception Throws an exception derived from openmpt::exception if the channel is outside the specified range. * \sa openmpt_module_ext_interface_interactive2::set_note_finetune * \since 0.6.0 */ double (*get_note_finetune) ( openmpt_module_ext * mod_ext, int32_t channel ); } openmpt_module_ext_interface_interactive2; /* add stuff here */ #ifdef __cplusplus } #endif /*! * @} */ #endif /* LIBOPENMPT_EXT_H */ libopenmpt-0.6.1+release.autotools/libopenmpt/libopenmpt_ext.hpp0000644000175000017500000004366514160652110022201 00000000000000/* * libopenmpt_ext.hpp * ------------------ * Purpose: libopenmpt public c++ interface for libopenmpt extensions * Notes : * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #ifndef LIBOPENMPT_EXT_HPP #define LIBOPENMPT_EXT_HPP #include "libopenmpt_config.h" #include "libopenmpt.hpp" /*! * \page libopenmpt_ext_cpp_overview libopenmpt_ext C++ API * * libopenmpt_ext is now included in all builds by default. * * \section libopenmpt-ext-cpp-detailed Detailed documentation * * \ref libopenmpt_ext_cpp * */ /*! \defgroup libopenmpt_ext_cpp libopenmpt_ext C++ */ /*! \addtogroup libopenmpt_ext_cpp @{ */ namespace openmpt { class module_ext_impl; class LIBOPENMPT_CXX_API module_ext : public module { private: module_ext_impl * ext_impl; private: // non-copyable module_ext( const module_ext & ); void operator = ( const module_ext & ); public: module_ext( std::istream & stream, std::ostream & log = std::clog, const std::map< std::string, std::string > & ctls = detail::initial_ctls_map() ); module_ext( const std::vector & data, std::ostream & log = std::clog, const std::map< std::string, std::string > & ctls = detail::initial_ctls_map() ); module_ext( const std::vector & data, std::ostream & log = std::clog, const std::map< std::string, std::string > & ctls = detail::initial_ctls_map() ); module_ext( const std::vector & data, std::ostream & log = std::clog, const std::map< std::string, std::string > & ctls = detail::initial_ctls_map() ); module_ext( const std::byte * data, std::size_t size, std::ostream & log = std::clog, const std::map< std::string, std::string > & ctls = detail::initial_ctls_map() ); module_ext( const std::uint8_t * data, std::size_t size, std::ostream & log = std::clog, const std::map< std::string, std::string > & ctls = detail::initial_ctls_map() ); module_ext( const char * data, std::size_t size, std::ostream & log = std::clog, const std::map< std::string, std::string > & ctls = detail::initial_ctls_map() ); module_ext( const void * data, std::size_t size, std::ostream & log = std::clog, const std::map< std::string, std::string > & ctls = detail::initial_ctls_map() ); virtual ~module_ext(); public: //! Retrieve a libopenmpt extension. /*! Example: Retrieving the interactive extension to change the tempo of a module: \code{.cpp} openmpt::module_ext *mod = new openmpt::module_ext( stream ); #ifdef LIBOPENMPT_EXT_INTERFACE_INTERACTIVE openmpt::ext::interactive *interactive = static_cast( self->mod->get_interface( openmpt::ext::interactive_id ) ); if ( interactive ) { interactive->set_tempo_factor( 2.0 ); // play module at double speed } else { // interface not available } #else // interfae not available #endif \endcode \param interface_id The name of the extension interface to retrieve. \return The interface object. This may be a nullptr if the extension was not found. */ void * get_interface( const std::string & interface_id ); }; // class module_ext namespace ext { #define LIBOPENMPT_DECLARE_EXT_CXX_INTERFACE(name) \ static const char name ## _id [] = # name ; \ class name; \ /**/ #define LIBOPENMPT_EXT_CXX_INTERFACE(name) \ protected: \ name () {} \ virtual ~ name () {} \ public: \ /**/ #ifndef LIBOPENMPT_EXT_INTERFACE_PATTERN_VIS #define LIBOPENMPT_EXT_INTERFACE_PATTERN_VIS #endif LIBOPENMPT_DECLARE_EXT_CXX_INTERFACE(pattern_vis) class pattern_vis { LIBOPENMPT_EXT_CXX_INTERFACE(pattern_vis) //! Pattern command type enum effect_type { effect_unknown = 0, effect_general = 1, effect_global = 2, effect_volume = 3, effect_panning = 4, effect_pitch = 5 }; // enum effect_type //! Get pattern command type for pattern highlighting /*! \param pattern The pattern whose data should be retrieved. \param row The row from which the data should be retrieved. \param channel The channel from which the data should be retrieved. \return The command type in the effect column at the given pattern position (see openmpt::ext::pattern_vis::effect_type) \sa openmpt::ext::pattern_vis::get_pattern_row_channel_effect_type */ virtual effect_type get_pattern_row_channel_volume_effect_type( std::int32_t pattern, std::int32_t row, std::int32_t channel ) const = 0; //! Get pattern command type for pattern highlighting /*! \param pattern The pattern whose data should be retrieved. \param row The row from which the data should be retrieved. \param channel The channel from which the data should be retrieved. \return The command type in the volume column at the given pattern position (see openmpt::ext::pattern_vis::effect_type) \sa openmpt::ext::pattern_vis::get_pattern_row_channel_volume_effect_type */ virtual effect_type get_pattern_row_channel_effect_type( std::int32_t pattern, std::int32_t row, std::int32_t channel ) const = 0; }; // class pattern_vis #ifndef LIBOPENMPT_EXT_INTERFACE_INTERACTIVE #define LIBOPENMPT_EXT_INTERFACE_INTERACTIVE #endif LIBOPENMPT_DECLARE_EXT_CXX_INTERFACE(interactive) class interactive { LIBOPENMPT_EXT_CXX_INTERFACE(interactive) //! Set the current ticks per row (speed) /*! \param speed The new tick count in range [1, 65535]. \throws openmpt::exception Throws an exception derived from openmpt::exception if the speed is outside the specified range. \remarks The tick count may be reset by pattern commands at any time. \sa openmpt::module::get_current_speed */ virtual void set_current_speed( std::int32_t speed ) = 0; //! Set the current module tempo /*! \param tempo The new tempo in range [32, 512]. The exact meaning of the value depends on the tempo mode used by the module. \throws openmpt::exception Throws an exception derived from openmpt::exception if the tempo is outside the specified range. \remarks The tempo may be reset by pattern commands at any time. Use openmpt::ext:interactive::set_tempo_factor to apply a tempo factor that is independent of pattern commands. \sa openmpt::module::get_current_tempo */ virtual void set_current_tempo( std::int32_t tempo ) = 0; //! Set the current module tempo factor without affecting playback pitch /*! \param factor The new tempo factor in range ]0.0, 4.0] - 1.0 means unmodified tempo. \throws openmpt::exception Throws an exception derived from openmpt::exception if the factor is outside the specified range. \remarks Modifying the tempo without applying the same pitch factor using openmpt::ext::interactive::set_pitch_factor may cause rhythmic samples (e.g. drum loops) to go out of sync. \sa openmpt::ext::interactive::get_tempo_factor */ virtual void set_tempo_factor( double factor ) = 0; //! Gets the current module tempo factor /*! \return The current tempo factor. \sa openmpt::ext::interactive::set_tempo_factor */ virtual double get_tempo_factor( ) const = 0; //! Set the current module pitch factor without affecting playback speed /*! \param factor The new pitch factor in range ]0.0, 4.0] - 1.0 means unmodified pitch. \throws openmpt::exception Throws an exception derived from openmpt::exception if the factor is outside the specified range. \remarks Modifying the pitch without applying the the same tempo factor using openmpt::ext::interactive::set_tempo_factor may cause rhythmic samples (e.g. drum loops) to go out of sync. \remarks To shift the pich by `n` semitones, the parameter can be calculated as follows: `pow( 2.0, n / 12.0 )` \sa openmpt::ext::interactive::get_pitch_factor */ virtual void set_pitch_factor( double factor ) = 0; //! Gets the current module pitch factor /*! \return The current pitch factor. \throws openmpt::exception Throws an exception derived from openmpt::exception if the pitch is outside the specified range. \sa openmpt::ext::interactive::set_pitch_factor */ virtual double get_pitch_factor( ) const = 0; //! Set the current global volume /*! \param volume The new global volume in range [0.0, 1.0] \throws openmpt::exception Throws an exception derived from openmpt::exception if the volume is outside the specified range. \remarks The global volume may be reset by pattern commands at any time. Use openmpt::module::set_render_param to apply a global overall volume factor that is independent of pattern commands. \sa openmpt::ext::interactive::get_global_volume */ virtual void set_global_volume( double volume ) = 0; //! Get the current global volume /*! \return The current global volume in range [0.0, 1.0] \sa openmpt::ext::interactive::set_global_volume */ virtual double get_global_volume( ) const = 0; //! Set the current channel volume for a channel /*! \param channel The channel whose volume should be set, in range [0, openmpt::module::get_num_channels()[ \param volume The new channel volume in range [0.0, 1.0] \throws openmpt::exception Throws an exception derived from openmpt::exception if the channel or volume is outside the specified range. \remarks The channel volume may be reset by pattern commands at any time. \sa openmpt::ext::interactive::get_channel_volume */ virtual void set_channel_volume( std::int32_t channel, double volume ) = 0; //! Get the current channel volume for a channel /*! \param channel The channel whose volume should be retrieved, in range [0, openmpt::module::get_num_channels()[ \return The current channel volume in range [0.0, 1.0] \throws openmpt::exception Throws an exception derived from openmpt::exception if the channel is outside the specified range. \sa openmpt::ext::interactive::set_channel_volume */ virtual double get_channel_volume( std::int32_t channel ) const = 0; //! Set the current mute status for a channel /*! \param channel The channel whose mute status should be set, in range [0, openmpt::module::get_num_channels()[ \param mute The new mute status. true is muted, false is unmuted. \throws openmpt::exception Throws an exception derived from openmpt::exception if the channel is outside the specified range. \sa openmpt::ext::interactive::get_channel_mute_status */ virtual void set_channel_mute_status( std::int32_t channel, bool mute ) = 0; //! Get the current mute status for a channel /*! \param channel The channel whose mute status should be retrieved, in range [0, openmpt::module::get_num_channels()[ \return The current channel mute status. true is muted, false is unmuted. \throws openmpt::exception Throws an exception derived from openmpt::exception if the channel is outside the specified range. \sa openmpt::ext::interactive::set_channel_mute_status */ virtual bool get_channel_mute_status( std::int32_t channel ) const = 0; //! Set the current mute status for an instrument /*! \param instrument The instrument whose mute status should be set, in range [0, openmpt::module::get_num_instruments()[ if openmpt::module::get_num_instruments is not 0, otherwise in [0, openmpt::module::get_num_samples()[ \param mute The new mute status. true is muted, false is unmuted. \throws openmpt::exception Throws an exception derived from openmpt::exception if the instrument is outside the specified range. \sa openmpt::ext::interactive::get_instrument_mute_status */ virtual void set_instrument_mute_status( std::int32_t instrument, bool mute ) = 0; //! Get the current mute status for an instrument /*! \param instrument The instrument whose mute status should be retrieved, in range [0, openmpt::module::get_num_instruments()[ if openmpt::module::get_num_instruments is not 0, otherwise in [0, openmpt::module::get_num_samples()[ \return The current instrument mute status. true is muted, false is unmuted. \throws openmpt::exception Throws an exception derived from openmpt::exception if the instrument is outside the specified range. \sa openmpt::ext::interactive::set_instrument_mute_status */ virtual bool get_instrument_mute_status( std::int32_t instrument ) const = 0; //! Play a note using the specified instrument /*! \param instrument The instrument that should be played, in range [0, openmpt::module::get_num_instruments()[ if openmpt::module::get_num_instruments is not 0, otherwise in [0, openmpt::module::get_num_samples()[ \param note The note to play, in rage [0, 119]. 60 is the middle C. \param volume The volume at which the note should be triggered, in range [0.0, 1.0] \param panning The panning position at which the note should be triggered, in range [-1.0, 1.0], 0.0 is center. \return The channel on which the note is played. This can pe be passed to openmpt::ext::interactive::stop_note to stop the note. \throws openmpt::exception Throws an exception derived from openmpt::exception if the instrument or note is outside the specified range. \sa openmpt::ext::interactive::stop_note \sa openmpt::ext::interactive2::note_off \sa openmpt::ext::interactive2::note_fade */ virtual std::int32_t play_note( std::int32_t instrument, std::int32_t note, double volume, double panning ) = 0; //! Stop the note playing on the specified channel /*! \param channel The channel on which the note should be stopped. This is the value returned by a previous play_note call. \throws openmpt::exception Throws an exception derived from openmpt::exception if the channel index is invalid. \sa openmpt::ext::interactive::play_note \sa openmpt::ext::interactive2::note_off \sa openmpt::ext::interactive2::note_fade */ virtual void stop_note( std::int32_t channel ) = 0; }; // class interactive #ifndef LIBOPENMPT_EXT_INTERFACE_INTERACTIVE2 #define LIBOPENMPT_EXT_INTERFACE_INTERACTIVE2 #endif LIBOPENMPT_DECLARE_EXT_CXX_INTERFACE(interactive2) class interactive2 { LIBOPENMPT_EXT_CXX_INTERFACE(interactive2) //! Sends a key-off command for the note playing on the specified channel /*! \param channel The channel on which the key-off event should be triggered. This is the value returned by a previous play_note call. \throws openmpt::exception Throws an exception derived from openmpt::exception if the channel index is invalid. \remarks This method releases envelopes and sample sustain loops. If the sample has no sustain loop, or if the module does not use instruments, it does nothing. \sa openmpt::ext::interactive::play_note \sa openmpt::ext::interactive::stop_note \sa openmpt::ext::interactive2::note_fade \since 0.6.0 */ virtual void note_off(int32_t channel ) = 0; //! Sends a note fade command for the note playing on the specified channel /*! \param channel The channel on which the note should be faded. This is the value returned by a previous play_note call. \throws openmpt::exception Throws an exception derived from openmpt::exception if the channel index is invalid. \remarks This method uses the instrument's fade-out value. If the module does not use instruments, or the instrument's fade-out value is 0, it does nothing. \sa openmpt::ext::interactive::play_note \sa openmpt::ext::interactive::stop_note \sa openmpt::ext::interactive2::note_off \since 0.6.0 */ virtual void note_fade(int32_t channel) = 0; //! Set the current panning position for a channel /*! \param channel The channel whose panning will be changed, in range [0, openmpt::module::get_num_channels()[ \param panning The panning position to set on the channel, in range [-1.0, 1.0], 0.0 is center. \throws openmpt::exception Throws an exception derived from openmpt::exception if the channel index is invalid. \remarks This command affects subsequent notes played on the same channel, and may itself be overridden by subsequent panning commands encountered in the module itself. \sa openmpt::ext::interactive2::get_channel_panning \since 0.6.0 */ virtual void set_channel_panning(int32_t channel, double panning ) = 0; //! Get the current panning position for a channel /*! \param channel The channel whose panning should be retrieved, in range [0, openmpt::module::get_num_channels()[ \return The current channel panning, in range [-1.0, 1.0], 0.0 is center. \throws openmpt::exception Throws an exception derived from openmpt::exception if the channel is outside the specified range. \sa openmpt::ext::interactive2::set_channel_panning \since 0.6.0 */ virtual double get_channel_panning( int32_t channel ) = 0; //! Set the finetune for the currently playing note on a channel /*! \param channel The channel whose finetune will be changed, in range [0, openmpt::module::get_num_channels()[ \param finetune The finetune to set on the channel, in range [-1.0, 1.0], 0.0 is center. \throws openmpt::exception Throws an exception derived from openmpt::exception if the channel index is invalid. \remarks The finetune range depends on the pitch wheel depth of the instrument playing on the current channel; for sample-based modules, the depth of this command is fixed to +/-1 semitone. \remarks This command does not affect subsequent notes played on the same channel, but may itself be overridden by subsequent finetune commands encountered in the module itself. \sa openmpt::ext::interactive2::get_note_finetune \since 0.6.0 */ virtual void set_note_finetune(int32_t channel, double finetune ) = 0; //! Get the finetune for the currently playing note on a channel /*! \param channel The channel whose finetune should be retrieved, in range [0, openmpt::module::get_num_channels()[ \return The current channel finetune, in range [-1.0, 1.0], 0.0 is center. \throws openmpt::exception Throws an exception derived from openmpt::exception if the channel is outside the specified range. \remarks The finetune range depends on the pitch wheel depth of the instrument playing on the current channel; for sample-based modules, the depth of this command is fixed to +/-1 semitone. \sa openmpt::ext::interactive2::set_note_finetune \since 0.6.0 */ virtual double get_note_finetune( int32_t channel ) = 0; }; // class interactive /* add stuff here */ #undef LIBOPENMPT_DECLARE_EXT_CXX_INTERFACE #undef LIBOPENMPT_EXT_CXX_INTERFACE } // namespace ext } // namespace openmpt /*! @} */ #endif // LIBOPENMPT_EXT_HPP libopenmpt-0.6.1+release.autotools/libopenmpt/libopenmpt.pc.in0000644000175000017500000000047112740237634021541 00000000000000 prefix=@prefix@ exec_prefix=@exec_prefix@ includedir=@includedir@ libdir=@libdir@ Name: libopenmpt Description: Tracker module player based on OpenMPT Version: @VERSION@ Requires.private: @LIBOPENMPT_REQUIRES_PRIVATE@ Libs: -L${libdir} -lopenmpt Libs.private: @LIBOPENMPT_LIBS_PRIVATE@ Cflags: -I${includedir} libopenmpt-0.6.1+release.autotools/libopenmpt/libopenmpt_c.cpp0000644000175000017500000016231614151346226021621 00000000000000/* * libopenmpt_c.cpp * ---------------- * Purpose: libopenmpt C interface implementation * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "openmpt/all/BuildSettings.hpp" #include "libopenmpt_internal.h" #include "libopenmpt.h" #include "libopenmpt_ext.h" #include "libopenmpt_impl.hpp" #include "libopenmpt_ext_impl.hpp" #include #include #include #include #include #include #include #if defined(_MSC_VER) #pragma warning(disable:4702) /* unreachable code */ #endif namespace openmpt { static const char * strdup( const char * src ) { char * dst = (char*)std::calloc( std::strlen( src ) + 1, sizeof( char ) ); if ( !dst ) { return NULL; } std::strcpy( dst, src ); return dst; } class logfunc_logger : public log_interface { private: openmpt_log_func m_logfunc; void * m_user; public: logfunc_logger( openmpt_log_func func, void * user ) : m_logfunc(func), m_user(user) { return; } void log( const std::string & message ) const override { if ( m_logfunc ) { m_logfunc( message.c_str(), m_user ); } else { openmpt_log_func_default( message.c_str(), m_user ); } } }; // class logfunc_logger namespace interface { class invalid_module_pointer : public openmpt::exception { public: invalid_module_pointer() : openmpt::exception("module * not valid") { return; } invalid_module_pointer(const invalid_module_pointer&) = default; virtual ~invalid_module_pointer() noexcept = default; }; class argument_null_pointer : public openmpt::exception { public: argument_null_pointer() : openmpt::exception("argument null pointer") { return; } argument_null_pointer(const argument_null_pointer&) = default; virtual ~argument_null_pointer() noexcept = default; }; } // namespace interface static std::string format_exception( const char * const function ) { std::string err; try { // cppcheck false-positive // cppcheck-suppress rethrowNoCurrentException throw; } catch ( const openmpt::exception & e ) { err += function; err += ": "; err += "ERROR: "; const char * what = e.what(); err += what ? what : ""; } catch ( const std::bad_alloc & e ) { err += function; err += ": "; err += "OUT OF MEMORY: "; const char * what = e.what(); err += what ? what : ""; } catch ( const std::exception & e ) { err += function; err += ": "; err += "INTERNAL ERROR: "; const char * what = e.what(); err += what ? what : ""; } catch ( ... ) { err += function; err += ": "; err += "UNKNOWN INTERNAL ERROR"; } return err; } static void error_message_from_exception( const char * * error_message, const std::exception & e ) { if ( error_message ) { const char * what = e.what(); *error_message = ( what ? openmpt::strdup( what ) : openmpt::strdup( "" ) ); } } static int error_from_exception( const char * * error_message ) { int error = 0; if ( error_message ) { if ( *error_message ) { openmpt_free_string( *error_message ); *error_message = NULL; } } try { // cppcheck false-positive // cppcheck-suppress rethrowNoCurrentException throw; } catch ( const std::bad_alloc & e ) { error = OPENMPT_ERROR_OUT_OF_MEMORY; error_message_from_exception( error_message, e ); } catch ( const openmpt::interface::invalid_module_pointer & e ) { error = OPENMPT_ERROR_INVALID_MODULE_POINTER; error_message_from_exception( error_message, e ); } catch ( const openmpt::interface::argument_null_pointer & e ) { error = OPENMPT_ERROR_ARGUMENT_NULL_POINTER; error_message_from_exception( error_message, e ); } catch ( const openmpt::exception & e ) { error = OPENMPT_ERROR_GENERAL; error_message_from_exception( error_message, e ); } catch ( const std::invalid_argument & e ) { error = OPENMPT_ERROR_INVALID_ARGUMENT; error_message_from_exception( error_message, e ); } catch ( const std::out_of_range & e ) { error = OPENMPT_ERROR_OUT_OF_RANGE; error_message_from_exception( error_message, e ); } catch ( const std::length_error & e ) { error = OPENMPT_ERROR_LENGTH; error_message_from_exception( error_message, e ); } catch ( const std::domain_error & e ) { error = OPENMPT_ERROR_DOMAIN; error_message_from_exception( error_message, e ); } catch ( const std::logic_error & e ) { error = OPENMPT_ERROR_LOGIC; error_message_from_exception( error_message, e ); } catch ( const std::underflow_error & e ) { error = OPENMPT_ERROR_UNDERFLOW; error_message_from_exception( error_message, e ); } catch ( const std::overflow_error & e ) { error = OPENMPT_ERROR_OVERFLOW; error_message_from_exception( error_message, e ); } catch ( const std::range_error & e ) { error = OPENMPT_ERROR_RANGE; error_message_from_exception( error_message, e ); } catch ( const std::runtime_error & e ) { error = OPENMPT_ERROR_RUNTIME; error_message_from_exception( error_message, e ); } catch ( const std::exception & e ) { error = OPENMPT_ERROR_EXCEPTION; error_message_from_exception( error_message, e ); } catch ( ... ) { error = OPENMPT_ERROR_UNKNOWN; } return error; } } // namespace openmpt extern "C" { struct openmpt_module { openmpt_log_func logfunc; void * loguser; openmpt_error_func errfunc; void * erruser; int error; const char * error_message; openmpt::module_impl * impl; }; struct openmpt_module_ext { openmpt_module mod; openmpt::module_ext_impl * impl; }; } // extern "C" namespace openmpt { static void do_report_exception( const char * const function, openmpt_log_func const logfunc = 0, void * const loguser = 0, openmpt_error_func errfunc = 0, void * const erruser = 0, openmpt::module_impl * const impl = 0, openmpt_module * const mod = 0, int * const err = 0, const char * * err_msg = 0 ) { int error = OPENMPT_ERROR_OK; const char * error_message = NULL; int error_func_result = OPENMPT_ERROR_FUNC_RESULT_DEFAULT; if ( errfunc || mod || err || err_msg ) { error = error_from_exception( mod ? &error_message : NULL ); } if ( errfunc ) { error_func_result = errfunc( error, erruser ); } if ( mod && ( error_func_result & OPENMPT_ERROR_FUNC_RESULT_STORE ) ) { mod->error = error; mod->error_message = ( error_message ? openmpt::strdup( error_message ) : openmpt::strdup( "" ) ); } if ( err ) { *err = error; } if ( err_msg ) { *err_msg = ( error_message ? openmpt::strdup( error_message ) : openmpt::strdup( "" ) ); } if ( error_message ) { openmpt_free_string( error_message ); error_message = NULL; } if ( error_func_result & OPENMPT_ERROR_FUNC_RESULT_LOG ) { try { const std::string message = format_exception( function ); if ( impl ) { impl->PushToCSoundFileLog( message ); } else if ( logfunc ) { logfunc( message.c_str(), loguser ); } else { openmpt_log_func_default( message.c_str(), NULL ); } } catch ( ... ) { fprintf( stderr, "openmpt: %s:%i: UNKNOWN INTERNAL ERROR in error handling: function='%s', logfunc=%p, loguser=%p, errfunc=%p, erruser=%p, impl=%p\n", __FILE__, static_cast( __LINE__ ), function ? function : "", reinterpret_cast( logfunc ), loguser, reinterpret_cast( errfunc ), erruser, static_cast( impl ) ); fflush( stderr ); } } } static void report_exception( const char * const function, openmpt_module * mod = 0, int * error = 0, const char * * error_message = 0 ) { do_report_exception( function, mod ? mod->logfunc : NULL, mod ? mod->loguser : NULL, mod ? mod->errfunc : NULL, mod ? mod->erruser : NULL, mod ? mod->impl : 0, mod ? mod : NULL, error ? error : NULL, error_message ? error_message : NULL ); } static void report_exception( const char * const function, openmpt_log_func const logfunc, void * const loguser, openmpt_error_func errfunc, void * const erruser, int * error, const char * * error_message ) { do_report_exception( function, logfunc, loguser, errfunc, erruser, 0, 0, error, error_message ); } namespace interface { template < typename T > void check_soundfile( T * mod ) { if ( !mod ) { throw openmpt::interface::invalid_module_pointer(); } } template < typename T > void check_pointer( T * p ) { if ( !p ) { throw openmpt::interface::argument_null_pointer(); } } } // namespace interface } // namespace openmpt extern "C" { uint32_t openmpt_get_library_version(void) { try { return openmpt::get_library_version(); } catch ( ... ) { openmpt::report_exception( __func__ ); } return 0; } uint32_t openmpt_get_core_version(void) { try { return openmpt::get_core_version(); } catch ( ... ) { openmpt::report_exception( __func__ ); } return 0; } void openmpt_free_string( const char * str ) { try { std::free( const_cast< char * >( str ) ); } catch ( ... ) { openmpt::report_exception( __func__ ); } return; } const char * openmpt_get_string( const char * key ) { try { if ( !key ) { return openmpt::strdup( "" ); } return openmpt::strdup( openmpt::string::get( key ).c_str() ); } catch ( ... ) { openmpt::report_exception( __func__ ); } return NULL; } const char * openmpt_get_supported_extensions(void) { try { std::string retval; bool first = true; std::vector supported_extensions = openmpt::module_impl::get_supported_extensions(); for ( std::vector::iterator i = supported_extensions.begin(); i != supported_extensions.end(); ++i ) { if ( first ) { first = false; } else { retval += ";"; } retval += *i; } return openmpt::strdup( retval.c_str() ); } catch ( ... ) { openmpt::report_exception( __func__ ); } return NULL; } int openmpt_is_extension_supported( const char * extension ) { try { if ( !extension ) { return 0; } return openmpt::module_impl::is_extension_supported( extension ) ? 1 : 0; } catch ( ... ) { openmpt::report_exception( __func__ ); } return 0; } void openmpt_log_func_default( const char * message, void * /*user*/ ) { fprintf( stderr, "openmpt: %s\n", message ); fflush( stderr ); } void openmpt_log_func_silent( const char * /*message*/ , void * /*user*/ ) { return; } int openmpt_error_is_transient( int error ) { int result = 0; switch ( error ) { case OPENMPT_ERROR_OUT_OF_MEMORY: result = 1; break; default: result = 0; break; } return result; } const char * openmpt_error_string( int error ) { const char * text = "unknown error"; switch ( error ) { case OPENMPT_ERROR_OK: text = ""; break; case OPENMPT_ERROR_UNKNOWN: text = "unknown internal error"; break; case OPENMPT_ERROR_EXCEPTION: text = "unknown exception "; break; case OPENMPT_ERROR_OUT_OF_MEMORY: text = "out of memory"; break; case OPENMPT_ERROR_RUNTIME: text = "runtime error"; break; case OPENMPT_ERROR_RANGE: text = "range error"; break; case OPENMPT_ERROR_OVERFLOW: text = "arithmetic overflow"; break; case OPENMPT_ERROR_UNDERFLOW: text = "arithmetic underflow"; break; case OPENMPT_ERROR_LOGIC: text = "logic error"; break; case OPENMPT_ERROR_DOMAIN: text = "value domain error"; break; case OPENMPT_ERROR_LENGTH: text = "maximum supported size exceeded"; break; case OPENMPT_ERROR_OUT_OF_RANGE: text = "argument out of range"; break; case OPENMPT_ERROR_INVALID_ARGUMENT: text = "invalid argument"; break; case OPENMPT_ERROR_GENERAL: text = "libopenmpt error"; break; } return openmpt::strdup( text ); } int openmpt_error_func_default( int error, void * /* user */ ) { (void)error; return OPENMPT_ERROR_FUNC_RESULT_DEFAULT; } int openmpt_error_func_log( int error, void * /* user */ ) { (void)error; return OPENMPT_ERROR_FUNC_RESULT_LOG; } int openmpt_error_func_store( int error, void * /* user */ ) { (void)error; return OPENMPT_ERROR_FUNC_RESULT_STORE; } int openmpt_error_func_ignore( int error, void * /* user */ ) { (void)error; return OPENMPT_ERROR_FUNC_RESULT_NONE; } int openmpt_error_func_errno( int error, void * user ) { int * e = (int *)user; if ( !e ) { return OPENMPT_ERROR_FUNC_RESULT_DEFAULT; } *e = error; return OPENMPT_ERROR_FUNC_RESULT_NONE; } void * openmpt_error_func_errno_userdata( int * error ) { return (void *)error; } double openmpt_could_open_probability( openmpt_stream_callbacks stream_callbacks, void * stream, double effort, openmpt_log_func logfunc, void * loguser ) { return openmpt_could_open_probability2( stream_callbacks, stream, effort, logfunc, loguser, NULL, NULL, NULL, NULL ); } double openmpt_could_open_propability( openmpt_stream_callbacks stream_callbacks, void * stream, double effort, openmpt_log_func logfunc, void * loguser ) { return openmpt_could_open_probability2( stream_callbacks, stream, effort, logfunc, loguser, NULL, NULL, NULL, NULL ); } double openmpt_could_open_probability2( openmpt_stream_callbacks stream_callbacks, void * stream, double effort, openmpt_log_func logfunc, void * loguser, openmpt_error_func errfunc, void * erruser, int * error, const char * * error_message ) { try { openmpt::callback_stream_wrapper istream = { stream, stream_callbacks.read, stream_callbacks.seek, stream_callbacks.tell }; return openmpt::module_impl::could_open_probability( istream, effort, openmpt::helper::make_unique( logfunc ? logfunc : openmpt_log_func_default, loguser ) ); } catch ( ... ) { openmpt::report_exception( __func__, logfunc, loguser, errfunc, erruser, error, error_message ); } return 0.0; } size_t openmpt_probe_file_header_get_recommended_size(void) { try { return openmpt::module_impl::probe_file_header_get_recommended_size(); } catch ( ... ) { openmpt::report_exception( __func__ ); } return 0; } int openmpt_probe_file_header( uint64_t flags, const void * data, size_t size, uint64_t filesize, openmpt_log_func logfunc, void * loguser, openmpt_error_func errfunc, void * erruser, int * error, const char * * error_message ) { try { return openmpt::module_impl::probe_file_header( flags, data, size, filesize ); } catch ( ... ) { openmpt::report_exception( __func__, logfunc, loguser, errfunc, erruser, error, error_message ); } return OPENMPT_PROBE_FILE_HEADER_RESULT_ERROR; } int openmpt_probe_file_header_without_filesize( uint64_t flags, const void * data, size_t size, openmpt_log_func logfunc, void * loguser, openmpt_error_func errfunc, void * erruser, int * error, const char * * error_message ) { try { return openmpt::module_impl::probe_file_header( flags, data, size ); } catch ( ... ) { openmpt::report_exception( __func__, logfunc, loguser, errfunc, erruser, error, error_message ); } return OPENMPT_PROBE_FILE_HEADER_RESULT_ERROR; } int openmpt_probe_file_header_from_stream( uint64_t flags, openmpt_stream_callbacks stream_callbacks, void * stream, openmpt_log_func logfunc, void * loguser, openmpt_error_func errfunc, void * erruser, int * error, const char * * error_message ) { try { openmpt::callback_stream_wrapper istream = { stream, stream_callbacks.read, stream_callbacks.seek, stream_callbacks.tell }; return openmpt::module_impl::probe_file_header( flags, istream ); } catch ( ... ) { openmpt::report_exception( __func__, logfunc, loguser, errfunc, erruser, error, error_message ); } return OPENMPT_PROBE_FILE_HEADER_RESULT_ERROR; } openmpt_module * openmpt_module_create( openmpt_stream_callbacks stream_callbacks, void * stream, openmpt_log_func logfunc, void * user, const openmpt_module_initial_ctl * ctls ) { return openmpt_module_create2( stream_callbacks, stream, logfunc, user, NULL, NULL, NULL, NULL, ctls ); } openmpt_module * openmpt_module_create2( openmpt_stream_callbacks stream_callbacks, void * stream, openmpt_log_func logfunc, void * loguser, openmpt_error_func errfunc, void * erruser, int * error, const char * * error_message, const openmpt_module_initial_ctl * ctls ) { try { openmpt_module * mod = (openmpt_module*)std::calloc( 1, sizeof( openmpt_module ) ); if ( !mod ) { throw std::bad_alloc(); } std::memset( mod, 0, sizeof( openmpt_module ) ); mod->logfunc = logfunc ? logfunc : openmpt_log_func_default; mod->loguser = loguser; mod->errfunc = errfunc ? errfunc : NULL; mod->erruser = erruser; mod->error = OPENMPT_ERROR_OK; mod->error_message = NULL; mod->impl = 0; try { std::map< std::string, std::string > ctls_map; if ( ctls ) { for ( const openmpt_module_initial_ctl * it = ctls; it->ctl; ++it ) { if ( it->value ) { ctls_map[ it->ctl ] = it->value; } else { ctls_map.erase( it->ctl ); } } } openmpt::callback_stream_wrapper istream = { stream, stream_callbacks.read, stream_callbacks.seek, stream_callbacks.tell }; mod->impl = new openmpt::module_impl( istream, openmpt::helper::make_unique( mod->logfunc, mod->loguser ), ctls_map ); return mod; } catch ( ... ) { #if defined(_MSC_VER) #pragma warning(push) #pragma warning(disable:6001) // false-positive: Using uninitialized memory 'mod'. #endif // _MSC_VER openmpt::report_exception( __func__, mod, error, error_message ); #if defined(_MSC_VER) #pragma warning(pop) #endif // _MSC_VER } delete mod->impl; mod->impl = 0; if ( mod->error_message ) { openmpt_free_string( mod->error_message ); mod->error_message = NULL; } std::free( (void*)mod ); mod = NULL; } catch ( ... ) { openmpt::report_exception( __func__, 0, error, error_message ); } return NULL; } openmpt_module * openmpt_module_create_from_memory( const void * filedata, size_t filesize, openmpt_log_func logfunc, void * user, const openmpt_module_initial_ctl * ctls ) { return openmpt_module_create_from_memory2( filedata, filesize, logfunc, user, NULL, NULL, NULL, NULL, ctls ); } openmpt_module * openmpt_module_create_from_memory2( const void * filedata, size_t filesize, openmpt_log_func logfunc, void * loguser, openmpt_error_func errfunc, void * erruser, int * error, const char * * error_message, const openmpt_module_initial_ctl * ctls ) { try { openmpt_module * mod = (openmpt_module*)std::calloc( 1, sizeof( openmpt_module ) ); if ( !mod ) { throw std::bad_alloc(); } std::memset( mod, 0, sizeof( openmpt_module ) ); mod->logfunc = logfunc ? logfunc : openmpt_log_func_default; mod->loguser = loguser; mod->errfunc = errfunc ? errfunc : NULL; mod->erruser = erruser; mod->error = OPENMPT_ERROR_OK; mod->error_message = NULL; mod->impl = 0; try { std::map< std::string, std::string > ctls_map; if ( ctls ) { for ( const openmpt_module_initial_ctl * it = ctls; it->ctl; ++it ) { if ( it->value ) { ctls_map[ it->ctl ] = it->value; } else { ctls_map.erase( it->ctl ); } } } mod->impl = new openmpt::module_impl( filedata, filesize, openmpt::helper::make_unique( mod->logfunc, mod->loguser ), ctls_map ); return mod; } catch ( ... ) { #if defined(_MSC_VER) #pragma warning(push) #pragma warning(disable:6001) // false-positive: Using uninitialized memory 'mod'. #endif // _MSC_VER openmpt::report_exception( __func__, mod, error, error_message ); #if defined(_MSC_VER) #pragma warning(pop) #endif // _MSC_VER } delete mod->impl; mod->impl = 0; if ( mod->error_message ) { openmpt_free_string( mod->error_message ); mod->error_message = NULL; } std::free( (void*)mod ); mod = NULL; } catch ( ... ) { openmpt::report_exception( __func__, 0, error, error_message ); } return NULL; } void openmpt_module_destroy( openmpt_module * mod ) { try { openmpt::interface::check_soundfile( mod ); delete mod->impl; mod->impl = 0; if ( mod->error_message ) { openmpt_free_string( mod->error_message ); mod->error_message = NULL; } std::free( (void*)mod ); mod = NULL; return; } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return; } void openmpt_module_set_log_func( openmpt_module * mod, openmpt_log_func logfunc, void * loguser ) { try { openmpt::interface::check_soundfile( mod ); mod->logfunc = logfunc ? logfunc : openmpt_log_func_default; mod->loguser = loguser; return; } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return; } void openmpt_module_set_error_func( openmpt_module * mod, openmpt_error_func errfunc, void * erruser ) { try { openmpt::interface::check_soundfile( mod ); mod->errfunc = errfunc ? errfunc : NULL; mod->erruser = erruser; mod->error = OPENMPT_ERROR_OK; return; } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return; } int openmpt_module_error_get_last( openmpt_module * mod ) { try { openmpt::interface::check_soundfile( mod ); return mod->error; } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return -1; } const char * openmpt_module_error_get_last_message( openmpt_module * mod ) { try { openmpt::interface::check_soundfile( mod ); return mod->error_message ? openmpt::strdup( mod->error_message ) : openmpt::strdup( "" ); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return NULL; } void openmpt_module_error_set_last( openmpt_module * mod, int error ) { try { openmpt::interface::check_soundfile( mod ); mod->error = error; if ( mod->error_message ) { openmpt_free_string( mod->error_message ); mod->error_message = NULL; } return; } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return; } void openmpt_module_error_clear( openmpt_module * mod ) { try { openmpt::interface::check_soundfile( mod ); mod->error = OPENMPT_ERROR_OK; if ( mod->error_message ) { openmpt_free_string( mod->error_message ); mod->error_message = NULL; } return; } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return; } int openmpt_module_select_subsong( openmpt_module * mod, int32_t subsong ) { try { openmpt::interface::check_soundfile( mod ); mod->impl->select_subsong( subsong ); return 1; } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return 0; } int32_t openmpt_module_get_selected_subsong( openmpt_module * mod ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_selected_subsong(); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return -1; } int openmpt_module_set_repeat_count( openmpt_module * mod, int32_t repeat_count ) { try { openmpt::interface::check_soundfile( mod ); mod->impl->set_repeat_count( repeat_count ); return 1; } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return 0; } int32_t openmpt_module_get_repeat_count( openmpt_module * mod ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_repeat_count(); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return 0; } double openmpt_module_get_duration_seconds( openmpt_module * mod ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_duration_seconds(); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return 0.0; } double openmpt_module_set_position_seconds( openmpt_module * mod, double seconds ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->set_position_seconds( seconds ); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return 0.0; } double openmpt_module_get_position_seconds( openmpt_module * mod ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_position_seconds(); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return 0.0; } double openmpt_module_set_position_order_row( openmpt_module * mod, int32_t order, int32_t row ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->set_position_order_row( order, row ); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return 0.0; } int openmpt_module_get_render_param( openmpt_module * mod, int param, int32_t * value ) { try { openmpt::interface::check_soundfile( mod ); openmpt::interface::check_pointer( value ); *value = mod->impl->get_render_param( (openmpt::module::render_param)param ); return 1; } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return 0; } int openmpt_module_set_render_param( openmpt_module * mod, int param, int32_t value ) { try { openmpt::interface::check_soundfile( mod ); mod->impl->set_render_param( (openmpt::module::render_param)param, value ); return 1; } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return 0; } size_t openmpt_module_read_mono( openmpt_module * mod, int32_t samplerate, size_t count, int16_t * mono ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->read( samplerate, count, mono ); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return 0; } size_t openmpt_module_read_stereo( openmpt_module * mod, int32_t samplerate, size_t count, int16_t * left, int16_t * right ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->read( samplerate, count, left, right ); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return 0; } size_t openmpt_module_read_quad( openmpt_module * mod, int32_t samplerate, size_t count, int16_t * left, int16_t * right, int16_t * rear_left, int16_t * rear_right ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->read( samplerate, count, left, right, rear_left, rear_right ); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return 0; } size_t openmpt_module_read_float_mono( openmpt_module * mod, int32_t samplerate, size_t count, float * mono ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->read( samplerate, count, mono ); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return 0; } size_t openmpt_module_read_float_stereo( openmpt_module * mod, int32_t samplerate, size_t count, float * left, float * right ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->read( samplerate, count, left, right ); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return 0; } size_t openmpt_module_read_float_quad( openmpt_module * mod, int32_t samplerate, size_t count, float * left, float * right, float * rear_left, float * rear_right ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->read( samplerate, count, left, right, rear_left, rear_right ); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return 0; } size_t openmpt_module_read_interleaved_stereo( openmpt_module * mod, int32_t samplerate, size_t count, int16_t * interleaved_stereo ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->read_interleaved_stereo( samplerate, count, interleaved_stereo ); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return 0; } size_t openmpt_module_read_interleaved_quad( openmpt_module * mod, int32_t samplerate, size_t count, int16_t * interleaved_quad ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->read_interleaved_quad( samplerate, count, interleaved_quad ); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return 0; } size_t openmpt_module_read_interleaved_float_stereo( openmpt_module * mod, int32_t samplerate, size_t count, float * interleaved_stereo ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->read_interleaved_stereo( samplerate, count, interleaved_stereo ); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return 0; } size_t openmpt_module_read_interleaved_float_quad( openmpt_module * mod, int32_t samplerate, size_t count, float * interleaved_quad ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->read_interleaved_quad( samplerate, count, interleaved_quad ); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return 0; } const char * openmpt_module_get_metadata_keys( openmpt_module * mod ) { try { openmpt::interface::check_soundfile( mod ); std::string retval; bool first = true; std::vector metadata_keys = mod->impl->get_metadata_keys(); for ( std::vector::iterator i = metadata_keys.begin(); i != metadata_keys.end(); ++i ) { if ( first ) { first = false; } else { retval += ";"; } retval += *i; } return openmpt::strdup( retval.c_str() ); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return NULL; } const char * openmpt_module_get_metadata( openmpt_module * mod, const char * key ) { try { openmpt::interface::check_soundfile( mod ); openmpt::interface::check_pointer( key ); return openmpt::strdup( mod->impl->get_metadata( key ).c_str() ); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return NULL; } double openmpt_module_get_current_estimated_bpm( openmpt_module * mod ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_current_estimated_bpm(); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return 0.0; } int32_t openmpt_module_get_current_speed( openmpt_module * mod ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_current_speed(); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return 0; } int32_t openmpt_module_get_current_tempo( openmpt_module * mod ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_current_tempo(); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return 0; } int32_t openmpt_module_get_current_order( openmpt_module * mod ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_current_order(); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return 0; } int32_t openmpt_module_get_current_pattern( openmpt_module * mod ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_current_pattern(); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return 0; } int32_t openmpt_module_get_current_row( openmpt_module * mod ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_current_row(); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return 0; } int32_t openmpt_module_get_current_playing_channels( openmpt_module * mod ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_current_playing_channels(); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return 0; } float openmpt_module_get_current_channel_vu_mono( openmpt_module * mod, int32_t channel ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_current_channel_vu_mono( channel ); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return 0.0; } float openmpt_module_get_current_channel_vu_left( openmpt_module * mod, int32_t channel ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_current_channel_vu_left( channel ); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return 0.0; } float openmpt_module_get_current_channel_vu_right( openmpt_module * mod, int32_t channel ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_current_channel_vu_right( channel ); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return 0.0; } float openmpt_module_get_current_channel_vu_rear_left( openmpt_module * mod, int32_t channel ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_current_channel_vu_rear_left( channel ); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return 0.0; } float openmpt_module_get_current_channel_vu_rear_right( openmpt_module * mod, int32_t channel ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_current_channel_vu_rear_right( channel ); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return 0.0; } int32_t openmpt_module_get_num_subsongs( openmpt_module * mod ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_num_subsongs(); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return 0; } int32_t openmpt_module_get_num_channels( openmpt_module * mod ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_num_channels(); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return 0; } int32_t openmpt_module_get_num_orders( openmpt_module * mod ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_num_orders(); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return 0; } int32_t openmpt_module_get_num_patterns( openmpt_module * mod ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_num_patterns(); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return 0; } int32_t openmpt_module_get_num_instruments( openmpt_module * mod ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_num_instruments(); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return 0; } int32_t openmpt_module_get_num_samples( openmpt_module * mod ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_num_samples(); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return 0; } const char * openmpt_module_get_subsong_name( openmpt_module * mod, int32_t index ) { try { openmpt::interface::check_soundfile( mod ); std::vector names = mod->impl->get_subsong_names(); if ( names.size() >= (std::size_t)std::numeric_limits::max() ) { throw std::runtime_error("too many names"); } if ( index < 0 || index >= (int32_t)names.size() ) { return openmpt::strdup( "" ); } return openmpt::strdup( names[index].c_str() ); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return NULL; } const char * openmpt_module_get_channel_name( openmpt_module * mod, int32_t index ) { try { openmpt::interface::check_soundfile( mod ); std::vector names = mod->impl->get_channel_names(); if ( names.size() >= (std::size_t)std::numeric_limits::max() ) { throw std::runtime_error("too many names"); } if ( index < 0 || index >= (int32_t)names.size() ) { return openmpt::strdup( "" ); } return openmpt::strdup( names[index].c_str() ); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return NULL; } const char * openmpt_module_get_order_name( openmpt_module * mod, int32_t index ) { try { openmpt::interface::check_soundfile( mod ); std::vector names = mod->impl->get_order_names(); if ( names.size() >= (std::size_t)std::numeric_limits::max() ) { throw std::runtime_error("too many names"); } if ( index < 0 || index >= (int32_t)names.size() ) { return openmpt::strdup( "" ); } return openmpt::strdup( names[index].c_str() ); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return NULL; } const char * openmpt_module_get_pattern_name( openmpt_module * mod, int32_t index ) { try { openmpt::interface::check_soundfile( mod ); std::vector names = mod->impl->get_pattern_names(); if ( names.size() >= (std::size_t)std::numeric_limits::max() ) { throw std::runtime_error("too many names"); } if ( index < 0 || index >= (int32_t)names.size() ) { return openmpt::strdup( "" ); } return openmpt::strdup( names[index].c_str() ); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return NULL; } const char * openmpt_module_get_instrument_name( openmpt_module * mod, int32_t index ) { try { openmpt::interface::check_soundfile( mod ); std::vector names = mod->impl->get_instrument_names(); if ( names.size() >= (std::size_t)std::numeric_limits::max() ) { throw std::runtime_error("too many names"); } if ( index < 0 || index >= (int32_t)names.size() ) { return openmpt::strdup( "" ); } return openmpt::strdup( names[index].c_str() ); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return NULL; } const char * openmpt_module_get_sample_name( openmpt_module * mod, int32_t index ) { try { openmpt::interface::check_soundfile( mod ); std::vector names = mod->impl->get_sample_names(); if ( names.size() >= (std::size_t)std::numeric_limits::max() ) { throw std::runtime_error("too many names"); } if ( index < 0 || index >= (int32_t)names.size() ) { return openmpt::strdup( "" ); } return openmpt::strdup( names[index].c_str() ); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return NULL; } int32_t openmpt_module_get_order_pattern( openmpt_module * mod, int32_t order ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_order_pattern( order ); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return 0; } int32_t openmpt_module_get_pattern_num_rows( openmpt_module * mod, int32_t pattern ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_pattern_num_rows( pattern ); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return 0; } uint8_t openmpt_module_get_pattern_row_channel_command( openmpt_module * mod, int32_t pattern, int32_t row, int32_t channel, int command ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_pattern_row_channel_command( pattern, row, channel, command ); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return 0; } const char * openmpt_module_format_pattern_row_channel_command( openmpt_module * mod, int32_t pattern, int32_t row, int32_t channel, int command ) { try { openmpt::interface::check_soundfile( mod ); return openmpt::strdup( mod->impl->format_pattern_row_channel_command( pattern, row, channel, command ).c_str() ); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return 0; } const char * openmpt_module_highlight_pattern_row_channel_command( openmpt_module * mod, int32_t pattern, int32_t row, int32_t channel, int command ) { try { openmpt::interface::check_soundfile( mod ); return openmpt::strdup( mod->impl->highlight_pattern_row_channel_command( pattern, row, channel, command ).c_str() ); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return 0; } const char * openmpt_module_format_pattern_row_channel( openmpt_module * mod, int32_t pattern, int32_t row, int32_t channel, size_t width, int pad ) { try { openmpt::interface::check_soundfile( mod ); return openmpt::strdup( mod->impl->format_pattern_row_channel( pattern, row, channel, width, pad ? true : false ).c_str() ); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return 0; } const char * openmpt_module_highlight_pattern_row_channel( openmpt_module * mod, int32_t pattern, int32_t row, int32_t channel, size_t width, int pad ) { try { openmpt::interface::check_soundfile( mod ); return openmpt::strdup( mod->impl->highlight_pattern_row_channel( pattern, row, channel, width, pad ? true : false ).c_str() ); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return 0; } const char * openmpt_module_get_ctls( openmpt_module * mod ) { try { openmpt::interface::check_soundfile( mod ); std::string retval; bool first = true; std::vector ctls = mod->impl->get_ctls(); for ( std::vector::iterator i = ctls.begin(); i != ctls.end(); ++i ) { if ( first ) { first = false; } else { retval += ";"; } retval += *i; } return openmpt::strdup( retval.c_str() ); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return NULL; } const char * openmpt_module_ctl_get( openmpt_module * mod, const char * ctl ) { try { openmpt::interface::check_soundfile( mod ); openmpt::interface::check_pointer( ctl ); return openmpt::strdup( mod->impl->ctl_get( ctl ).c_str() ); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return NULL; } int openmpt_module_ctl_get_boolean( openmpt_module * mod, const char * ctl ) { try { openmpt::interface::check_soundfile( mod ); openmpt::interface::check_pointer( ctl ); return mod->impl->ctl_get_boolean( ctl ); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return 0; } int64_t openmpt_module_ctl_get_integer( openmpt_module * mod, const char * ctl ) { try { openmpt::interface::check_soundfile( mod ); openmpt::interface::check_pointer( ctl ); return mod->impl->ctl_get_integer( ctl ); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return 0; } double openmpt_module_ctl_get_floatingpoint( openmpt_module * mod, const char * ctl ) { try { openmpt::interface::check_soundfile( mod ); openmpt::interface::check_pointer( ctl ); return mod->impl->ctl_get_floatingpoint( ctl ); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return 0.0; } const char * openmpt_module_ctl_get_text( openmpt_module * mod, const char * ctl ) { try { openmpt::interface::check_soundfile( mod ); openmpt::interface::check_pointer( ctl ); return openmpt::strdup( mod->impl->ctl_get_text( ctl ).c_str() ); } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return NULL; } int openmpt_module_ctl_set( openmpt_module * mod, const char * ctl, const char * value ) { try { openmpt::interface::check_soundfile( mod ); openmpt::interface::check_pointer( ctl ); openmpt::interface::check_pointer( value ); mod->impl->ctl_set( ctl, value ); return 1; } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return 0; } int openmpt_module_ctl_set_boolean( openmpt_module * mod, const char * ctl, int value ) { try { openmpt::interface::check_soundfile( mod ); openmpt::interface::check_pointer( ctl ); mod->impl->ctl_set_boolean( ctl, value ? true : false ); return 1; } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return 0; } int openmpt_module_ctl_set_integer( openmpt_module * mod, const char * ctl, int64_t value ) { try { openmpt::interface::check_soundfile( mod ); openmpt::interface::check_pointer( ctl ); mod->impl->ctl_set_integer( ctl, value ); return 1; } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return 0; } int openmpt_module_ctl_set_floatingpoint( openmpt_module * mod, const char * ctl, double value ) { try { openmpt::interface::check_soundfile( mod ); openmpt::interface::check_pointer( ctl ); mod->impl->ctl_set_floatingpoint( ctl, value ); return 1; } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return 0; } int openmpt_module_ctl_set_text( openmpt_module * mod, const char * ctl, const char * value ) { try { openmpt::interface::check_soundfile( mod ); openmpt::interface::check_pointer( ctl ); openmpt::interface::check_pointer( value ); mod->impl->ctl_set_text( ctl, value ); return 1; } catch ( ... ) { openmpt::report_exception( __func__, mod ); } return 0; } openmpt_module_ext * openmpt_module_ext_create( openmpt_stream_callbacks stream_callbacks, void * stream, openmpt_log_func logfunc, void * loguser, openmpt_error_func errfunc, void * erruser, int * error, const char * * error_message, const openmpt_module_initial_ctl * ctls ) { try { openmpt_module_ext * mod_ext = (openmpt_module_ext*)std::calloc( 1, sizeof( openmpt_module_ext ) ); if ( !mod_ext ) { throw std::bad_alloc(); } std::memset( mod_ext, 0, sizeof( openmpt_module_ext ) ); openmpt_module * mod = &mod_ext->mod; std::memset( mod, 0, sizeof( openmpt_module ) ); mod_ext->impl = 0; mod->logfunc = logfunc ? logfunc : openmpt_log_func_default; mod->loguser = loguser; mod->errfunc = errfunc ? errfunc : NULL; mod->erruser = erruser; mod->error = OPENMPT_ERROR_OK; mod->error_message = NULL; mod->impl = 0; try { std::map< std::string, std::string > ctls_map; if ( ctls ) { for ( const openmpt_module_initial_ctl * it = ctls; it->ctl; ++it ) { if ( it->value ) { ctls_map[ it->ctl ] = it->value; } else { ctls_map.erase( it->ctl ); } } } openmpt::callback_stream_wrapper istream = { stream, stream_callbacks.read, stream_callbacks.seek, stream_callbacks.tell }; mod_ext->impl = new openmpt::module_ext_impl( istream, openmpt::helper::make_unique( mod->logfunc, mod->loguser ), ctls_map ); mod->impl = mod_ext->impl; return mod_ext; } catch ( ... ) { openmpt::report_exception( __func__, mod, error, error_message ); } #if defined(_MSC_VER) #pragma warning(push) #pragma warning(disable:6001) // false-positive: Using uninitialized memory 'mod_ext'. #endif // _MSC_VER delete mod_ext->impl; #if defined(_MSC_VER) #pragma warning(pop) #endif // _MSC_VER mod_ext->impl = 0; mod->impl = 0; if ( mod->error_message ) { openmpt_free_string( mod->error_message ); mod->error_message = NULL; } std::free( (void*)mod_ext ); mod_ext = NULL; } catch ( ... ) { openmpt::report_exception( __func__, 0, error, error_message ); } return NULL; } openmpt_module_ext * openmpt_module_ext_create_from_memory( const void * filedata, size_t filesize, openmpt_log_func logfunc, void * loguser, openmpt_error_func errfunc, void * erruser, int * error, const char * * error_message, const openmpt_module_initial_ctl * ctls ) { try { openmpt_module_ext * mod_ext = (openmpt_module_ext*)std::calloc( 1, sizeof( openmpt_module_ext ) ); if ( !mod_ext ) { throw std::bad_alloc(); } std::memset( mod_ext, 0, sizeof( openmpt_module_ext ) ); openmpt_module * mod = &mod_ext->mod; std::memset( mod, 0, sizeof( openmpt_module ) ); mod_ext->impl = 0; mod->logfunc = logfunc ? logfunc : openmpt_log_func_default; mod->loguser = loguser; mod->errfunc = errfunc ? errfunc : NULL; mod->erruser = erruser; mod->error = OPENMPT_ERROR_OK; mod->error_message = NULL; mod->impl = 0; try { std::map< std::string, std::string > ctls_map; if ( ctls ) { for ( const openmpt_module_initial_ctl * it = ctls; it->ctl; ++it ) { if ( it->value ) { ctls_map[ it->ctl ] = it->value; } else { ctls_map.erase( it->ctl ); } } } mod_ext->impl = new openmpt::module_ext_impl( filedata, filesize, openmpt::helper::make_unique( mod->logfunc, mod->loguser ), ctls_map ); mod->impl = mod_ext->impl; return mod_ext; } catch ( ... ) { openmpt::report_exception( __func__, mod, error, error_message ); } #if defined(_MSC_VER) #pragma warning(push) #pragma warning(disable:6001) // false-positive: Using uninitialized memory 'mod_ext'. #endif // _MSC_VER delete mod_ext->impl; #if defined(_MSC_VER) #pragma warning(pop) #endif // _MSC_VER mod_ext->impl = 0; mod->impl = 0; if ( mod->error_message ) { openmpt_free_string( mod->error_message ); mod->error_message = NULL; } std::free( (void*)mod_ext ); mod_ext = NULL; } catch ( ... ) { openmpt::report_exception( __func__, 0, error, error_message ); } return NULL; } void openmpt_module_ext_destroy( openmpt_module_ext * mod_ext ) { try { openmpt::interface::check_soundfile( mod_ext ); openmpt_module * mod = &mod_ext->mod; mod->impl = 0; delete mod_ext->impl; mod_ext->impl = 0; if ( mod->error_message ) { openmpt_free_string( mod->error_message ); mod->error_message = NULL; } std::free( (void*)mod_ext ); mod_ext = NULL; return; } catch ( ... ) { openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return; } openmpt_module * openmpt_module_ext_get_module( openmpt_module_ext * mod_ext ) { try { openmpt::interface::check_soundfile( mod_ext ); openmpt_module * mod = &mod_ext->mod; return mod; } catch ( ... ) { openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return NULL; } static int get_pattern_row_channel_volume_effect_type( openmpt_module_ext * mod_ext, int32_t pattern, int32_t row, int32_t channel ) { try { openmpt::interface::check_soundfile( mod_ext ); return mod_ext->impl->get_pattern_row_channel_volume_effect_type( pattern, row, channel ); } catch ( ... ) { openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return -1; } static int get_pattern_row_channel_effect_type( openmpt_module_ext * mod_ext, int32_t pattern, int32_t row, int32_t channel ) { try { openmpt::interface::check_soundfile( mod_ext ); return mod_ext->impl->get_pattern_row_channel_effect_type( pattern, row, channel ); } catch ( ... ) { openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return -1; } static int set_current_speed( openmpt_module_ext * mod_ext, int32_t speed ) { try { openmpt::interface::check_soundfile( mod_ext ); mod_ext->impl->set_current_speed( speed ); return 1; } catch ( ... ) { openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return 0; } static int set_current_tempo( openmpt_module_ext * mod_ext, int32_t tempo ) { try { openmpt::interface::check_soundfile( mod_ext ); mod_ext->impl->set_current_tempo( tempo ); return 1; } catch ( ... ) { openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return 0; } static int set_tempo_factor( openmpt_module_ext * mod_ext, double factor ) { try { openmpt::interface::check_soundfile( mod_ext ); mod_ext->impl->set_tempo_factor( factor ); return 1; } catch ( ... ) { openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return 0; } static double get_tempo_factor( openmpt_module_ext * mod_ext ) { try { openmpt::interface::check_soundfile( mod_ext ); return mod_ext->impl->get_tempo_factor(); } catch ( ... ) { openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return 0.0; } static int set_pitch_factor( openmpt_module_ext * mod_ext, double factor ) { try { openmpt::interface::check_soundfile( mod_ext ); mod_ext->impl->set_pitch_factor( factor ); return 1; } catch ( ... ) { openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return 0; } static double get_pitch_factor( openmpt_module_ext * mod_ext ) { try { openmpt::interface::check_soundfile( mod_ext ); return mod_ext->impl->get_pitch_factor(); } catch ( ... ) { openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return 0.0; } static int set_global_volume( openmpt_module_ext * mod_ext, double volume ) { try { openmpt::interface::check_soundfile( mod_ext ); mod_ext->impl->set_global_volume( volume ); return 1; } catch ( ... ) { openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return 0; } static double get_global_volume( openmpt_module_ext * mod_ext ) { try { openmpt::interface::check_soundfile( mod_ext ); return mod_ext->impl->get_global_volume(); } catch ( ... ) { openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return 0.0; } static int set_channel_volume( openmpt_module_ext * mod_ext, int32_t channel, double volume ) { try { openmpt::interface::check_soundfile( mod_ext ); mod_ext->impl->set_channel_volume( channel, volume ); return 1; } catch ( ... ) { openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return 0; } static double get_channel_volume( openmpt_module_ext * mod_ext, int32_t channel ) { try { openmpt::interface::check_soundfile( mod_ext ); return mod_ext->impl->get_channel_volume( channel ); } catch ( ... ) { openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return 0.0; } static int set_channel_mute_status( openmpt_module_ext * mod_ext, int32_t channel, int mute ) { try { openmpt::interface::check_soundfile( mod_ext ); mod_ext->impl->set_channel_mute_status( channel, mute ? true : false ); return 1; } catch ( ... ) { openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return 0; } static int get_channel_mute_status( openmpt_module_ext * mod_ext, int32_t channel ) { try { openmpt::interface::check_soundfile( mod_ext ); return mod_ext->impl->get_channel_mute_status( channel ) ? 1 : 0; } catch ( ... ) { openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return -1; } static int set_instrument_mute_status( openmpt_module_ext * mod_ext, int32_t instrument, int mute ) { try { openmpt::interface::check_soundfile( mod_ext ); mod_ext->impl->set_instrument_mute_status( instrument, mute ? true : false ); return 1; } catch ( ... ) { openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return 0; } static int get_instrument_mute_status( openmpt_module_ext * mod_ext, int32_t instrument ) { try { openmpt::interface::check_soundfile( mod_ext ); return mod_ext->impl->get_instrument_mute_status( instrument ) ? 1 : 0; } catch ( ... ) { openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return -1; } static int32_t play_note( openmpt_module_ext * mod_ext, int32_t instrument, int32_t note, double volume, double panning ) { try { openmpt::interface::check_soundfile( mod_ext ); return mod_ext->impl->play_note( instrument, note, volume, panning ); } catch ( ... ) { openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return -1; } static int stop_note( openmpt_module_ext * mod_ext, int32_t channel ) { try { openmpt::interface::check_soundfile( mod_ext ); mod_ext->impl->stop_note( channel ); return 1; } catch ( ... ) { openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return 0; } static int note_off( openmpt_module_ext * mod_ext, int32_t channel ) { try { openmpt::interface::check_soundfile( mod_ext ); mod_ext->impl->note_off(channel ); return 1; } catch ( ... ) { openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return 0; } static int note_fade( openmpt_module_ext * mod_ext, int32_t channel ) { try { openmpt::interface::check_soundfile( mod_ext ); mod_ext->impl->note_fade(channel ); return 1; } catch ( ... ) { openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return 0; } static int set_channel_panning( openmpt_module_ext * mod_ext, int32_t channel, double panning ) { try { openmpt::interface::check_soundfile( mod_ext ); mod_ext->impl->set_channel_panning( channel, panning ); return 1; } catch ( ... ) { openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return 0; } static double get_channel_panning( openmpt_module_ext * mod_ext, int32_t channel ) { try { openmpt::interface::check_soundfile( mod_ext ); return mod_ext->impl->get_channel_panning( channel ); } catch ( ... ) { openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return 0.0; } static int set_note_finetune( openmpt_module_ext * mod_ext, int32_t channel, double finetune ) { try { openmpt::interface::check_soundfile( mod_ext ); mod_ext->impl->set_note_finetune( channel, finetune ); return 1; } catch ( ... ) { openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return 0; } static double get_note_finetune( openmpt_module_ext * mod_ext, int32_t channel ) { try { openmpt::interface::check_soundfile( mod_ext ); return mod_ext->impl->get_note_finetune( channel ); } catch ( ... ) { openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return 0.0; } /* add stuff here */ int openmpt_module_ext_get_interface( openmpt_module_ext * mod_ext, const char * interface_id, void * interface, size_t interface_size ) { try { openmpt::interface::check_soundfile( mod_ext ); openmpt::interface::check_pointer( interface_id ); openmpt::interface::check_pointer( interface ); std::memset( interface, 0, interface_size ); int result = 0; if ( !std::strcmp( interface_id, "" ) ) { result = 0; } else if ( !std::strcmp( interface_id, LIBOPENMPT_EXT_C_INTERFACE_PATTERN_VIS ) && ( interface_size == sizeof( openmpt_module_ext_interface_pattern_vis ) ) ) { openmpt_module_ext_interface_pattern_vis * i = static_cast< openmpt_module_ext_interface_pattern_vis * >( interface ); i->get_pattern_row_channel_volume_effect_type = &get_pattern_row_channel_volume_effect_type; i->get_pattern_row_channel_effect_type = &get_pattern_row_channel_effect_type; result = 1; } else if ( !std::strcmp( interface_id, LIBOPENMPT_EXT_C_INTERFACE_INTERACTIVE ) && ( interface_size == sizeof( openmpt_module_ext_interface_interactive ) ) ) { openmpt_module_ext_interface_interactive * i = static_cast< openmpt_module_ext_interface_interactive * >( interface ); i->set_current_speed = &set_current_speed; i->set_current_tempo = &set_current_tempo; i->set_tempo_factor = &set_tempo_factor; i->get_tempo_factor = &get_tempo_factor; i->set_pitch_factor = &set_pitch_factor; i->get_pitch_factor = &get_pitch_factor; i->set_global_volume = &set_global_volume; i->get_global_volume = &get_global_volume; i->set_channel_volume = &set_channel_volume; i->get_channel_volume = &get_channel_volume; i->set_channel_mute_status = &set_channel_mute_status; i->get_channel_mute_status = &get_channel_mute_status; i->set_instrument_mute_status = &set_instrument_mute_status; i->get_instrument_mute_status = &get_instrument_mute_status; i->play_note = &play_note; i->stop_note = &stop_note; result = 1; } else if ( !std::strcmp( interface_id, LIBOPENMPT_EXT_C_INTERFACE_INTERACTIVE2 ) && ( interface_size == sizeof( openmpt_module_ext_interface_interactive2 ) ) ) { openmpt_module_ext_interface_interactive2 * i = static_cast< openmpt_module_ext_interface_interactive2 * >( interface ); i->note_off = ¬e_off; i->note_fade = ¬e_fade; i->set_channel_panning = &set_channel_panning; i->get_channel_panning = &get_channel_panning; i->set_note_finetune = &set_note_finetune; i->get_note_finetune = &get_note_finetune; result = 1; /* add stuff here */ } else { result = 0; } return result; } catch ( ... ) { openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return 0; } } // extern "C" libopenmpt-0.6.1+release.autotools/libopenmpt/libopenmpt_cxx.cpp0000644000175000017500000004376614057134205022205 00000000000000/* * libopenmpt_cxx.cpp * ------------------ * Purpose: libopenmpt C++ interface implementation * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "openmpt/all/BuildSettings.hpp" #include "libopenmpt_internal.h" #include "libopenmpt.hpp" #include "libopenmpt_ext.hpp" #include "libopenmpt_impl.hpp" #include "libopenmpt_ext_impl.hpp" #include #include #include #include namespace openmpt { exception::exception( const std::string & text_ ) noexcept : std::exception() , text(0) { text = static_cast( std::malloc( text_.length() + 1 ) ); if ( text ) { std::memcpy( text, text_.c_str(), text_.length() + 1 ); } } exception::exception( const exception & other ) noexcept : std::exception() , text(0) { const char * const text_ = ( other.what() ? other.what() : "" ); text = static_cast( std::malloc( std::strlen( text_ ) + 1 ) ); if ( text ) { std::memcpy( text, text_, std::strlen( text_ ) + 1 ); } } exception::exception( exception && other ) noexcept : std::exception() , text(0) { text = std::move( other.text ); other.text = 0; } exception & exception::operator = ( const exception & other ) noexcept { if ( this == &other ) { return *this; } if ( text ) { std::free( text ); text = 0; } const char * const text_ = ( other.what() ? other.what() : "" ); text = static_cast( std::malloc( std::strlen( text_ ) + 1 ) ); if ( text ) { std::memcpy( text, text_, std::strlen( text_ ) + 1 ); } return *this; } exception & exception::operator = ( exception && other ) noexcept { if ( this == &other ) { return *this; } if ( text ) { std::free( text ); text = 0; } text = std::move( other.text ); other.text = 0; return *this; } exception::~exception() noexcept { if ( text ) { std::free( text ); text = 0; } } const char * exception::what() const noexcept { if ( text ) { return text; } else { return "out of memory"; } } std::uint32_t get_library_version() { return openmpt::version::get_library_version(); } std::uint32_t get_core_version() { return openmpt::version::get_core_version(); } namespace string { std::string get( const std::string & key ) { return openmpt::version::get_string( key ); } } // namespace string } // namespace openmpt namespace openmpt { std::vector get_supported_extensions() { return openmpt::module_impl::get_supported_extensions(); } bool is_extension_supported( const std::string & extension ) { return openmpt::module_impl::is_extension_supported( extension ); } bool is_extension_supported2( std::string_view extension ) { return openmpt::module_impl::is_extension_supported( extension ); } double could_open_probability( std::istream & stream, double effort, std::ostream & log ) { return openmpt::module_impl::could_open_probability( stream, effort, openmpt::helper::make_unique( log ) ); } double could_open_propability( std::istream & stream, double effort, std::ostream & log ) { return openmpt::module_impl::could_open_probability( stream, effort, openmpt::helper::make_unique( log ) ); } std::size_t probe_file_header_get_recommended_size() { return openmpt::module_impl::probe_file_header_get_recommended_size(); } int probe_file_header( std::uint64_t flags, const std::byte * data, std::size_t size, std::uint64_t filesize ) { return openmpt::module_impl::probe_file_header( flags, data, size, filesize ); } int probe_file_header( std::uint64_t flags, const std::uint8_t * data, std::size_t size, std::uint64_t filesize ) { return openmpt::module_impl::probe_file_header( flags, data, size, filesize ); } int probe_file_header( std::uint64_t flags, const std::byte * data, std::size_t size ) { return openmpt::module_impl::probe_file_header( flags, data, size ); } int probe_file_header( std::uint64_t flags, const std::uint8_t * data, std::size_t size ) { return openmpt::module_impl::probe_file_header( flags, data, size ); } int probe_file_header( std::uint64_t flags, std::istream & stream ) { return openmpt::module_impl::probe_file_header( flags, stream ); } #if defined(_MSC_VER) #pragma warning(push) #pragma warning(disable:4702) // unreachable code #endif // _MSC_VER module::module( const module & ) : impl(nullptr) { throw exception("openmpt::module is non-copyable"); } // cppcheck-suppress operatorEqVarError void module::operator = ( const module & ) { throw exception("openmpt::module is non-copyable"); } #if defined(_MSC_VER) #pragma warning(pop) #endif // _MSC_VER module::module() : impl(0) { return; } void module::set_impl( module_impl * i ) { impl = i; } module::module( std::istream & stream, std::ostream & log, const std::map< std::string, std::string > & ctls ) : impl(0) { impl = new module_impl( stream, openmpt::helper::make_unique( log ), ctls ); } module::module( const std::vector & data, std::ostream & log, const std::map< std::string, std::string > & ctls ) : impl(0) { impl = new module_impl( data, openmpt::helper::make_unique( log ), ctls ); } module::module( const std::byte * beg, const std::byte * end, std::ostream & log, const std::map< std::string, std::string > & ctls ) : impl(0) { impl = new module_impl( beg, end - beg, openmpt::helper::make_unique( log ), ctls ); } module::module( const std::byte * data, std::size_t size, std::ostream & log, const std::map< std::string, std::string > & ctls ) : impl(0) { impl = new module_impl( data, size, openmpt::helper::make_unique( log ), ctls ); } module::module( const std::vector & data, std::ostream & log, const std::map< std::string, std::string > & ctls ) : impl(0) { impl = new module_impl( data, openmpt::helper::make_unique( log ), ctls ); } module::module( const std::uint8_t * beg, const std::uint8_t * end, std::ostream & log, const std::map< std::string, std::string > & ctls ) : impl(0) { impl = new module_impl( beg, end - beg, openmpt::helper::make_unique( log ), ctls ); } module::module( const std::uint8_t * data, std::size_t size, std::ostream & log, const std::map< std::string, std::string > & ctls ) : impl(0) { impl = new module_impl( data, size, openmpt::helper::make_unique( log ), ctls ); } module::module( const std::vector & data, std::ostream & log, const std::map< std::string, std::string > & ctls ) : impl(0) { impl = new module_impl( data, openmpt::helper::make_unique( log ), ctls ); } module::module( const char * beg, const char * end, std::ostream & log, const std::map< std::string, std::string > & ctls ) : impl(0) { impl = new module_impl( beg, end - beg, openmpt::helper::make_unique( log ), ctls ); } module::module( const char * data, std::size_t size, std::ostream & log, const std::map< std::string, std::string > & ctls ) : impl(0) { impl = new module_impl( data, size, openmpt::helper::make_unique( log ), ctls ); } module::module( const void * data, std::size_t size, std::ostream & log, const std::map< std::string, std::string > & ctls ) : impl(0) { impl = new module_impl( data, size, openmpt::helper::make_unique( log ), ctls ); } module::~module() { delete impl; impl = 0; } void module::select_subsong( std::int32_t subsong ) { impl->select_subsong( subsong ); } std::int32_t module::get_selected_subsong() const { return impl->get_selected_subsong(); } void module::set_repeat_count( std::int32_t repeat_count ) { impl->set_repeat_count( repeat_count ); } std::int32_t module::get_repeat_count() const { return impl->get_repeat_count(); } double module::get_duration_seconds() const { return impl->get_duration_seconds(); } double module::set_position_seconds( double seconds ) { return impl->set_position_seconds( seconds ); } double module::get_position_seconds() const { return impl->get_position_seconds(); } double module::set_position_order_row( std::int32_t order, std::int32_t row ) { return impl->set_position_order_row( order, row ); } std::int32_t module::get_render_param( int param ) const { return impl->get_render_param( param ); } void module::set_render_param( int param, std::int32_t value ) { impl->set_render_param( param, value ); } std::size_t module::read( std::int32_t samplerate, std::size_t count, std::int16_t * mono ) { return impl->read( samplerate, count, mono ); } std::size_t module::read( std::int32_t samplerate, std::size_t count, std::int16_t * left, std::int16_t * right ) { return impl->read( samplerate, count, left, right ); } std::size_t module::read( std::int32_t samplerate, std::size_t count, std::int16_t * left, std::int16_t * right, std::int16_t * rear_left, std::int16_t * rear_right ) { return impl->read( samplerate, count, left, right, rear_left, rear_right ); } std::size_t module::read( std::int32_t samplerate, std::size_t count, float * mono ) { return impl->read( samplerate, count, mono ); } std::size_t module::read( std::int32_t samplerate, std::size_t count, float * left, float * right ) { return impl->read( samplerate, count, left, right ); } std::size_t module::read( std::int32_t samplerate, std::size_t count, float * left, float * right, float * rear_left, float * rear_right ) { return impl->read( samplerate, count, left, right, rear_left, rear_right ); } std::size_t module::read_interleaved_stereo( std::int32_t samplerate, std::size_t count, std::int16_t * interleaved_stereo ) { return impl->read_interleaved_stereo( samplerate, count, interleaved_stereo ); } std::size_t module::read_interleaved_quad( std::int32_t samplerate, std::size_t count, std::int16_t * interleaved_quad ) { return impl->read_interleaved_quad( samplerate, count, interleaved_quad ); } std::size_t module::read_interleaved_stereo( std::int32_t samplerate, std::size_t count, float * interleaved_stereo ) { return impl->read_interleaved_stereo( samplerate, count, interleaved_stereo ); } std::size_t module::read_interleaved_quad( std::int32_t samplerate, std::size_t count, float * interleaved_quad ) { return impl->read_interleaved_quad( samplerate, count, interleaved_quad ); } std::vector module::get_metadata_keys() const { return impl->get_metadata_keys(); } std::string module::get_metadata( const std::string & key ) const { return impl->get_metadata( key ); } double module::get_current_estimated_bpm() const { return impl->get_current_estimated_bpm(); } std::int32_t module::get_current_speed() const { return impl->get_current_speed(); } std::int32_t module::get_current_tempo() const { return impl->get_current_tempo(); } std::int32_t module::get_current_order() const { return impl->get_current_order(); } std::int32_t module::get_current_pattern() const { return impl->get_current_pattern(); } std::int32_t module::get_current_row() const { return impl->get_current_row(); } std::int32_t module::get_current_playing_channels() const { return impl->get_current_playing_channels(); } float module::get_current_channel_vu_mono( std::int32_t channel ) const { return impl->get_current_channel_vu_mono( channel ); } float module::get_current_channel_vu_left( std::int32_t channel ) const { return impl->get_current_channel_vu_left( channel ); } float module::get_current_channel_vu_right( std::int32_t channel ) const { return impl->get_current_channel_vu_right( channel ); } float module::get_current_channel_vu_rear_left( std::int32_t channel ) const { return impl->get_current_channel_vu_rear_left( channel ); } float module::get_current_channel_vu_rear_right( std::int32_t channel ) const { return impl->get_current_channel_vu_rear_right( channel ); } std::int32_t module::get_num_subsongs() const { return impl->get_num_subsongs(); } std::int32_t module::get_num_channels() const { return impl->get_num_channels(); } std::int32_t module::get_num_orders() const { return impl->get_num_orders(); } std::int32_t module::get_num_patterns() const { return impl->get_num_patterns(); } std::int32_t module::get_num_instruments() const { return impl->get_num_instruments(); } std::int32_t module::get_num_samples() const { return impl->get_num_samples(); } std::vector module::get_subsong_names() const { return impl->get_subsong_names(); } std::vector module::get_channel_names() const { return impl->get_channel_names(); } std::vector module::get_order_names() const { return impl->get_order_names(); } std::vector module::get_pattern_names() const { return impl->get_pattern_names(); } std::vector module::get_instrument_names() const { return impl->get_instrument_names(); } std::vector module::get_sample_names() const { return impl->get_sample_names(); } std::int32_t module::get_order_pattern( std::int32_t order ) const { return impl->get_order_pattern( order ); } std::int32_t module::get_pattern_num_rows( std::int32_t pattern ) const { return impl->get_pattern_num_rows( pattern ); } std::uint8_t module::get_pattern_row_channel_command( std::int32_t pattern, std::int32_t row, std::int32_t channel, int command ) const { return impl->get_pattern_row_channel_command( pattern, row, channel, command ); } std::string module::format_pattern_row_channel_command( std::int32_t pattern, std::int32_t row, std::int32_t channel, int command ) const { return impl->format_pattern_row_channel_command( pattern, row, channel, command ); } std::string module::highlight_pattern_row_channel_command( std::int32_t pattern, std::int32_t row, std::int32_t channel, int command ) const { return impl->highlight_pattern_row_channel_command( pattern, row, channel, command ); } std::string module::format_pattern_row_channel( std::int32_t pattern, std::int32_t row, std::int32_t channel, std::size_t width, bool pad ) const { return impl->format_pattern_row_channel( pattern, row, channel, width, pad ); } std::string module::highlight_pattern_row_channel( std::int32_t pattern, std::int32_t row, std::int32_t channel, std::size_t width, bool pad ) const { return impl->highlight_pattern_row_channel( pattern, row, channel, width, pad ); } std::vector module::get_ctls() const { return impl->get_ctls(); } std::string module::ctl_get( const std::string & ctl ) const { return impl->ctl_get( ctl ); } bool module::ctl_get_boolean( std::string_view ctl ) const { return impl->ctl_get_boolean( ctl ); } std::int64_t module::ctl_get_integer( std::string_view ctl ) const { return impl->ctl_get_integer( ctl ); } double module::ctl_get_floatingpoint( std::string_view ctl ) const { return impl->ctl_get_floatingpoint( ctl ); } std::string module::ctl_get_text( std::string_view ctl ) const { return impl->ctl_get_text( ctl ); } void module::ctl_set( const std::string & ctl, const std::string & value ) { impl->ctl_set( ctl, value ); } void module::ctl_set_boolean( std::string_view ctl, bool value ) { impl->ctl_set_boolean( ctl, value ); } void module::ctl_set_integer( std::string_view ctl, std::int64_t value ) { impl->ctl_set_integer( ctl, value ); } void module::ctl_set_floatingpoint( std::string_view ctl, double value ) { impl->ctl_set_floatingpoint( ctl, value ); } void module::ctl_set_text( std::string_view ctl, std::string_view value ) { impl->ctl_set_text( ctl, value ); } module_ext::module_ext( std::istream & stream, std::ostream & log, const std::map< std::string, std::string > & ctls ) : ext_impl(0) { ext_impl = new module_ext_impl( stream, openmpt::helper::make_unique( log ), ctls ); set_impl( ext_impl ); } module_ext::module_ext( const std::vector & data, std::ostream & log, const std::map< std::string, std::string > & ctls ) : ext_impl(0) { ext_impl = new module_ext_impl( data, openmpt::helper::make_unique( log ), ctls ); set_impl( ext_impl ); } module_ext::module_ext( const std::vector & data, std::ostream & log, const std::map< std::string, std::string > & ctls ) : ext_impl(0) { ext_impl = new module_ext_impl( data, openmpt::helper::make_unique( log ), ctls ); set_impl( ext_impl ); } module_ext::module_ext( const std::vector & data, std::ostream & log, const std::map< std::string, std::string > & ctls ) : ext_impl(0) { ext_impl = new module_ext_impl( data, openmpt::helper::make_unique( log ), ctls ); set_impl( ext_impl ); } module_ext::module_ext( const std::uint8_t * data, std::size_t size, std::ostream & log, const std::map< std::string, std::string > & ctls ) : ext_impl(0) { ext_impl = new module_ext_impl( data, size, openmpt::helper::make_unique( log ), ctls ); set_impl( ext_impl ); } module_ext::module_ext( const char * data, std::size_t size, std::ostream & log, const std::map< std::string, std::string > & ctls ) : ext_impl(0) { ext_impl = new module_ext_impl( data, size, openmpt::helper::make_unique( log ), ctls ); set_impl( ext_impl ); } module_ext::module_ext( const std::byte * data, std::size_t size, std::ostream & log, const std::map< std::string, std::string > & ctls ) : ext_impl(0) { ext_impl = new module_ext_impl( data, size, openmpt::helper::make_unique( log ), ctls ); set_impl( ext_impl ); } module_ext::module_ext( const void * data, std::size_t size, std::ostream & log, const std::map< std::string, std::string > & ctls ) : ext_impl(0) { ext_impl = new module_ext_impl( data, size, openmpt::helper::make_unique( log ), ctls ); set_impl( ext_impl ); } module_ext::~module_ext() { set_impl( 0 ); delete ext_impl; ext_impl = 0; } #if defined(_MSC_VER) #pragma warning(push) #pragma warning(disable:4702) // unreachable code #endif // _MSC_VER module_ext::module_ext( const module_ext & other ) : module(other), ext_impl(nullptr) { throw std::runtime_error("openmpt::module_ext is non-copyable"); } // cppcheck-suppress operatorEqVarError void module_ext::operator = ( const module_ext & ) { throw std::runtime_error("openmpt::module_ext is non-copyable"); } #if defined(_MSC_VER) #pragma warning(pop) #endif // _MSC_VER void * module_ext::get_interface( const std::string & interface_id ) { return ext_impl->get_interface( interface_id ); } } // namespace openmpt libopenmpt-0.6.1+release.autotools/libopenmpt/libopenmpt_ext_impl.cpp0000644000175000017500000003226214151240412023202 00000000000000/* * libopenmpt_ext_impl.cpp * ----------------------- * Purpose: libopenmpt extensions - implementation * Notes : * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "common/stdafx.h" #include "libopenmpt_internal.h" #include "libopenmpt_ext.hpp" #include "libopenmpt_ext_impl.hpp" #include "mpt/base/saturate_round.hpp" #include "soundlib/Sndfile.h" // assume OPENMPT_NAMESPACE is OpenMPT namespace openmpt { module_ext_impl::module_ext_impl( callback_stream_wrapper stream, std::unique_ptr log, const std::map< std::string, std::string > & ctls ) : module_impl( stream, std::move(log), ctls ) { ctor(); } module_ext_impl::module_ext_impl( std::istream & stream, std::unique_ptr log, const std::map< std::string, std::string > & ctls ) : module_impl( stream, std::move(log), ctls ) { ctor(); } module_ext_impl::module_ext_impl( const std::vector & data, std::unique_ptr log, const std::map< std::string, std::string > & ctls ) : module_impl( data, std::move(log), ctls ) { ctor(); } module_ext_impl::module_ext_impl( const std::vector & data, std::unique_ptr log, const std::map< std::string, std::string > & ctls ) : module_impl( data, std::move(log), ctls ) { ctor(); } module_ext_impl::module_ext_impl( const std::vector & data, std::unique_ptr log, const std::map< std::string, std::string > & ctls ) : module_impl( data, std::move(log), ctls ) { ctor(); } module_ext_impl::module_ext_impl( const std::byte * data, std::size_t size, std::unique_ptr log, const std::map< std::string, std::string > & ctls ) : module_impl( data, size, std::move(log), ctls ) { ctor(); } module_ext_impl::module_ext_impl( const std::uint8_t * data, std::size_t size, std::unique_ptr log, const std::map< std::string, std::string > & ctls ) : module_impl( data, size, std::move(log), ctls ) { ctor(); } module_ext_impl::module_ext_impl( const char * data, std::size_t size, std::unique_ptr log, const std::map< std::string, std::string > & ctls ) : module_impl( data, size, std::move(log), ctls ) { ctor(); } module_ext_impl::module_ext_impl( const void * data, std::size_t size, std::unique_ptr log, const std::map< std::string, std::string > & ctls ) : module_impl( data, size, std::move(log), ctls ) { ctor(); } void module_ext_impl::ctor() { /* add stuff here */ } module_ext_impl::~module_ext_impl() { /* add stuff here */ } void * module_ext_impl::get_interface( const std::string & interface_id ) { if ( interface_id.empty() ) { return 0; } else if ( interface_id == ext::pattern_vis_id ) { return dynamic_cast< ext::pattern_vis * >( this ); } else if ( interface_id == ext::interactive_id ) { return dynamic_cast< ext::interactive * >( this ); } else if ( interface_id == ext::interactive2_id ) { return dynamic_cast< ext::interactive2 * >( this ); /* add stuff here */ } else { return 0; } } // pattern_vis module_ext_impl::effect_type module_ext_impl::get_pattern_row_channel_volume_effect_type( std::int32_t pattern, std::int32_t row, std::int32_t channel ) const { std::uint8_t byte = get_pattern_row_channel_command( pattern, row, channel, module::command_volumeffect ); switch ( OpenMPT::ModCommand::GetVolumeEffectType( byte ) ) { case OpenMPT::EFFECT_TYPE_NORMAL : return effect_general; break; case OpenMPT::EFFECT_TYPE_GLOBAL : return effect_global ; break; case OpenMPT::EFFECT_TYPE_VOLUME : return effect_volume ; break; case OpenMPT::EFFECT_TYPE_PANNING: return effect_panning; break; case OpenMPT::EFFECT_TYPE_PITCH : return effect_pitch ; break; default: return effect_unknown; break; } } module_ext_impl::effect_type module_ext_impl::get_pattern_row_channel_effect_type( std::int32_t pattern, std::int32_t row, std::int32_t channel ) const { std::uint8_t byte = get_pattern_row_channel_command( pattern, row, channel, module::command_effect ); switch (OpenMPT::ModCommand::GetEffectType( byte ) ) { case OpenMPT::EFFECT_TYPE_NORMAL : return effect_general; break; case OpenMPT::EFFECT_TYPE_GLOBAL : return effect_global ; break; case OpenMPT::EFFECT_TYPE_VOLUME : return effect_volume ; break; case OpenMPT::EFFECT_TYPE_PANNING: return effect_panning; break; case OpenMPT::EFFECT_TYPE_PITCH : return effect_pitch ; break; default: return effect_unknown; break; } } // interactive void module_ext_impl::set_current_speed( std::int32_t speed ) { if ( speed < 1 || speed > 65535 ) { throw openmpt::exception("invalid tick count"); } m_sndFile->m_PlayState.m_nMusicSpeed = speed; } void module_ext_impl::set_current_tempo( std::int32_t tempo ) { if ( tempo < 32 || tempo > 512 ) { throw openmpt::exception("invalid tempo"); } m_sndFile->m_PlayState.m_nMusicTempo.Set( tempo ); } void module_ext_impl::set_tempo_factor( double factor ) { if ( factor <= 0.0 || factor > 4.0 ) { throw openmpt::exception("invalid tempo factor"); } m_sndFile->m_nTempoFactor = mpt::saturate_round( 65536.0 / factor ); m_sndFile->RecalculateSamplesPerTick(); } double module_ext_impl::get_tempo_factor( ) const { return 65536.0 / m_sndFile->m_nTempoFactor; } void module_ext_impl::set_pitch_factor( double factor ) { if ( factor <= 0.0 || factor > 4.0 ) { throw openmpt::exception("invalid pitch factor"); } m_sndFile->m_nFreqFactor = mpt::saturate_round( 65536.0 * factor ); m_sndFile->RecalculateSamplesPerTick(); } double module_ext_impl::get_pitch_factor( ) const { return m_sndFile->m_nFreqFactor / 65536.0; } void module_ext_impl::set_global_volume( double volume ) { if ( volume < 0.0 || volume > 1.0 ) { throw openmpt::exception("invalid global volume"); } m_sndFile->m_PlayState.m_nGlobalVolume = mpt::saturate_round( volume * MAX_GLOBAL_VOLUME ); } double module_ext_impl::get_global_volume( ) const { return m_sndFile->m_PlayState.m_nGlobalVolume / static_cast( MAX_GLOBAL_VOLUME ); } void module_ext_impl::set_channel_volume( std::int32_t channel, double volume ) { if ( channel < 0 || channel >= get_num_channels() ) { throw openmpt::exception("invalid channel"); } if ( volume < 0.0 || volume > 1.0 ) { throw openmpt::exception("invalid global volume"); } m_sndFile->m_PlayState.Chn[channel].nGlobalVol = mpt::saturate_round(volume * 64.0); } double module_ext_impl::get_channel_volume( std::int32_t channel ) const { if ( channel < 0 || channel >= get_num_channels() ) { throw openmpt::exception("invalid channel"); } return m_sndFile->m_PlayState.Chn[channel].nGlobalVol / 64.0; } void module_ext_impl::set_channel_mute_status( std::int32_t channel, bool mute ) { if ( channel < 0 || channel >= get_num_channels() ) { throw openmpt::exception("invalid channel"); } m_sndFile->ChnSettings[channel].dwFlags.set( OpenMPT::CHN_MUTE | OpenMPT::CHN_SYNCMUTE , mute ); m_sndFile->m_PlayState.Chn[channel].dwFlags.set( OpenMPT::CHN_MUTE | OpenMPT::CHN_SYNCMUTE , mute ); // Also update NNA channels for ( OpenMPT::CHANNELINDEX i = m_sndFile->GetNumChannels(); i < OpenMPT::MAX_CHANNELS; i++) { if ( m_sndFile->m_PlayState.Chn[i].nMasterChn == channel + 1) { m_sndFile->m_PlayState.Chn[i].dwFlags.set( OpenMPT::CHN_MUTE | OpenMPT::CHN_SYNCMUTE, mute ); } } } bool module_ext_impl::get_channel_mute_status( std::int32_t channel ) const { if ( channel < 0 || channel >= get_num_channels() ) { throw openmpt::exception("invalid channel"); } return m_sndFile->m_PlayState.Chn[channel].dwFlags[OpenMPT::CHN_MUTE]; } void module_ext_impl::set_instrument_mute_status( std::int32_t instrument, bool mute ) { const bool instrument_mode = get_num_instruments() != 0; const std::int32_t max_instrument = instrument_mode ? get_num_instruments() : get_num_samples(); if ( instrument < 0 || instrument >= max_instrument ) { throw openmpt::exception("invalid instrument"); } if ( instrument_mode ) { if ( m_sndFile->Instruments[instrument + 1] != nullptr ) { m_sndFile->Instruments[instrument + 1]->dwFlags.set( OpenMPT::INS_MUTE, mute ); } } else { m_sndFile->GetSample( static_cast( instrument + 1 ) ).uFlags.set( OpenMPT::CHN_MUTE, mute ) ; } } bool module_ext_impl::get_instrument_mute_status( std::int32_t instrument ) const { const bool instrument_mode = get_num_instruments() != 0; const std::int32_t max_instrument = instrument_mode ? get_num_instruments() : get_num_samples(); if ( instrument < 0 || instrument >= max_instrument ) { throw openmpt::exception("invalid instrument"); } if ( instrument_mode ) { if ( m_sndFile->Instruments[instrument + 1] != nullptr ) { return m_sndFile->Instruments[instrument + 1]->dwFlags[OpenMPT::INS_MUTE]; } return true; } else { return m_sndFile->GetSample( static_cast( instrument + 1 ) ).uFlags[OpenMPT::CHN_MUTE]; } } std::int32_t module_ext_impl::play_note( std::int32_t instrument, std::int32_t note, double volume, double panning ) { const bool instrument_mode = get_num_instruments() != 0; const std::int32_t max_instrument = instrument_mode ? get_num_instruments() : get_num_samples(); if ( instrument < 0 || instrument >= max_instrument ) { throw openmpt::exception("invalid instrument"); } note += OpenMPT::NOTE_MIN; if ( note < OpenMPT::NOTE_MIN || note > OpenMPT::NOTE_MAX ) { throw openmpt::exception("invalid note"); } // Find a free channel OpenMPT::CHANNELINDEX free_channel = m_sndFile->GetNNAChannel( OpenMPT::CHANNELINDEX_INVALID ); if ( free_channel == OpenMPT::CHANNELINDEX_INVALID ) { free_channel = OpenMPT::MAX_CHANNELS - 1; } OpenMPT::ModChannel &chn = m_sndFile->m_PlayState.Chn[free_channel]; chn.Reset( OpenMPT::ModChannel::resetTotal, *m_sndFile, OpenMPT::CHANNELINDEX_INVALID, OpenMPT::CHN_MUTE ); chn.nMasterChn = 0; // remove NNA association chn.nNewNote = chn.nLastNote = static_cast(note); chn.ResetEnvelopes(); m_sndFile->InstrumentChange(chn, instrument + 1); chn.nFadeOutVol = 0x10000; m_sndFile->NoteChange(chn, note, false, true, true); chn.nPan = mpt::saturate_round( OpenMPT::Clamp( panning * 128.0, -128.0, 128.0 ) + 128.0 ); chn.nVolume = mpt::saturate_round( OpenMPT::Clamp( volume * 256.0, 0.0, 256.0 ) ); // Remove channel from list of mixed channels to fix https://bugs.openmpt.org/view.php?id=209 // This is required because a previous note on the same channel might have just stopped playing, // but the channel is still in the mix list. // Since the channel volume / etc is only updated every tick in CSoundFile::ReadNote, and we // do not want to duplicate mixmode-dependant logic here, CSoundFile::CreateStereoMix may already // try to mix our newly set up channel at volume 0 if we don't remove it from the list. auto mix_begin = std::begin( m_sndFile->m_PlayState.ChnMix ); auto mix_end = std::remove( mix_begin, mix_begin + m_sndFile->m_nMixChannels, free_channel ); m_sndFile->m_nMixChannels = static_cast( std::distance( mix_begin, mix_end ) ); return free_channel; } void module_ext_impl::stop_note( std::int32_t channel ) { if ( channel < 0 || channel >= OpenMPT::MAX_CHANNELS ) { throw openmpt::exception("invalid channel"); } auto & chn = m_sndFile->m_PlayState.Chn[channel]; chn.nLength = 0; chn.pCurrentSample = nullptr; } void module_ext_impl::note_off(int32_t channel ) { if ( channel < 0 || channel >= OpenMPT::MAX_CHANNELS ) { throw openmpt::exception( "invalid channel" ); } auto & chn = m_sndFile->m_PlayState.Chn[channel]; chn.dwFlags |= OpenMPT::CHN_KEYOFF; } void module_ext_impl::note_fade(int32_t channel ) { if ( channel < 0 || channel >= OpenMPT::MAX_CHANNELS ) { throw openmpt::exception( "invalid channel" ); } auto & chn = m_sndFile->m_PlayState.Chn[channel]; chn.dwFlags |= OpenMPT::CHN_NOTEFADE; } void module_ext_impl::set_channel_panning( int32_t channel, double panning ) { if ( channel < 0 || channel >= OpenMPT::MAX_CHANNELS ) { throw openmpt::exception( "invalid channel" ); } auto & chn = m_sndFile->m_PlayState.Chn[channel]; chn.nPan = mpt::saturate_round( std::clamp( panning, -1.0, 1.0 ) * 128.0 + 128.0 ); } double module_ext_impl::get_channel_panning( int32_t channel ) { if ( channel < 0 || channel >= OpenMPT::MAX_CHANNELS ) { throw openmpt::exception( "invalid channel" ); } auto & chn = m_sndFile->m_PlayState.Chn[channel]; return ( chn.nPan - 128 ) / 128.0; } void module_ext_impl::set_note_finetune( int32_t channel, double finetune ) { if ( channel < 0 || channel >= OpenMPT::MAX_CHANNELS ) { throw openmpt::exception( "invalid channel" ); } auto & chn = m_sndFile->m_PlayState.Chn[channel]; chn.microTuning = mpt::saturate_round( finetune * 32768.0 ); } double module_ext_impl::get_note_finetune( int32_t channel ) { if ( channel < 0 || channel >= OpenMPT::MAX_CHANNELS ) { throw openmpt::exception( "invalid channel" ); } auto & chn = m_sndFile->m_PlayState.Chn[channel]; return chn.microTuning / 32768.0; } /* add stuff here */ } // namespace openmpt libopenmpt-0.6.1+release.autotools/libopenmpt/libopenmpt_impl.cpp0000644000175000017500000025153414143724705022344 00000000000000/* * libopenmpt_impl.cpp * ------------------- * Purpose: libopenmpt private interface implementation * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "common/stdafx.h" #include "libopenmpt_internal.h" #include "libopenmpt.hpp" #include "libopenmpt_impl.hpp" #include #include #include #include #include #include #include #include #include #include "mpt/audio/span.hpp" #include "mpt/base/algorithm.hpp" #include "mpt/base/saturate_cast.hpp" #include "mpt/base/saturate_round.hpp" #include "mpt/format/default_integer.hpp" #include "mpt/format/default_floatingpoint.hpp" #include "mpt/format/default_string.hpp" #include "mpt/io_read/callbackstream.hpp" #include "mpt/io_read/filecursor_callbackstream.hpp" #include "mpt/io_read/filecursor_memory.hpp" #include "mpt/io_read/filecursor_stdstream.hpp" #include "mpt/mutex/mutex.hpp" #include "mpt/parse/parse.hpp" #include "mpt/string/types.hpp" #include "mpt/string/utility.hpp" #include "mpt/string_transcode/transcode.hpp" #include "common/version.h" #include "common/misc_util.h" #include "common/Dither.h" #include "common/FileReader.h" #include "common/Logging.h" #include "soundlib/Sndfile.h" #include "soundlib/mod_specifications.h" #include "soundlib/AudioReadTarget.h" OPENMPT_NAMESPACE_BEGIN #if !defined(MPT_BUILD_SILENCE_LIBOPENMPT_CONFIGURATION_WARNINGS) #if MPT_OS_WINDOWS && MPT_OS_WINDOWS_WINRT #if defined(_WIN32_WINNT) #if (_WIN32_WINNT < 0x0602) MPT_WARNING("Warning: libopenmpt for WinRT is built with reduced functionality. Please #define _WIN32_WINNT 0x0602.") #endif // _WIN32_WINNT #endif // _WIN32_WINNT #endif // MPT_OS_WINDOWS && MPT_OS_WINDOWS_WINRT #if defined(MPT_BUILD_MSVC) || defined(MPT_BUILD_VCPKG) #if MPT_OS_WINDOWS_WINRT #pragma comment(lib, "ole32.lib") #else #pragma comment(lib, "rpcrt4.lib") #endif #ifndef NO_DMO #pragma comment(lib, "dmoguids.lib") #pragma comment(lib, "strmiids.lib") #endif // !NO_DMO #endif // MPT_BUILD_MSVC #if MPT_PLATFORM_MULTITHREADED && MPT_MUTEX_NONE MPT_WARNING("Warning: libopenmpt built in non thread-safe mode because mutexes are not supported by the C++ standard library available.") #endif // MPT_MUTEX_NONE #if (defined(__MINGW32__) || defined(__MINGW64__)) && !defined(_GLIBCXX_HAS_GTHREADS) && !defined(MPT_WITH_MINGWSTDTHREADS) MPT_WARNING("Warning: Building libopenmpt with MinGW-w64 without std::thread support is not recommended and is deprecated. Please use MinGW-w64 with posix threading model (as opposed to win32 threading model), or build with mingw-std-threads.") #endif // MINGW #if MPT_CLANG_AT_LEAST(5,0,0) && MPT_CLANG_BEFORE(11,0,0) && defined(__powerpc__) && !defined(__powerpc64__) MPT_WARNING("Warning: libopenmpt is known to trigger bad code generation with Clang 5..10 on powerpc (32bit) when using -O3. See .") #endif #endif // !MPT_BUILD_SILENCE_LIBOPENMPT_CONFIGURATION_WARNINGS #if defined(MPT_ASSERT_HANDLER_NEEDED) && !defined(ENABLE_TESTS) MPT_NOINLINE void AssertHandler(const mpt::source_location &loc, const char *expr, const char *msg) { if(msg) { mpt::log::GlobalLogger().SendLogMessage(loc, LogError, "ASSERT", MPT_USTRING("ASSERTION FAILED: ") + mpt::ToUnicode(mpt::CharsetSource, msg) + MPT_USTRING(" (") + mpt::ToUnicode(mpt::CharsetSource, expr) + MPT_USTRING(")") ); } else { mpt::log::GlobalLogger().SendLogMessage(loc, LogError, "ASSERT", MPT_USTRING("ASSERTION FAILED: ") + mpt::ToUnicode(mpt::CharsetSource, expr) ); } #if defined(MPT_BUILD_FATAL_ASSERTS) std::abort(); #endif // MPT_BUILD_FATAL_ASSERTS } #endif // MPT_ASSERT_HANDLER_NEEDED && !ENABLE_TESTS OPENMPT_NAMESPACE_END // assume OPENMPT_NAMESPACE is OpenMPT namespace openmpt { namespace version { std::uint32_t get_library_version() { return OPENMPT_API_VERSION; } std::uint32_t get_core_version() { return OpenMPT::Version::Current().GetRawVersion(); } static std::string get_library_version_string() { std::string str; const OpenMPT::SourceInfo sourceInfo = OpenMPT::SourceInfo::Current(); str += mpt::format_value_default(OPENMPT_API_VERSION_MAJOR); str += "."; str += mpt::format_value_default(OPENMPT_API_VERSION_MINOR); str += "."; str += mpt::format_value_default(OPENMPT_API_VERSION_PATCH); if ( std::string(OPENMPT_API_VERSION_PREREL).length() > 0 ) { str += OPENMPT_API_VERSION_PREREL; } std::vector fields; if ( sourceInfo.Revision() ) { fields.push_back( "r" + mpt::format_value_default( sourceInfo.Revision() ) ); } if ( sourceInfo.IsDirty() ) { fields.push_back( "modified" ); } else if ( sourceInfo.HasMixedRevisions() ) { fields.push_back( "mixed" ); } if ( sourceInfo.IsPackage() ) { fields.push_back( "pkg" ); } if ( !fields.empty() ) { str += "+"; str += OpenMPT::mpt::String::Combine( fields, std::string(".") ); } return str; } static std::string get_library_features_string() { return mpt::transcode( mpt::common_encoding::utf8, mpt::trim(OpenMPT::Build::GetBuildFeaturesString())); } static std::string get_core_version_string() { return mpt::transcode( mpt::common_encoding::utf8, OpenMPT::Build::GetVersionStringExtended()); } static std::string get_source_url_string() { return mpt::transcode( mpt::common_encoding::utf8, OpenMPT::SourceInfo::Current().GetUrlWithRevision()); } static std::string get_source_date_string() { return mpt::transcode( mpt::common_encoding::utf8, OpenMPT::SourceInfo::Current().Date()); } static std::string get_source_revision_string() { const OpenMPT::SourceInfo sourceInfo = OpenMPT::SourceInfo::Current(); return sourceInfo.Revision() ? mpt::format_value_default(sourceInfo.Revision()) : std::string(); } static std::string get_build_string() { return mpt::transcode( mpt::common_encoding::utf8, OpenMPT::Build::GetBuildDateString()); } static std::string get_build_compiler_string() { return mpt::transcode( mpt::common_encoding::utf8, OpenMPT::Build::GetBuildCompilerString()); } static std::string get_credits_string() { return mpt::transcode( mpt::common_encoding::utf8, OpenMPT::Build::GetFullCreditsString()); } static std::string get_contact_string() { return mpt::transcode( mpt::common_encoding::utf8, MPT_USTRING("Forum: ") + OpenMPT::Build::GetURL(OpenMPT::Build::Url::Forum)); } static std::string get_license_string() { return mpt::transcode( mpt::common_encoding::utf8, OpenMPT::Build::GetLicenseString()); } static std::string get_url_string() { return mpt::transcode( mpt::common_encoding::utf8, OpenMPT::Build::GetURL(OpenMPT::Build::Url::Website)); } static std::string get_support_forum_url_string() { return mpt::transcode( mpt::common_encoding::utf8, OpenMPT::Build::GetURL(OpenMPT::Build::Url::Forum)); } static std::string get_bugtracker_url_string() { return mpt::transcode( mpt::common_encoding::utf8, OpenMPT::Build::GetURL(OpenMPT::Build::Url::Bugtracker)); } std::string get_string( const std::string & key ) { if ( key == "" ) { return std::string(); } else if ( key == "library_version" ) { return get_library_version_string(); } else if ( key == "library_version_major" ) { return mpt::format_value_default(OPENMPT_API_VERSION_MAJOR); } else if ( key == "library_version_minor" ) { return mpt::format_value_default(OPENMPT_API_VERSION_MINOR); } else if ( key == "library_version_patch" ) { return mpt::format_value_default(OPENMPT_API_VERSION_PATCH); } else if ( key == "library_version_prerel" ) { return mpt::format_value_default(OPENMPT_API_VERSION_PREREL); } else if ( key == "library_version_is_release" ) { return ( std::string(OPENMPT_API_VERSION_PREREL).length() == 0 ) ? "1" : "0"; } else if ( key == "library_features" ) { return get_library_features_string(); } else if ( key == "core_version" ) { return get_core_version_string(); } else if ( key == "source_url" ) { return get_source_url_string(); } else if ( key == "source_date" ) { return get_source_date_string(); } else if ( key == "source_revision" ) { return get_source_revision_string(); } else if ( key == "source_is_modified" ) { return OpenMPT::SourceInfo::Current().IsDirty() ? "1" : "0"; } else if ( key == "source_has_mixed_revision" ) { return OpenMPT::SourceInfo::Current().HasMixedRevisions() ? "1" : "0"; } else if ( key == "source_is_package" ) { return OpenMPT::SourceInfo::Current().IsPackage() ? "1" : "0"; } else if ( key == "build" ) { return get_build_string(); } else if ( key == "build_compiler" ) { return get_build_compiler_string(); } else if ( key == "credits" ) { return get_credits_string(); } else if ( key == "contact" ) { return get_contact_string(); } else if ( key == "license" ) { return get_license_string(); } else if ( key == "url" ) { return get_url_string(); } else if ( key == "support_forum_url" ) { return get_support_forum_url_string(); } else if ( key == "bugtracker_url" ) { return get_bugtracker_url_string(); } else { return std::string(); } } } // namespace version log_interface::log_interface() { return; } log_interface::~log_interface() { return; } std_ostream_log::std_ostream_log( std::ostream & dst ) : destination(dst) { return; } std_ostream_log::~std_ostream_log() { return; } void std_ostream_log::log( const std::string & message ) const { destination.flush(); destination << message << std::endl; destination.flush(); } class log_forwarder : public OpenMPT::ILog { private: log_interface & destination; public: log_forwarder( log_interface & dest ) : destination(dest) { return; } private: void AddToLog( OpenMPT::LogLevel level, const mpt::ustring & text ) const override { destination.log( mpt::transcode( mpt::common_encoding::utf8, LogLevelToString( level ) + MPT_USTRING(": ") + text ) ); } }; // class log_forwarder class loader_log : public OpenMPT::ILog { private: mutable std::vector > m_Messages; public: std::vector > GetMessages() const; private: void AddToLog( OpenMPT::LogLevel level, const mpt::ustring & text ) const override; }; // class loader_log std::vector > loader_log::GetMessages() const { return m_Messages; } void loader_log::AddToLog( OpenMPT::LogLevel level, const mpt::ustring & text ) const { m_Messages.push_back( std::make_pair( level, mpt::transcode( mpt::common_encoding::utf8, text ) ) ); } void module_impl::PushToCSoundFileLog( const std::string & text ) const { m_sndFile->AddToLog( OpenMPT::LogError, mpt::transcode( mpt::common_encoding::utf8, text ) ); } void module_impl::PushToCSoundFileLog( int loglevel, const std::string & text ) const { m_sndFile->AddToLog( static_cast( loglevel ), mpt::transcode( mpt::common_encoding::utf8, text ) ); } module_impl::subsong_data::subsong_data( double duration, std::int32_t start_row, std::int32_t start_order, std::int32_t sequence ) : duration(duration) , start_row(start_row) , start_order(start_order) , sequence(sequence) { return; } static OpenMPT::ResamplingMode filterlength_to_resamplingmode(std::int32_t length) { OpenMPT::ResamplingMode result = OpenMPT::SRCMODE_SINC8LP; if ( length == 0 ) { result = OpenMPT::SRCMODE_SINC8LP; } else if ( length >= 8 ) { result = OpenMPT::SRCMODE_SINC8LP; } else if ( length >= 3 ) { result = OpenMPT::SRCMODE_CUBIC; } else if ( length >= 2 ) { result = OpenMPT::SRCMODE_LINEAR; } else if ( length >= 1 ) { result = OpenMPT::SRCMODE_NEAREST; } else { throw openmpt::exception("negative filter length"); } return result; } static std::int32_t resamplingmode_to_filterlength(OpenMPT::ResamplingMode mode) { switch ( mode ) { case OpenMPT::SRCMODE_NEAREST: return 1; break; case OpenMPT::SRCMODE_LINEAR: return 2; break; case OpenMPT::SRCMODE_CUBIC: return 4; break; case OpenMPT::SRCMODE_SINC8: case OpenMPT::SRCMODE_SINC8LP: case OpenMPT::SRCMODE_DEFAULT: return 8; default: throw openmpt::exception("unknown interpolation filter length set internally"); break; } } template < typename sample_type > static inline std::size_t valid_channels( sample_type * const * buffers, std::size_t max_channels ) { std::size_t channel; for ( channel = 0; channel < max_channels; ++channel ) { if ( !buffers[ channel ] ) { break; } } return channel; } static OpenMPT::Resampling::AmigaFilter translate_amiga_filter_type( module_impl::amiga_filter_type amiga_type ) { switch (amiga_type ) { case module_impl::amiga_filter_type::a500: return OpenMPT::Resampling::AmigaFilter::A500; case module_impl::amiga_filter_type::a1200: case module_impl::amiga_filter_type::auto_filter: default: return OpenMPT::Resampling::AmigaFilter::A1200; case module_impl::amiga_filter_type::unfiltered: return OpenMPT::Resampling::AmigaFilter::Unfiltered; } } static void ramping_to_mixersettings( OpenMPT::MixerSettings & settings, int ramping ) { if ( ramping == -1 ) { settings.SetVolumeRampUpMicroseconds( OpenMPT::MixerSettings().GetVolumeRampUpMicroseconds() ); settings.SetVolumeRampDownMicroseconds( OpenMPT::MixerSettings().GetVolumeRampDownMicroseconds() ); } else if ( ramping <= 0 ) { settings.SetVolumeRampUpMicroseconds( 0 ); settings.SetVolumeRampDownMicroseconds( 0 ); } else { settings.SetVolumeRampUpMicroseconds( ramping * 1000 ); settings.SetVolumeRampDownMicroseconds( ramping * 1000 ); } } static void mixersettings_to_ramping( int & ramping, const OpenMPT::MixerSettings & settings ) { std::int32_t ramp_us = std::max( settings.GetVolumeRampUpMicroseconds(), settings.GetVolumeRampDownMicroseconds() ); if ( ( settings.GetVolumeRampUpMicroseconds() == OpenMPT::MixerSettings().GetVolumeRampUpMicroseconds() ) && ( settings.GetVolumeRampDownMicroseconds() == OpenMPT::MixerSettings().GetVolumeRampDownMicroseconds() ) ) { ramping = -1; } else if ( ramp_us <= 0 ) { ramping = 0; } else { ramping = ( ramp_us + 500 ) / 1000; } } std::string module_impl::mod_string_to_utf8( const std::string & encoded ) const { return OpenMPT::mpt::ToCharset( OpenMPT::mpt::Charset::UTF8, m_sndFile->GetCharsetInternal(), encoded ); } void module_impl::apply_mixer_settings( std::int32_t samplerate, int channels ) { bool samplerate_changed = static_cast( m_sndFile->m_MixerSettings.gdwMixingFreq ) != samplerate; bool channels_changed = static_cast( m_sndFile->m_MixerSettings.gnChannels ) != channels; if ( samplerate_changed || channels_changed ) { OpenMPT::MixerSettings mixersettings = m_sndFile->m_MixerSettings; std::int32_t volrampin_us = mixersettings.GetVolumeRampUpMicroseconds(); std::int32_t volrampout_us = mixersettings.GetVolumeRampDownMicroseconds(); mixersettings.gdwMixingFreq = samplerate; mixersettings.gnChannels = channels; mixersettings.SetVolumeRampUpMicroseconds( volrampin_us ); mixersettings.SetVolumeRampDownMicroseconds( volrampout_us ); m_sndFile->SetMixerSettings( mixersettings ); } else if ( !m_mixer_initialized ) { m_sndFile->InitPlayer( true ); } if ( samplerate_changed ) { m_sndFile->SuspendPlugins(); m_sndFile->ResumePlugins(); } m_mixer_initialized = true; } void module_impl::apply_libopenmpt_defaults() { set_render_param( module::RENDER_STEREOSEPARATION_PERCENT, 100 ); m_sndFile->Order.SetSequence( 0 ); } module_impl::subsongs_type module_impl::get_subsongs() const { std::vector subsongs; if ( m_sndFile->Order.GetNumSequences() == 0 ) { throw openmpt::exception("module contains no songs"); } for ( OpenMPT::SEQUENCEINDEX seq = 0; seq < m_sndFile->Order.GetNumSequences(); ++seq ) { const std::vector lengths = m_sndFile->GetLength( OpenMPT::eNoAdjust, OpenMPT::GetLengthTarget( true ).StartPos( seq, 0, 0 ) ); for ( const auto & l : lengths ) { subsongs.push_back( subsong_data( l.duration, l.startRow, l.startOrder, seq ) ); } } return subsongs; } void module_impl::init_subsongs( subsongs_type & subsongs ) const { subsongs = get_subsongs(); } bool module_impl::has_subsongs_inited() const { return !m_subsongs.empty(); } void module_impl::ctor( const std::map< std::string, std::string > & ctls ) { m_sndFile = std::make_unique(); m_loaded = false; m_mixer_initialized = false; m_Dithers = std::make_unique( OpenMPT::mpt::global_prng(), OpenMPT::DithersWrapperOpenMPT::DefaultDither, 4 ); m_LogForwarder = std::make_unique( *m_Log ); m_sndFile->SetCustomLog( m_LogForwarder.get() ); m_current_subsong = 0; m_currentPositionSeconds = 0.0; m_Gain = 1.0f; m_ctl_play_at_end = song_end_action::fadeout_song; m_ctl_load_skip_samples = false; m_ctl_load_skip_patterns = false; m_ctl_load_skip_plugins = false; m_ctl_load_skip_subsongs_init = false; m_ctl_seek_sync_samples = false; // init member variables that correspond to ctls for ( const auto & ctl : ctls ) { ctl_set( ctl.first, ctl.second, false ); } } void module_impl::load( const OpenMPT::FileCursor & file, const std::map< std::string, std::string > & ctls ) { loader_log loaderlog; m_sndFile->SetCustomLog( &loaderlog ); { int load_flags = OpenMPT::CSoundFile::loadCompleteModule; if ( m_ctl_load_skip_samples ) { load_flags &= ~OpenMPT::CSoundFile::loadSampleData; } if ( m_ctl_load_skip_patterns ) { load_flags &= ~OpenMPT::CSoundFile::loadPatternData; } if ( m_ctl_load_skip_plugins ) { load_flags &= ~(OpenMPT::CSoundFile::loadPluginData | OpenMPT::CSoundFile::loadPluginInstance); } if ( !m_sndFile->Create( file, static_cast( load_flags ) ) ) { throw openmpt::exception("error loading file"); } if ( !m_ctl_load_skip_subsongs_init ) { init_subsongs( m_subsongs ); } m_loaded = true; } m_sndFile->SetCustomLog( m_LogForwarder.get() ); std::vector > loaderMessages = loaderlog.GetMessages(); for ( const auto & msg : loaderMessages ) { PushToCSoundFileLog( msg.first, msg.second ); m_loaderMessages.push_back( mpt::transcode( mpt::common_encoding::utf8, LogLevelToString( msg.first ) ) + std::string(": ") + msg.second ); } // init CSoundFile state that corresponds to ctls for ( const auto & ctl : ctls ) { ctl_set( ctl.first, ctl.second, false ); } } bool module_impl::is_loaded() const { return m_loaded; } std::size_t module_impl::read_wrapper( std::size_t count, std::int16_t * left, std::int16_t * right, std::int16_t * rear_left, std::int16_t * rear_right ) { m_sndFile->ResetMixStat(); m_sndFile->m_bIsRendering = ( m_ctl_play_at_end != song_end_action::fadeout_song ); std::size_t count_read = 0; std::int16_t * const buffers[4] = { left, right, rear_left, rear_right }; OpenMPT::AudioTargetBufferWithGain> target( mpt::audio_span_planar( buffers, valid_channels( buffers, std::size( buffers ) ), count ), *m_Dithers, m_Gain ); while ( count > 0 ) { std::size_t count_chunk = m_sndFile->Read( static_cast( std::min( static_cast( count ), static_cast( std::numeric_limits::max() / 2 / 4 / 4 ) ) ), // safety margin / samplesize / channels target ); if ( count_chunk == 0 ) { break; } count -= count_chunk; count_read += count_chunk; } if ( count_read == 0 && m_ctl_play_at_end == song_end_action::continue_song ) { // This is the song end, but allow the song or loop to restart on the next call m_sndFile->m_SongFlags.reset(OpenMPT::SONG_ENDREACHED); } return count_read; } std::size_t module_impl::read_wrapper( std::size_t count, float * left, float * right, float * rear_left, float * rear_right ) { m_sndFile->ResetMixStat(); m_sndFile->m_bIsRendering = ( m_ctl_play_at_end != song_end_action::fadeout_song ); std::size_t count_read = 0; float * const buffers[4] = { left, right, rear_left, rear_right }; OpenMPT::AudioTargetBufferWithGain> target( mpt::audio_span_planar( buffers, valid_channels( buffers, std::size( buffers ) ), count ), *m_Dithers, m_Gain ); while ( count > 0 ) { std::size_t count_chunk = m_sndFile->Read( static_cast( std::min( static_cast( count ), static_cast( std::numeric_limits::max() / 2 / 4 / 4 ) ) ), // safety margin / samplesize / channels target ); if ( count_chunk == 0 ) { break; } count -= count_chunk; count_read += count_chunk; } if ( count_read == 0 && m_ctl_play_at_end == song_end_action::continue_song ) { // This is the song end, but allow the song or loop to restart on the next call m_sndFile->m_SongFlags.reset(OpenMPT::SONG_ENDREACHED); } return count_read; } std::size_t module_impl::read_interleaved_wrapper( std::size_t count, std::size_t channels, std::int16_t * interleaved ) { m_sndFile->ResetMixStat(); m_sndFile->m_bIsRendering = ( m_ctl_play_at_end != song_end_action::fadeout_song ); std::size_t count_read = 0; OpenMPT::AudioTargetBufferWithGain> target( mpt::audio_span_interleaved( interleaved, channels, count ), *m_Dithers, m_Gain ); while ( count > 0 ) { std::size_t count_chunk = m_sndFile->Read( static_cast( std::min( static_cast( count ), static_cast( std::numeric_limits::max() / 2 / 4 / 4 ) ) ), // safety margin / samplesize / channels target ); if ( count_chunk == 0 ) { break; } count -= count_chunk; count_read += count_chunk; } if ( count_read == 0 && m_ctl_play_at_end == song_end_action::continue_song ) { // This is the song end, but allow the song or loop to restart on the next call m_sndFile->m_SongFlags.reset(OpenMPT::SONG_ENDREACHED); } return count_read; } std::size_t module_impl::read_interleaved_wrapper( std::size_t count, std::size_t channels, float * interleaved ) { m_sndFile->ResetMixStat(); m_sndFile->m_bIsRendering = ( m_ctl_play_at_end != song_end_action::fadeout_song ); std::size_t count_read = 0; OpenMPT::AudioTargetBufferWithGain> target( mpt::audio_span_interleaved( interleaved, channels, count ), *m_Dithers, m_Gain ); while ( count > 0 ) { std::size_t count_chunk = m_sndFile->Read( static_cast( std::min( static_cast( count ), static_cast( std::numeric_limits::max() / 2 / 4 / 4 ) ) ), // safety margin / samplesize / channels target ); if ( count_chunk == 0 ) { break; } count -= count_chunk; count_read += count_chunk; } if ( count_read == 0 && m_ctl_play_at_end == song_end_action::continue_song ) { // This is the song end, but allow the song or loop to restart on the next call m_sndFile->m_SongFlags.reset(OpenMPT::SONG_ENDREACHED); } return count_read; } std::vector module_impl::get_supported_extensions() { std::vector retval; std::vector extensions = OpenMPT::CSoundFile::GetSupportedExtensions( false ); std::copy( extensions.begin(), extensions.end(), std::back_insert_iterator >( retval ) ); return retval; } bool module_impl::is_extension_supported( std::string_view extension ) { return OpenMPT::CSoundFile::IsExtensionSupported( extension ); } double module_impl::could_open_probability( const OpenMPT::FileCursor & file, double effort, std::unique_ptr log ) { try { if ( effort >= 0.8 ) { std::unique_ptr sndFile = std::make_unique(); std::unique_ptr logForwarder = std::make_unique( *log ); sndFile->SetCustomLog( logForwarder.get() ); if ( !sndFile->Create( file, OpenMPT::CSoundFile::loadCompleteModule ) ) { return 0.0; } sndFile->Destroy(); return 1.0; } else if ( effort >= 0.6 ) { std::unique_ptr sndFile = std::make_unique(); std::unique_ptr logForwarder = std::make_unique( *log ); sndFile->SetCustomLog( logForwarder.get() ); if ( !sndFile->Create( file, OpenMPT::CSoundFile::loadNoPatternOrPluginData ) ) { return 0.0; } sndFile->Destroy(); return 0.8; } else if ( effort >= 0.2 ) { std::unique_ptr sndFile = std::make_unique(); std::unique_ptr logForwarder = std::make_unique( *log ); sndFile->SetCustomLog( logForwarder.get() ); if ( !sndFile->Create( file, OpenMPT::CSoundFile::onlyVerifyHeader ) ) { return 0.0; } sndFile->Destroy(); return 0.6; } else if ( effort >= 0.1 ) { OpenMPT::FileCursor::PinnedView view = file.GetPinnedView( probe_file_header_get_recommended_size() ); int probe_file_header_result = probe_file_header( probe_file_header_flags_default2, view.data(), view.size(), file.GetLength() ); double result = 0.0; switch ( probe_file_header_result ) { case probe_file_header_result_success: result = 0.6; break; case probe_file_header_result_failure: result = 0.0; break; case probe_file_header_result_wantmoredata: result = 0.3; break; default: throw openmpt::exception(""); break; } return result; } else { return 0.2; } } catch ( ... ) { return 0.0; } } double module_impl::could_open_probability( callback_stream_wrapper stream, double effort, std::unique_ptr log ) { mpt::IO::CallbackStream fstream; fstream.stream = stream.stream; fstream.read = stream.read; fstream.seek = stream.seek; fstream.tell = stream.tell; return could_open_probability( mpt::IO::make_FileCursor( fstream ), effort, std::move(log) ); } double module_impl::could_open_probability( std::istream & stream, double effort, std::unique_ptr log ) { return could_open_probability(mpt::IO::make_FileCursor( stream ), effort, std::move(log) ); } std::size_t module_impl::probe_file_header_get_recommended_size() { return OpenMPT::CSoundFile::ProbeRecommendedSize; } int module_impl::probe_file_header( std::uint64_t flags, const std::byte * data, std::size_t size, std::uint64_t filesize ) { int result = 0; switch ( OpenMPT::CSoundFile::Probe( static_cast( flags ), mpt::span( data, size ), &filesize ) ) { case OpenMPT::CSoundFile::ProbeSuccess: result = probe_file_header_result_success; break; case OpenMPT::CSoundFile::ProbeFailure: result = probe_file_header_result_failure; break; case OpenMPT::CSoundFile::ProbeWantMoreData: result = probe_file_header_result_wantmoredata; break; default: throw exception("internal error"); break; } return result; } int module_impl::probe_file_header( std::uint64_t flags, const std::uint8_t * data, std::size_t size, std::uint64_t filesize ) { int result = 0; switch ( OpenMPT::CSoundFile::Probe( static_cast( flags ), mpt::span( mpt::byte_cast( data ), size ), &filesize ) ) { case OpenMPT::CSoundFile::ProbeSuccess: result = probe_file_header_result_success; break; case OpenMPT::CSoundFile::ProbeFailure: result = probe_file_header_result_failure; break; case OpenMPT::CSoundFile::ProbeWantMoreData: result = probe_file_header_result_wantmoredata; break; default: throw exception("internal error"); break; } return result; } int module_impl::probe_file_header( std::uint64_t flags, const void * data, std::size_t size, std::uint64_t filesize ) { int result = 0; switch ( OpenMPT::CSoundFile::Probe( static_cast( flags ), mpt::span( mpt::void_cast( data ), size ), &filesize ) ) { case OpenMPT::CSoundFile::ProbeSuccess: result = probe_file_header_result_success; break; case OpenMPT::CSoundFile::ProbeFailure: result = probe_file_header_result_failure; break; case OpenMPT::CSoundFile::ProbeWantMoreData: result = probe_file_header_result_wantmoredata; break; default: throw exception("internal error"); break; } return result; } int module_impl::probe_file_header( std::uint64_t flags, const std::byte * data, std::size_t size ) { int result = 0; switch ( OpenMPT::CSoundFile::Probe( static_cast( flags ), mpt::span( data, size ), nullptr ) ) { case OpenMPT::CSoundFile::ProbeSuccess: result = probe_file_header_result_success; break; case OpenMPT::CSoundFile::ProbeFailure: result = probe_file_header_result_failure; break; case OpenMPT::CSoundFile::ProbeWantMoreData: result = probe_file_header_result_wantmoredata; break; default: throw exception("internal error"); break; } return result; } int module_impl::probe_file_header( std::uint64_t flags, const std::uint8_t * data, std::size_t size ) { int result = 0; switch ( OpenMPT::CSoundFile::Probe( static_cast( flags ), mpt::span( mpt::byte_cast( data ), size ), nullptr ) ) { case OpenMPT::CSoundFile::ProbeSuccess: result = probe_file_header_result_success; break; case OpenMPT::CSoundFile::ProbeFailure: result = probe_file_header_result_failure; break; case OpenMPT::CSoundFile::ProbeWantMoreData: result = probe_file_header_result_wantmoredata; break; default: throw exception("internal error"); break; } return result; } int module_impl::probe_file_header( std::uint64_t flags, const void * data, std::size_t size ) { int result = 0; switch ( OpenMPT::CSoundFile::Probe( static_cast( flags ), mpt::span( mpt::void_cast( data ), size ), nullptr ) ) { case OpenMPT::CSoundFile::ProbeSuccess: result = probe_file_header_result_success; break; case OpenMPT::CSoundFile::ProbeFailure: result = probe_file_header_result_failure; break; case OpenMPT::CSoundFile::ProbeWantMoreData: result = probe_file_header_result_wantmoredata; break; default: throw exception("internal error"); break; } return result; } int module_impl::probe_file_header( std::uint64_t flags, std::istream & stream ) { int result = 0; char buffer[ PROBE_RECOMMENDED_SIZE ]; OpenMPT::MemsetZero( buffer ); std::size_t size_read = 0; std::size_t size_toread = OpenMPT::CSoundFile::ProbeRecommendedSize; if ( stream.bad() ) { throw exception("error reading stream"); } const bool seekable = mpt::IO::FileDataStdStream::IsSeekable( stream ); const std::uint64_t filesize = ( seekable ? mpt::IO::FileDataStdStream::GetLength( stream ) : 0 ); while ( ( size_toread > 0 ) && stream ) { stream.read( buffer + size_read, size_toread ); if ( stream.bad() ) { throw exception("error reading stream"); } else if ( stream.eof() ) { // normal } else if ( stream.fail() ) { throw exception("error reading stream"); } else { // normal } std::size_t read_count = static_cast( stream.gcount() ); size_read += read_count; size_toread -= read_count; } switch ( OpenMPT::CSoundFile::Probe( static_cast( flags ), mpt::span( mpt::byte_cast( buffer ), size_read ), seekable ? &filesize : nullptr ) ) { case OpenMPT::CSoundFile::ProbeSuccess: result = probe_file_header_result_success; break; case OpenMPT::CSoundFile::ProbeFailure: result = probe_file_header_result_failure; break; case OpenMPT::CSoundFile::ProbeWantMoreData: result = probe_file_header_result_wantmoredata; break; default: throw exception("internal error"); break; } return result; } int module_impl::probe_file_header( std::uint64_t flags, callback_stream_wrapper stream ) { int result = 0; char buffer[ PROBE_RECOMMENDED_SIZE ]; OpenMPT::MemsetZero( buffer ); std::size_t size_read = 0; std::size_t size_toread = OpenMPT::CSoundFile::ProbeRecommendedSize; if ( !stream.read ) { throw exception("error reading stream"); } mpt::IO::CallbackStream fstream; fstream.stream = stream.stream; fstream.read = stream.read; fstream.seek = stream.seek; fstream.tell = stream.tell; const bool seekable = mpt::IO::FileDataCallbackStream::IsSeekable( fstream ); const std::uint64_t filesize = ( seekable ? mpt::IO::FileDataCallbackStream::GetLength( fstream ) : 0 ); while ( size_toread > 0 ) { std::size_t read_count = stream.read( stream.stream, buffer + size_read, size_toread ); size_read += read_count; size_toread -= read_count; if ( read_count == 0 ) { // eof break; } } switch ( OpenMPT::CSoundFile::Probe( static_cast( flags ), mpt::span( mpt::byte_cast( buffer ), size_read ), seekable ? &filesize : nullptr ) ) { case OpenMPT::CSoundFile::ProbeSuccess: result = probe_file_header_result_success; break; case OpenMPT::CSoundFile::ProbeFailure: result = probe_file_header_result_failure; break; case OpenMPT::CSoundFile::ProbeWantMoreData: result = probe_file_header_result_wantmoredata; break; default: throw exception("internal error"); break; } return result; } module_impl::module_impl( callback_stream_wrapper stream, std::unique_ptr log, const std::map< std::string, std::string > & ctls ) : m_Log(std::move(log)) { ctor( ctls ); mpt::IO::CallbackStream fstream; fstream.stream = stream.stream; fstream.read = stream.read; fstream.seek = stream.seek; fstream.tell = stream.tell; load( mpt::IO::make_FileCursor( fstream ), ctls ); apply_libopenmpt_defaults(); } module_impl::module_impl( std::istream & stream, std::unique_ptr log, const std::map< std::string, std::string > & ctls ) : m_Log(std::move(log)) { ctor( ctls ); load( mpt::IO::make_FileCursor( stream ), ctls ); apply_libopenmpt_defaults(); } module_impl::module_impl( const std::vector & data, std::unique_ptr log, const std::map< std::string, std::string > & ctls ) : m_Log(std::move(log)) { ctor( ctls ); load( mpt::IO::make_FileCursor( mpt::as_span( data ) ), ctls ); apply_libopenmpt_defaults(); } module_impl::module_impl( const std::vector & data, std::unique_ptr log, const std::map< std::string, std::string > & ctls ) : m_Log(std::move(log)) { ctor( ctls ); load( mpt::IO::make_FileCursor( mpt::as_span( data ) ), ctls ); apply_libopenmpt_defaults(); } module_impl::module_impl( const std::vector & data, std::unique_ptr log, const std::map< std::string, std::string > & ctls ) : m_Log(std::move(log)) { ctor( ctls ); load( mpt::IO::make_FileCursor( mpt::byte_cast< mpt::span< const std::byte > >( mpt::as_span( data ) ) ), ctls ); apply_libopenmpt_defaults(); } module_impl::module_impl( const std::byte * data, std::size_t size, std::unique_ptr log, const std::map< std::string, std::string > & ctls ) : m_Log(std::move(log)) { ctor( ctls ); load( mpt::IO::make_FileCursor( mpt::as_span( data, size ) ), ctls ); apply_libopenmpt_defaults(); } module_impl::module_impl( const std::uint8_t * data, std::size_t size, std::unique_ptr log, const std::map< std::string, std::string > & ctls ) : m_Log(std::move(log)) { ctor( ctls ); load( mpt::IO::make_FileCursor( mpt::as_span( data, size ) ), ctls ); apply_libopenmpt_defaults(); } module_impl::module_impl( const char * data, std::size_t size, std::unique_ptr log, const std::map< std::string, std::string > & ctls ) : m_Log(std::move(log)) { ctor( ctls ); load( mpt::IO::make_FileCursor( mpt::byte_cast< mpt::span< const std::byte > >( mpt::as_span( data, size ) ) ), ctls ); apply_libopenmpt_defaults(); } module_impl::module_impl( const void * data, std::size_t size, std::unique_ptr log, const std::map< std::string, std::string > & ctls ) : m_Log(std::move(log)) { ctor( ctls ); load( mpt::IO::make_FileCursor( mpt::as_span( mpt::void_cast< const std::byte * >( data ), size ) ), ctls ); apply_libopenmpt_defaults(); } module_impl::~module_impl() { m_sndFile->Destroy(); } std::int32_t module_impl::get_render_param( int param ) const { std::int32_t result = 0; switch ( param ) { case module::RENDER_MASTERGAIN_MILLIBEL: { result = static_cast( 1000.0f * 2.0f * std::log10( m_Gain ) ); } break; case module::RENDER_STEREOSEPARATION_PERCENT: { result = m_sndFile->m_MixerSettings.m_nStereoSeparation * 100 / OpenMPT::MixerSettings::StereoSeparationScale; } break; case module::RENDER_INTERPOLATIONFILTER_LENGTH: { result = resamplingmode_to_filterlength( m_sndFile->m_Resampler.m_Settings.SrcMode ); } break; case module::RENDER_VOLUMERAMPING_STRENGTH: { int ramping = 0; mixersettings_to_ramping( ramping, m_sndFile->m_MixerSettings ); result = ramping; } break; default: throw openmpt::exception("unknown render param"); break; } return result; } void module_impl::set_render_param( int param, std::int32_t value ) { switch ( param ) { case module::RENDER_MASTERGAIN_MILLIBEL: { m_Gain = static_cast( std::pow( 10.0f, value * 0.001f * 0.5f ) ); } break; case module::RENDER_STEREOSEPARATION_PERCENT: { std::int32_t newvalue = value * OpenMPT::MixerSettings::StereoSeparationScale / 100; if ( newvalue != static_cast( m_sndFile->m_MixerSettings.m_nStereoSeparation ) ) { OpenMPT::MixerSettings settings = m_sndFile->m_MixerSettings; settings.m_nStereoSeparation = newvalue; m_sndFile->SetMixerSettings( settings ); } } break; case module::RENDER_INTERPOLATIONFILTER_LENGTH: { OpenMPT::CResamplerSettings newsettings = m_sndFile->m_Resampler.m_Settings; newsettings.SrcMode = filterlength_to_resamplingmode( value ); if ( newsettings != m_sndFile->m_Resampler.m_Settings ) { m_sndFile->SetResamplerSettings( newsettings ); } } break; case module::RENDER_VOLUMERAMPING_STRENGTH: { OpenMPT::MixerSettings newsettings = m_sndFile->m_MixerSettings; ramping_to_mixersettings( newsettings, value ); if ( m_sndFile->m_MixerSettings.VolumeRampUpMicroseconds != newsettings.VolumeRampUpMicroseconds || m_sndFile->m_MixerSettings.VolumeRampDownMicroseconds != newsettings.VolumeRampDownMicroseconds ) { m_sndFile->SetMixerSettings( newsettings ); } } break; default: throw openmpt::exception("unknown render param"); break; } } std::size_t module_impl::read( std::int32_t samplerate, std::size_t count, std::int16_t * mono ) { if ( !mono ) { throw openmpt::exception("null pointer"); } apply_mixer_settings( samplerate, 1 ); count = read_wrapper( count, mono, nullptr, nullptr, nullptr ); m_currentPositionSeconds += static_cast( count ) / static_cast( samplerate ); return count; } std::size_t module_impl::read( std::int32_t samplerate, std::size_t count, std::int16_t * left, std::int16_t * right ) { if ( !left || !right ) { throw openmpt::exception("null pointer"); } apply_mixer_settings( samplerate, 2 ); count = read_wrapper( count, left, right, nullptr, nullptr ); m_currentPositionSeconds += static_cast( count ) / static_cast( samplerate ); return count; } std::size_t module_impl::read( std::int32_t samplerate, std::size_t count, std::int16_t * left, std::int16_t * right, std::int16_t * rear_left, std::int16_t * rear_right ) { if ( !left || !right || !rear_left || !rear_right ) { throw openmpt::exception("null pointer"); } apply_mixer_settings( samplerate, 4 ); count = read_wrapper( count, left, right, rear_left, rear_right ); m_currentPositionSeconds += static_cast( count ) / static_cast( samplerate ); return count; } std::size_t module_impl::read( std::int32_t samplerate, std::size_t count, float * mono ) { if ( !mono ) { throw openmpt::exception("null pointer"); } apply_mixer_settings( samplerate, 1 ); count = read_wrapper( count, mono, nullptr, nullptr, nullptr ); m_currentPositionSeconds += static_cast( count ) / static_cast( samplerate ); return count; } std::size_t module_impl::read( std::int32_t samplerate, std::size_t count, float * left, float * right ) { if ( !left || !right ) { throw openmpt::exception("null pointer"); } apply_mixer_settings( samplerate, 2 ); count = read_wrapper( count, left, right, nullptr, nullptr ); m_currentPositionSeconds += static_cast( count ) / static_cast( samplerate ); return count; } std::size_t module_impl::read( std::int32_t samplerate, std::size_t count, float * left, float * right, float * rear_left, float * rear_right ) { if ( !left || !right || !rear_left || !rear_right ) { throw openmpt::exception("null pointer"); } apply_mixer_settings( samplerate, 4 ); count = read_wrapper( count, left, right, rear_left, rear_right ); m_currentPositionSeconds += static_cast( count ) / static_cast( samplerate ); return count; } std::size_t module_impl::read_interleaved_stereo( std::int32_t samplerate, std::size_t count, std::int16_t * interleaved_stereo ) { if ( !interleaved_stereo ) { throw openmpt::exception("null pointer"); } apply_mixer_settings( samplerate, 2 ); count = read_interleaved_wrapper( count, 2, interleaved_stereo ); m_currentPositionSeconds += static_cast( count ) / static_cast( samplerate ); return count; } std::size_t module_impl::read_interleaved_quad( std::int32_t samplerate, std::size_t count, std::int16_t * interleaved_quad ) { if ( !interleaved_quad ) { throw openmpt::exception("null pointer"); } apply_mixer_settings( samplerate, 4 ); count = read_interleaved_wrapper( count, 4, interleaved_quad ); m_currentPositionSeconds += static_cast( count ) / static_cast( samplerate ); return count; } std::size_t module_impl::read_interleaved_stereo( std::int32_t samplerate, std::size_t count, float * interleaved_stereo ) { if ( !interleaved_stereo ) { throw openmpt::exception("null pointer"); } apply_mixer_settings( samplerate, 2 ); count = read_interleaved_wrapper( count, 2, interleaved_stereo ); m_currentPositionSeconds += static_cast( count ) / static_cast( samplerate ); return count; } std::size_t module_impl::read_interleaved_quad( std::int32_t samplerate, std::size_t count, float * interleaved_quad ) { if ( !interleaved_quad ) { throw openmpt::exception("null pointer"); } apply_mixer_settings( samplerate, 4 ); count = read_interleaved_wrapper( count, 4, interleaved_quad ); m_currentPositionSeconds += static_cast( count ) / static_cast( samplerate ); return count; } double module_impl::get_duration_seconds() const { std::unique_ptr subsongs_temp = has_subsongs_inited() ? std::unique_ptr() : std::make_unique( get_subsongs() ); const subsongs_type & subsongs = has_subsongs_inited() ? m_subsongs : *subsongs_temp; if ( m_current_subsong == all_subsongs ) { // Play all subsongs consecutively. double total_duration = 0.0; for ( const auto & subsong : subsongs ) { total_duration += subsong.duration; } return total_duration; } return subsongs[m_current_subsong].duration; } void module_impl::select_subsong( std::int32_t subsong ) { std::unique_ptr subsongs_temp = has_subsongs_inited() ? std::unique_ptr() : std::make_unique( get_subsongs() ); const subsongs_type & subsongs = has_subsongs_inited() ? m_subsongs : *subsongs_temp; if ( subsong != all_subsongs && ( subsong < 0 || subsong >= static_cast( subsongs.size() ) ) ) { throw openmpt::exception("invalid subsong"); } m_current_subsong = subsong; m_sndFile->m_SongFlags.set( OpenMPT::SONG_PLAYALLSONGS, subsong == all_subsongs ); if ( subsong == all_subsongs ) { subsong = 0; } m_sndFile->Order.SetSequence( static_cast( subsongs[subsong].sequence ) ); set_position_order_row( subsongs[subsong].start_order, subsongs[subsong].start_row ); m_currentPositionSeconds = 0.0; } std::int32_t module_impl::get_selected_subsong() const { return m_current_subsong; } void module_impl::set_repeat_count( std::int32_t repeat_count ) { m_sndFile->SetRepeatCount( repeat_count ); } std::int32_t module_impl::get_repeat_count() const { return m_sndFile->GetRepeatCount(); } double module_impl::get_position_seconds() const { return m_currentPositionSeconds; } double module_impl::set_position_seconds( double seconds ) { std::unique_ptr subsongs_temp = has_subsongs_inited() ? std::unique_ptr() : std::make_unique( get_subsongs() ); const subsongs_type & subsongs = has_subsongs_inited() ? m_subsongs : *subsongs_temp; const subsong_data * subsong = 0; double base_seconds = 0.0; if ( m_current_subsong == all_subsongs ) { // When playing all subsongs, find out which subsong this time would belong to. subsong = &subsongs.back(); for ( std::size_t i = 0; i < subsongs.size(); ++i ) { if ( base_seconds + subsongs[i].duration > seconds ) { subsong = &subsongs[i]; break; } base_seconds += subsong->duration; } seconds -= base_seconds; } else { subsong = &subsongs[m_current_subsong]; } m_sndFile->SetCurrentOrder( static_cast( subsong->start_order ) ); OpenMPT::GetLengthType t = m_sndFile->GetLength( m_ctl_seek_sync_samples ? OpenMPT::eAdjustSamplePositions : OpenMPT::eAdjust, OpenMPT::GetLengthTarget( seconds ).StartPos( static_cast( subsong->sequence ), static_cast( subsong->start_order ), static_cast( subsong->start_row ) ) ).back(); m_sndFile->m_PlayState.m_nNextOrder = m_sndFile->m_PlayState.m_nCurrentOrder = t.targetReached ? t.lastOrder : t.endOrder; m_sndFile->m_PlayState.m_nNextRow = t.targetReached ? t.lastRow : t.endRow; m_sndFile->m_PlayState.m_nTickCount = OpenMPT::CSoundFile::TICKS_ROW_FINISHED; m_currentPositionSeconds = base_seconds + t.duration; return m_currentPositionSeconds; } double module_impl::set_position_order_row( std::int32_t order, std::int32_t row ) { if ( order < 0 || order >= m_sndFile->Order().GetLengthTailTrimmed() ) { return m_currentPositionSeconds; } OpenMPT::PATTERNINDEX pattern = m_sndFile->Order()[order]; if ( m_sndFile->Patterns.IsValidIndex( pattern ) ) { if ( row < 0 || row >= static_cast( m_sndFile->Patterns[pattern].GetNumRows() ) ) { return m_currentPositionSeconds; } } else { row = 0; } m_sndFile->m_PlayState.m_nCurrentOrder = static_cast( order ); m_sndFile->SetCurrentOrder( static_cast( order ) ); m_sndFile->m_PlayState.m_nNextRow = static_cast( row ); m_sndFile->m_PlayState.m_nTickCount = OpenMPT::CSoundFile::TICKS_ROW_FINISHED; m_currentPositionSeconds = m_sndFile->GetLength( m_ctl_seek_sync_samples ? OpenMPT::eAdjustSamplePositions : OpenMPT::eAdjust, OpenMPT::GetLengthTarget( static_cast( order ), static_cast( row ) ) ).back().duration; return m_currentPositionSeconds; } std::vector module_impl::get_metadata_keys() const { return { "type", "type_long", "originaltype", "originaltype_long", "container", "container_long", "tracker", "artist", "title", "date", "message", "message_raw", "warnings", }; } std::string module_impl::get_message_instruments() const { std::string retval; std::string tmp; bool valid = false; for ( OpenMPT::INSTRUMENTINDEX i = 1; i <= m_sndFile->GetNumInstruments(); ++i ) { std::string instname = m_sndFile->GetInstrumentName( i ); if ( !instname.empty() ) { valid = true; } tmp += instname; tmp += "\n"; } if ( valid ) { retval = tmp; } return retval; } std::string module_impl::get_message_samples() const { std::string retval; std::string tmp; bool valid = false; for ( OpenMPT::SAMPLEINDEX i = 1; i <= m_sndFile->GetNumSamples(); ++i ) { std::string samplename = m_sndFile->GetSampleName( i ); if ( !samplename.empty() ) { valid = true; } tmp += samplename; tmp += "\n"; } if ( valid ) { retval = tmp; } return retval; } std::string module_impl::get_metadata( const std::string & key ) const { if ( key == std::string("type") ) { return mpt::transcode( mpt::common_encoding::utf8, m_sndFile->m_modFormat.type ); } else if ( key == std::string("type_long") ) { return mpt::transcode( mpt::common_encoding::utf8, m_sndFile->m_modFormat.formatName ); } else if ( key == std::string("originaltype") ) { return mpt::transcode( mpt::common_encoding::utf8, m_sndFile->m_modFormat.originalType ); } else if ( key == std::string("originaltype_long") ) { return mpt::transcode( mpt::common_encoding::utf8, m_sndFile->m_modFormat.originalFormatName ); } else if ( key == std::string("container") ) { return mpt::transcode( mpt::common_encoding::utf8, OpenMPT::CSoundFile::ModContainerTypeToString( m_sndFile->GetContainerType() ) ); } else if ( key == std::string("container_long") ) { return mpt::transcode( mpt::common_encoding::utf8, OpenMPT::CSoundFile::ModContainerTypeToTracker( m_sndFile->GetContainerType() ) ); } else if ( key == std::string("tracker") ) { return mpt::transcode( mpt::common_encoding::utf8, m_sndFile->m_modFormat.madeWithTracker ); } else if ( key == std::string("artist") ) { return mpt::transcode( mpt::common_encoding::utf8, m_sndFile->m_songArtist ); } else if ( key == std::string("title") ) { return mod_string_to_utf8( m_sndFile->GetTitle() ); } else if ( key == std::string("date") ) { if ( m_sndFile->GetFileHistory().empty() || !m_sndFile->GetFileHistory().back().HasValidDate() ) { return std::string(); } return mpt::transcode( mpt::common_encoding::utf8, m_sndFile->GetFileHistory().back().AsISO8601() ); } else if ( key == std::string("message") ) { std::string retval = m_sndFile->m_songMessage.GetFormatted( OpenMPT::SongMessage::leLF ); if ( retval.empty() ) { switch ( m_sndFile->GetMessageHeuristic() ) { case OpenMPT::ModMessageHeuristicOrder::Instruments: retval = get_message_instruments(); break; case OpenMPT::ModMessageHeuristicOrder::Samples: retval = get_message_samples(); break; case OpenMPT::ModMessageHeuristicOrder::InstrumentsSamples: if ( retval.empty() ) { retval = get_message_instruments(); } if ( retval.empty() ) { retval = get_message_samples(); } break; case OpenMPT::ModMessageHeuristicOrder::SamplesInstruments: if ( retval.empty() ) { retval = get_message_samples(); } if ( retval.empty() ) { retval = get_message_instruments(); } break; case OpenMPT::ModMessageHeuristicOrder::BothInstrumentsSamples: { std::string message_instruments = get_message_instruments(); std::string message_samples = get_message_samples(); if ( !message_instruments.empty() ) { retval += std::move( message_instruments ); } if ( !message_samples.empty() ) { retval += std::move( message_samples ); } } break; case OpenMPT::ModMessageHeuristicOrder::BothSamplesInstruments: { std::string message_instruments = get_message_instruments(); std::string message_samples = get_message_samples(); if ( !message_samples.empty() ) { retval += std::move( message_samples ); } if ( !message_instruments.empty() ) { retval += std::move( message_instruments ); } } break; } } return mod_string_to_utf8( retval ); } else if ( key == std::string("message_raw") ) { std::string retval = m_sndFile->m_songMessage.GetFormatted( OpenMPT::SongMessage::leLF ); return mod_string_to_utf8( retval ); } else if ( key == std::string("warnings") ) { std::string retval; bool first = true; for ( const auto & msg : m_loaderMessages ) { if ( !first ) { retval += "\n"; } else { first = false; } retval += msg; } return retval; } return ""; } double module_impl::get_current_estimated_bpm() const { return m_sndFile->GetCurrentBPM(); } std::int32_t module_impl::get_current_speed() const { return m_sndFile->m_PlayState.m_nMusicSpeed; } std::int32_t module_impl::get_current_tempo() const { return static_cast( m_sndFile->m_PlayState.m_nMusicTempo.GetInt() ); } std::int32_t module_impl::get_current_order() const { return m_sndFile->GetCurrentOrder(); } std::int32_t module_impl::get_current_pattern() const { std::int32_t order = m_sndFile->GetCurrentOrder(); if ( order < 0 || order >= m_sndFile->Order().GetLengthTailTrimmed() ) { return m_sndFile->GetCurrentPattern(); } std::int32_t pattern = m_sndFile->Order()[order]; if ( !m_sndFile->Patterns.IsValidIndex( static_cast( pattern ) ) ) { return -1; } return pattern; } std::int32_t module_impl::get_current_row() const { return m_sndFile->m_PlayState.m_nRow; } std::int32_t module_impl::get_current_playing_channels() const { return m_sndFile->GetMixStat(); } float module_impl::get_current_channel_vu_mono( std::int32_t channel ) const { if ( channel < 0 || channel >= m_sndFile->GetNumChannels() ) { return 0.0f; } const float left = m_sndFile->m_PlayState.Chn[channel].nLeftVU * (1.0f/128.0f); const float right = m_sndFile->m_PlayState.Chn[channel].nRightVU * (1.0f/128.0f); return std::sqrt(left*left + right*right); } float module_impl::get_current_channel_vu_left( std::int32_t channel ) const { if ( channel < 0 || channel >= m_sndFile->GetNumChannels() ) { return 0.0f; } return m_sndFile->m_PlayState.Chn[channel].dwFlags[OpenMPT::CHN_SURROUND] ? 0.0f : m_sndFile->m_PlayState.Chn[channel].nLeftVU * (1.0f/128.0f); } float module_impl::get_current_channel_vu_right( std::int32_t channel ) const { if ( channel < 0 || channel >= m_sndFile->GetNumChannels() ) { return 0.0f; } return m_sndFile->m_PlayState.Chn[channel].dwFlags[OpenMPT::CHN_SURROUND] ? 0.0f : m_sndFile->m_PlayState.Chn[channel].nRightVU * (1.0f/128.0f); } float module_impl::get_current_channel_vu_rear_left( std::int32_t channel ) const { if ( channel < 0 || channel >= m_sndFile->GetNumChannels() ) { return 0.0f; } return m_sndFile->m_PlayState.Chn[channel].dwFlags[OpenMPT::CHN_SURROUND] ? m_sndFile->m_PlayState.Chn[channel].nLeftVU * (1.0f/128.0f) : 0.0f; } float module_impl::get_current_channel_vu_rear_right( std::int32_t channel ) const { if ( channel < 0 || channel >= m_sndFile->GetNumChannels() ) { return 0.0f; } return m_sndFile->m_PlayState.Chn[channel].dwFlags[OpenMPT::CHN_SURROUND] ? m_sndFile->m_PlayState.Chn[channel].nRightVU * (1.0f/128.0f) : 0.0f; } std::int32_t module_impl::get_num_subsongs() const { std::unique_ptr subsongs_temp = has_subsongs_inited() ? std::unique_ptr() : std::make_unique( get_subsongs() ); const subsongs_type & subsongs = has_subsongs_inited() ? m_subsongs : *subsongs_temp; return static_cast( subsongs.size() ); } std::int32_t module_impl::get_num_channels() const { return m_sndFile->GetNumChannels(); } std::int32_t module_impl::get_num_orders() const { return m_sndFile->Order().GetLengthTailTrimmed(); } std::int32_t module_impl::get_num_patterns() const { return m_sndFile->Patterns.GetNumPatterns(); } std::int32_t module_impl::get_num_instruments() const { return m_sndFile->GetNumInstruments(); } std::int32_t module_impl::get_num_samples() const { return m_sndFile->GetNumSamples(); } std::vector module_impl::get_subsong_names() const { std::vector retval; std::unique_ptr subsongs_temp = has_subsongs_inited() ? std::unique_ptr() : std::make_unique( get_subsongs() ); const subsongs_type & subsongs = has_subsongs_inited() ? m_subsongs : *subsongs_temp; retval.reserve( subsongs.size() ); for ( const auto & subsong : subsongs ) { const auto & order = m_sndFile->Order( static_cast( subsong.sequence ) ); retval.push_back( mpt::transcode( mpt::common_encoding::utf8, order.GetName() ) ); if ( retval.back().empty() ) { // use first pattern name instead if ( order.IsValidPat( static_cast( subsong.start_order ) ) ) retval.back() = OpenMPT::mpt::ToCharset( OpenMPT::mpt::Charset::UTF8, m_sndFile->GetCharsetInternal(), m_sndFile->Patterns[ order[ subsong.start_order ] ].GetName() ); } } return retval; } std::vector module_impl::get_channel_names() const { std::vector retval; for ( OpenMPT::CHANNELINDEX i = 0; i < m_sndFile->GetNumChannels(); ++i ) { retval.push_back( mod_string_to_utf8( m_sndFile->ChnSettings[i].szName ) ); } return retval; } std::vector module_impl::get_order_names() const { std::vector retval; OpenMPT::ORDERINDEX num_orders = m_sndFile->Order().GetLengthTailTrimmed(); retval.reserve( num_orders ); for ( OpenMPT::ORDERINDEX i = 0; i < num_orders; ++i ) { OpenMPT::PATTERNINDEX pat = m_sndFile->Order()[i]; if ( m_sndFile->Patterns.IsValidIndex( pat ) ) { retval.push_back( mod_string_to_utf8( m_sndFile->Patterns[ m_sndFile->Order()[i] ].GetName() ) ); } else { if ( pat == m_sndFile->Order.GetIgnoreIndex() ) { retval.push_back( "+++ skip" ); } else if ( pat == m_sndFile->Order.GetInvalidPatIndex() ) { retval.push_back( "--- stop" ); } else { retval.push_back( "???" ); } } } return retval; } std::vector module_impl::get_pattern_names() const { std::vector retval; retval.reserve( m_sndFile->Patterns.GetNumPatterns() ); for ( OpenMPT::PATTERNINDEX i = 0; i < m_sndFile->Patterns.GetNumPatterns(); ++i ) { retval.push_back( mod_string_to_utf8( m_sndFile->Patterns[i].GetName() ) ); } return retval; } std::vector module_impl::get_instrument_names() const { std::vector retval; retval.reserve( m_sndFile->GetNumInstruments() ); for ( OpenMPT::INSTRUMENTINDEX i = 1; i <= m_sndFile->GetNumInstruments(); ++i ) { retval.push_back( mod_string_to_utf8( m_sndFile->GetInstrumentName( i ) ) ); } return retval; } std::vector module_impl::get_sample_names() const { std::vector retval; retval.reserve( m_sndFile->GetNumSamples() ); for ( OpenMPT::SAMPLEINDEX i = 1; i <= m_sndFile->GetNumSamples(); ++i ) { retval.push_back( mod_string_to_utf8( m_sndFile->GetSampleName( i ) ) ); } return retval; } std::int32_t module_impl::get_order_pattern( std::int32_t o ) const { if ( o < 0 || o >= m_sndFile->Order().GetLengthTailTrimmed() ) { return -1; } return m_sndFile->Order()[o]; } std::int32_t module_impl::get_pattern_num_rows( std::int32_t p ) const { if ( !mpt::is_in_range( p, std::numeric_limits::min(), std::numeric_limits::max() ) || !m_sndFile->Patterns.IsValidPat( static_cast( p ) ) ) { return 0; } return m_sndFile->Patterns[p].GetNumRows(); } std::uint8_t module_impl::get_pattern_row_channel_command( std::int32_t p, std::int32_t r, std::int32_t c, int cmd ) const { if ( !mpt::is_in_range( p, std::numeric_limits::min(), std::numeric_limits::max() ) || !m_sndFile->Patterns.IsValidPat( static_cast( p ) ) ) { return 0; } const OpenMPT::CPattern & pattern = m_sndFile->Patterns[p]; if ( r < 0 || r >= static_cast( pattern.GetNumRows() ) ) { return 0; } if ( c < 0 || c >= m_sndFile->GetNumChannels() ) { return 0; } if ( cmd < module::command_note || cmd > module::command_parameter ) { return 0; } const OpenMPT::ModCommand & cell = *pattern.GetpModCommand( static_cast( r ), static_cast( c ) ); switch ( cmd ) { case module::command_note: return cell.note; break; case module::command_instrument: return cell.instr; break; case module::command_volumeffect: return cell.volcmd; break; case module::command_effect: return cell.command; break; case module::command_volume: return cell.vol; break; case module::command_parameter: return cell.param; break; } return 0; } /* highlight chars explained: : empty/space . : empty/dot n : generic note m : special note i : generic instrument u : generic volume column effect v : generic volume column parameter e : generic effect column effect f : generic effect column parameter */ std::pair< std::string, std::string > module_impl::format_and_highlight_pattern_row_channel_command( std::int32_t p, std::int32_t r, std::int32_t c, int cmd ) const { if ( !mpt::is_in_range( p, std::numeric_limits::min(), std::numeric_limits::max() ) || !m_sndFile->Patterns.IsValidPat( static_cast( p ) ) ) { return std::make_pair( std::string(), std::string() ); } const OpenMPT::CPattern & pattern = m_sndFile->Patterns[p]; if ( r < 0 || r >= static_cast( pattern.GetNumRows() ) ) { return std::make_pair( std::string(), std::string() ); } if ( c < 0 || c >= m_sndFile->GetNumChannels() ) { return std::make_pair( std::string(), std::string() ); } if ( cmd < module::command_note || cmd > module::command_parameter ) { return std::make_pair( std::string(), std::string() ); } const OpenMPT::ModCommand & cell = *pattern.GetpModCommand( static_cast( r ), static_cast( c ) ); // clang-format off switch ( cmd ) { case module::command_note: return std::make_pair( ( cell.IsNote() || cell.IsSpecialNote() ) ? mpt::transcode( mpt::common_encoding::utf8, m_sndFile->GetNoteName( cell.note, cell.instr ) ) : std::string("...") , ( cell.IsNote() ) ? std::string("nnn") : cell.IsSpecialNote() ? std::string("mmm") : std::string("...") ); break; case module::command_instrument: return std::make_pair( cell.instr ? OpenMPT::mpt::afmt::HEX0<2>( cell.instr ) : std::string("..") , cell.instr ? std::string("ii") : std::string("..") ); break; case module::command_volumeffect: return std::make_pair( cell.IsPcNote() ? std::string(" ") : cell.volcmd != OpenMPT::VOLCMD_NONE ? std::string( 1, m_sndFile->GetModSpecifications().GetVolEffectLetter( cell.volcmd ) ) : std::string(" ") , cell.IsPcNote() ? std::string(" ") : cell.volcmd != OpenMPT::VOLCMD_NONE ? std::string("u") : std::string(" ") ); break; case module::command_volume: return std::make_pair( cell.IsPcNote() ? OpenMPT::mpt::afmt::HEX0<2>( cell.GetValueVolCol() & 0xff ) : cell.volcmd != OpenMPT::VOLCMD_NONE ? OpenMPT::mpt::afmt::HEX0<2>( cell.vol ) : std::string("..") , cell.IsPcNote() ? std::string("vv") : cell.volcmd != OpenMPT::VOLCMD_NONE ? std::string("vv") : std::string("..") ); break; case module::command_effect: return std::make_pair( cell.IsPcNote() ? OpenMPT::mpt::afmt::HEX0<1>( ( cell.GetValueEffectCol() & 0x0f00 ) > 16 ) : cell.command != OpenMPT::CMD_NONE ? std::string( 1, m_sndFile->GetModSpecifications().GetEffectLetter( cell.command ) ) : std::string(".") , cell.IsPcNote() ? std::string("e") : cell.command != OpenMPT::CMD_NONE ? std::string("e") : std::string(".") ); break; case module::command_parameter: return std::make_pair( cell.IsPcNote() ? OpenMPT::mpt::afmt::HEX0<2>( cell.GetValueEffectCol() & 0x00ff ) : cell.command != OpenMPT::CMD_NONE ? OpenMPT::mpt::afmt::HEX0<2>( cell.param ) : std::string("..") , cell.IsPcNote() ? std::string("ff") : cell.command != OpenMPT::CMD_NONE ? std::string("ff") : std::string("..") ); break; } // clang-format on return std::make_pair( std::string(), std::string() ); } std::string module_impl::format_pattern_row_channel_command( std::int32_t p, std::int32_t r, std::int32_t c, int cmd ) const { return format_and_highlight_pattern_row_channel_command( p, r, c, cmd ).first; } std::string module_impl::highlight_pattern_row_channel_command( std::int32_t p, std::int32_t r, std::int32_t c, int cmd ) const { return format_and_highlight_pattern_row_channel_command( p, r, c, cmd ).second; } std::pair< std::string, std::string > module_impl::format_and_highlight_pattern_row_channel( std::int32_t p, std::int32_t r, std::int32_t c, std::size_t width, bool pad ) const { std::string text = pad ? std::string( width, ' ' ) : std::string(); std::string high = pad ? std::string( width, ' ' ) : std::string(); if ( !mpt::is_in_range( p, std::numeric_limits::min(), std::numeric_limits::max() ) || !m_sndFile->Patterns.IsValidPat( static_cast( p ) ) ) { return std::make_pair( text, high ); } const OpenMPT::CPattern & pattern = m_sndFile->Patterns[p]; if ( r < 0 || r >= static_cast( pattern.GetNumRows() ) ) { return std::make_pair( text, high ); } if ( c < 0 || c >= m_sndFile->GetNumChannels() ) { return std::make_pair( text, high ); } // 0000000001111 // 1234567890123 // "NNN IIvVV EFF" const OpenMPT::ModCommand & cell = *pattern.GetpModCommand( static_cast( r ), static_cast( c ) ); text.clear(); high.clear(); // clang-format off text += ( cell.IsNote() || cell.IsSpecialNote() ) ? mpt::transcode( mpt::common_encoding::utf8, m_sndFile->GetNoteName( cell.note, cell.instr ) ) : std::string("..."); high += ( cell.IsNote() ) ? std::string("nnn") : cell.IsSpecialNote() ? std::string("mmm") : std::string("..."); if ( ( width == 0 ) || ( width >= 6 ) ) { text += std::string(" "); high += std::string(" "); text += cell.instr ? OpenMPT::mpt::afmt::HEX0<2>( cell.instr ) : std::string(".."); high += cell.instr ? std::string("ii") : std::string(".."); } if ( ( width == 0 ) || ( width >= 9 ) ) { text += cell.IsPcNote() ? std::string(" ") + OpenMPT::mpt::afmt::HEX0<2>( cell.GetValueVolCol() & 0xff ) : cell.volcmd != OpenMPT::VOLCMD_NONE ? std::string( 1, m_sndFile->GetModSpecifications().GetVolEffectLetter( cell.volcmd ) ) + OpenMPT::mpt::afmt::HEX0<2>( cell.vol ) : std::string(" .."); high += cell.IsPcNote() ? std::string(" vv") : cell.volcmd != OpenMPT::VOLCMD_NONE ? std::string("uvv") : std::string(" .."); } if ( ( width == 0 ) || ( width >= 13 ) ) { text += std::string(" "); high += std::string(" "); text += cell.IsPcNote() ? OpenMPT::mpt::afmt::HEX0<3>( cell.GetValueEffectCol() & 0x0fff ) : cell.command != OpenMPT::CMD_NONE ? std::string( 1, m_sndFile->GetModSpecifications().GetEffectLetter( cell.command ) ) + OpenMPT::mpt::afmt::HEX0<2>( cell.param ) : std::string("..."); high += cell.IsPcNote() ? std::string("eff") : cell.command != OpenMPT::CMD_NONE ? std::string("eff") : std::string("..."); } if ( ( width != 0 ) && ( text.length() > width ) ) { text = text.substr( 0, width ); } else if ( ( width != 0 ) && pad ) { text += std::string( width - text.length(), ' ' ); } if ( ( width != 0 ) && ( high.length() > width ) ) { high = high.substr( 0, width ); } else if ( ( width != 0 ) && pad ) { high += std::string( width - high.length(), ' ' ); } // clang-format on return std::make_pair( text, high ); } std::string module_impl::format_pattern_row_channel( std::int32_t p, std::int32_t r, std::int32_t c, std::size_t width, bool pad ) const { return format_and_highlight_pattern_row_channel( p, r, c, width, pad ).first; } std::string module_impl::highlight_pattern_row_channel( std::int32_t p, std::int32_t r, std::int32_t c, std::size_t width, bool pad ) const { return format_and_highlight_pattern_row_channel( p, r, c, width, pad ).second; } std::pair module_impl::get_ctl_infos() const { static constexpr ctl_info ctl_infos[] = { { "load.skip_samples", ctl_type::boolean }, { "load.skip_patterns", ctl_type::boolean }, { "load.skip_plugins", ctl_type::boolean }, { "load.skip_subsongs_init", ctl_type::boolean }, { "seek.sync_samples", ctl_type::boolean }, { "subsong", ctl_type::integer }, { "play.tempo_factor", ctl_type::floatingpoint }, { "play.pitch_factor", ctl_type::floatingpoint }, { "play.at_end", ctl_type::text }, { "render.resampler.emulate_amiga", ctl_type::boolean }, { "render.resampler.emulate_amiga_type", ctl_type::text }, { "render.opl.volume_factor", ctl_type::floatingpoint }, { "dither", ctl_type::integer } }; return std::make_pair(std::begin(ctl_infos), std::end(ctl_infos)); } std::vector module_impl::get_ctls() const { std::vector result; auto ctl_infos = get_ctl_infos(); result.reserve(std::distance(ctl_infos.first, ctl_infos.second)); for ( std::ptrdiff_t i = 0; i < std::distance(ctl_infos.first, ctl_infos.second); ++i ) { result.push_back(ctl_infos.first[i].name); } return result; } std::string module_impl::ctl_get( std::string ctl, bool throw_if_unknown ) const { if ( !ctl.empty() ) { // cppcheck false-positive // cppcheck-suppress containerOutOfBounds char rightmost = ctl.back(); if ( rightmost == '!' || rightmost == '?' ) { if ( rightmost == '!' ) { throw_if_unknown = true; } else if ( rightmost == '?' ) { throw_if_unknown = false; } ctl = ctl.substr( 0, ctl.length() - 1 ); } } auto found_ctl = std::find_if(get_ctl_infos().first, get_ctl_infos().second, [&](const ctl_info & info) -> bool { return info.name == ctl; }); if ( found_ctl == get_ctl_infos().second ) { if ( ctl == "" ) { throw openmpt::exception("empty ctl"); } else if ( throw_if_unknown ) { throw openmpt::exception("unknown ctl: " + ctl); } else { return std::string(); } } std::string result; switch ( found_ctl->type ) { case ctl_type::boolean: return mpt::format_value_default( ctl_get_boolean( ctl, throw_if_unknown ) ); break; case ctl_type::integer: return mpt::format_value_default( ctl_get_integer( ctl, throw_if_unknown ) ); break; case ctl_type::floatingpoint: return mpt::format_value_default( ctl_get_floatingpoint( ctl, throw_if_unknown ) ); break; case ctl_type::text: return ctl_get_text( ctl, throw_if_unknown ); break; } return result; } bool module_impl::ctl_get_boolean( std::string_view ctl, bool throw_if_unknown ) const { if ( !ctl.empty() ) { // cppcheck false-positive // cppcheck-suppress containerOutOfBounds char rightmost = ctl.back(); if ( rightmost == '!' || rightmost == '?' ) { if ( rightmost == '!' ) { throw_if_unknown = true; } else if ( rightmost == '?' ) { throw_if_unknown = false; } ctl = ctl.substr( 0, ctl.length() - 1 ); } } auto found_ctl = std::find_if(get_ctl_infos().first, get_ctl_infos().second, [&](const ctl_info & info) -> bool { return info.name == ctl; }); if ( found_ctl == get_ctl_infos().second ) { if ( ctl == "" ) { throw openmpt::exception("empty ctl"); } else if ( throw_if_unknown ) { throw openmpt::exception("unknown ctl: " + std::string(ctl)); } else { return false; } } if ( found_ctl->type != ctl_type::boolean ) { throw openmpt::exception("wrong ctl value type"); } if ( ctl == "" ) { throw openmpt::exception("empty ctl"); } else if ( ctl == "load.skip_samples" || ctl == "load_skip_samples" ) { return m_ctl_load_skip_samples; } else if ( ctl == "load.skip_patterns" || ctl == "load_skip_patterns" ) { return m_ctl_load_skip_patterns; } else if ( ctl == "load.skip_plugins" ) { return m_ctl_load_skip_plugins; } else if ( ctl == "load.skip_subsongs_init" ) { return m_ctl_load_skip_subsongs_init; } else if ( ctl == "seek.sync_samples" ) { return m_ctl_seek_sync_samples; } else if ( ctl == "render.resampler.emulate_amiga" ) { return ( m_sndFile->m_Resampler.m_Settings.emulateAmiga != OpenMPT::Resampling::AmigaFilter::Off ); } else { MPT_ASSERT_NOTREACHED(); return false; } } std::int64_t module_impl::ctl_get_integer( std::string_view ctl, bool throw_if_unknown ) const { if ( !ctl.empty() ) { // cppcheck false-positive // cppcheck-suppress containerOutOfBounds char rightmost = ctl.back(); if ( rightmost == '!' || rightmost == '?' ) { if ( rightmost == '!' ) { throw_if_unknown = true; } else if ( rightmost == '?' ) { throw_if_unknown = false; } ctl = ctl.substr( 0, ctl.length() - 1 ); } } auto found_ctl = std::find_if(get_ctl_infos().first, get_ctl_infos().second, [&](const ctl_info & info) -> bool { return info.name == ctl; }); if ( found_ctl == get_ctl_infos().second ) { if ( ctl == "" ) { throw openmpt::exception("empty ctl"); } else if ( throw_if_unknown ) { throw openmpt::exception("unknown ctl: " + std::string(ctl)); } else { return 0; } } if ( found_ctl->type != ctl_type::integer ) { throw openmpt::exception("wrong ctl value type"); } if ( ctl == "" ) { throw openmpt::exception("empty ctl"); } else if ( ctl == "subsong" ) { return get_selected_subsong(); } else if ( ctl == "dither" ) { return static_cast( m_Dithers->GetMode() ); } else { MPT_ASSERT_NOTREACHED(); return 0; } } double module_impl::ctl_get_floatingpoint( std::string_view ctl, bool throw_if_unknown ) const { if ( !ctl.empty() ) { // cppcheck false-positive // cppcheck-suppress containerOutOfBounds char rightmost = ctl.back(); if ( rightmost == '!' || rightmost == '?' ) { if ( rightmost == '!' ) { throw_if_unknown = true; } else if ( rightmost == '?' ) { throw_if_unknown = false; } ctl = ctl.substr( 0, ctl.length() - 1 ); } } auto found_ctl = std::find_if(get_ctl_infos().first, get_ctl_infos().second, [&](const ctl_info & info) -> bool { return info.name == ctl; }); if ( found_ctl == get_ctl_infos().second ) { if ( ctl == "" ) { throw openmpt::exception("empty ctl"); } else if ( throw_if_unknown ) { throw openmpt::exception("unknown ctl: " + std::string(ctl)); } else { return 0.0; } } if ( found_ctl->type != ctl_type::floatingpoint ) { throw openmpt::exception("wrong ctl value type"); } if ( ctl == "" ) { throw openmpt::exception("empty ctl"); } else if ( ctl == "play.tempo_factor" ) { if ( !is_loaded() ) { return 1.0; } return 65536.0 / m_sndFile->m_nTempoFactor; } else if ( ctl == "play.pitch_factor" ) { if ( !is_loaded() ) { return 1.0; } return m_sndFile->m_nFreqFactor / 65536.0; } else if ( ctl == "render.opl.volume_factor" ) { return static_cast( m_sndFile->m_OPLVolumeFactor ) / static_cast( OpenMPT::CSoundFile::m_OPLVolumeFactorScale ); } else { MPT_ASSERT_NOTREACHED(); return 0.0; } } std::string module_impl::ctl_get_text( std::string_view ctl, bool throw_if_unknown ) const { if ( !ctl.empty() ) { // cppcheck false-positive // cppcheck-suppress containerOutOfBounds char rightmost = ctl.back(); if ( rightmost == '!' || rightmost == '?' ) { if ( rightmost == '!' ) { throw_if_unknown = true; } else if ( rightmost == '?' ) { throw_if_unknown = false; } ctl = ctl.substr( 0, ctl.length() - 1 ); } } auto found_ctl = std::find_if(get_ctl_infos().first, get_ctl_infos().second, [&](const ctl_info & info) -> bool { return info.name == ctl; }); if ( found_ctl == get_ctl_infos().second ) { if ( ctl == "" ) { throw openmpt::exception("empty ctl"); } else if ( throw_if_unknown ) { throw openmpt::exception("unknown ctl: " + std::string(ctl)); } else { return std::string(); } } if ( ctl == "" ) { throw openmpt::exception("empty ctl"); } else if ( ctl == "play.at_end" ) { switch ( m_ctl_play_at_end ) { case song_end_action::fadeout_song: return "fadeout"; case song_end_action::continue_song: return "continue"; case song_end_action::stop_song: return "stop"; default: return std::string(); } } else if ( ctl == "render.resampler.emulate_amiga_type" ) { switch ( m_ctl_render_resampler_emulate_amiga_type ) { case amiga_filter_type::a500: return "a500"; case amiga_filter_type::a1200: return "a1200"; case amiga_filter_type::unfiltered: return "unfiltered"; case amiga_filter_type::auto_filter: return "auto"; default: return std::string(); } } else { MPT_ASSERT_NOTREACHED(); return std::string(); } } void module_impl::ctl_set( std::string ctl, const std::string & value, bool throw_if_unknown ) { if ( !ctl.empty() ) { // cppcheck false-positive // cppcheck-suppress containerOutOfBounds char rightmost = ctl.back(); if ( rightmost == '!' || rightmost == '?' ) { if ( rightmost == '!' ) { throw_if_unknown = true; } else if ( rightmost == '?' ) { throw_if_unknown = false; } ctl = ctl.substr( 0, ctl.length() - 1 ); } } auto found_ctl = std::find_if(get_ctl_infos().first, get_ctl_infos().second, [&](const ctl_info & info) -> bool { return info.name == ctl; }); if ( found_ctl == get_ctl_infos().second ) { if ( ctl == "" ) { throw openmpt::exception("empty ctl: := " + value); } else if ( throw_if_unknown ) { throw openmpt::exception("unknown ctl: " + ctl + " := " + value); } else { return; } } switch ( found_ctl->type ) { case ctl_type::boolean: ctl_set_boolean( ctl, mpt::ConvertStringTo( value ), throw_if_unknown ); break; case ctl_type::integer: ctl_set_integer( ctl, mpt::ConvertStringTo( value ), throw_if_unknown ); break; case ctl_type::floatingpoint: ctl_set_floatingpoint( ctl, mpt::ConvertStringTo( value ), throw_if_unknown ); break; case ctl_type::text: ctl_set_text( ctl, value, throw_if_unknown ); break; } } void module_impl::ctl_set_boolean( std::string_view ctl, bool value, bool throw_if_unknown ) { if ( !ctl.empty() ) { // cppcheck false-positive // cppcheck-suppress containerOutOfBounds char rightmost = ctl.back(); if ( rightmost == '!' || rightmost == '?' ) { if ( rightmost == '!' ) { throw_if_unknown = true; } else if ( rightmost == '?' ) { throw_if_unknown = false; } ctl = ctl.substr( 0, ctl.length() - 1 ); } } auto found_ctl = std::find_if(get_ctl_infos().first, get_ctl_infos().second, [&](const ctl_info & info) -> bool { return info.name == ctl; }); if ( found_ctl == get_ctl_infos().second ) { if ( ctl == "" ) { throw openmpt::exception("empty ctl: := " + mpt::format_value_default( value ) ); } else if ( throw_if_unknown ) { throw openmpt::exception("unknown ctl: " + std::string(ctl) + " := " + mpt::format_value_default(value)); } else { return; } } if ( ctl == "" ) { throw openmpt::exception("empty ctl: := " + mpt::format_value_default( value ) ); } else if ( ctl == "load.skip_samples" || ctl == "load_skip_samples" ) { m_ctl_load_skip_samples = value; } else if ( ctl == "load.skip_patterns" || ctl == "load_skip_patterns" ) { m_ctl_load_skip_patterns = value; } else if ( ctl == "load.skip_plugins" ) { m_ctl_load_skip_plugins = value; } else if ( ctl == "load.skip_subsongs_init" ) { m_ctl_load_skip_subsongs_init = value; } else if ( ctl == "seek.sync_samples" ) { m_ctl_seek_sync_samples = value; } else if ( ctl == "render.resampler.emulate_amiga" ) { OpenMPT::CResamplerSettings newsettings = m_sndFile->m_Resampler.m_Settings; const bool enabled = value; if ( enabled ) newsettings.emulateAmiga = translate_amiga_filter_type( m_ctl_render_resampler_emulate_amiga_type ); else newsettings.emulateAmiga = OpenMPT::Resampling::AmigaFilter::Off; if ( newsettings != m_sndFile->m_Resampler.m_Settings ) { m_sndFile->SetResamplerSettings( newsettings ); } } else { MPT_ASSERT_NOTREACHED(); } } void module_impl::ctl_set_integer( std::string_view ctl, std::int64_t value, bool throw_if_unknown ) { if ( !ctl.empty() ) { // cppcheck false-positive // cppcheck-suppress containerOutOfBounds char rightmost = ctl.back(); if ( rightmost == '!' || rightmost == '?' ) { if ( rightmost == '!' ) { throw_if_unknown = true; } else if ( rightmost == '?' ) { throw_if_unknown = false; } ctl = ctl.substr( 0, ctl.length() - 1 ); } } auto found_ctl = std::find_if(get_ctl_infos().first, get_ctl_infos().second, [&](const ctl_info & info) -> bool { return info.name == ctl; }); if ( found_ctl == get_ctl_infos().second ) { if ( ctl == "" ) { throw openmpt::exception("empty ctl: := " + mpt::format_value_default( value ) ); } else if ( throw_if_unknown ) { throw openmpt::exception("unknown ctl: " + std::string(ctl) + " := " + mpt::format_value_default(value)); } else { return; } } if ( ctl == "" ) { throw openmpt::exception("empty ctl: := " + mpt::format_value_default( value ) ); } else if ( ctl == "subsong" ) { select_subsong( mpt::saturate_cast( value ) ); } else if ( ctl == "dither" ) { std::size_t dither = mpt::saturate_cast( value ); if ( dither >= OpenMPT::DithersOpenMPT::GetNumDithers() ) { dither = OpenMPT::DithersOpenMPT::GetDefaultDither(); } m_Dithers->SetMode( dither ); } else { MPT_ASSERT_NOTREACHED(); } } void module_impl::ctl_set_floatingpoint( std::string_view ctl, double value, bool throw_if_unknown ) { if ( !ctl.empty() ) { // cppcheck false-positive // cppcheck-suppress containerOutOfBounds char rightmost = ctl.back(); if ( rightmost == '!' || rightmost == '?' ) { if ( rightmost == '!' ) { throw_if_unknown = true; } else if ( rightmost == '?' ) { throw_if_unknown = false; } ctl = ctl.substr( 0, ctl.length() - 1 ); } } auto found_ctl = std::find_if(get_ctl_infos().first, get_ctl_infos().second, [&](const ctl_info & info) -> bool { return info.name == ctl; }); if ( found_ctl == get_ctl_infos().second ) { if ( ctl == "" ) { throw openmpt::exception("empty ctl: := " + mpt::format_value_default( value ) ); } else if ( throw_if_unknown ) { throw openmpt::exception("unknown ctl: " + std::string(ctl) + " := " + mpt::format_value_default(value)); } else { return; } } if ( ctl == "" ) { throw openmpt::exception("empty ctl: := " + mpt::format_value_default( value ) ); } else if ( ctl == "play.tempo_factor" ) { if ( !is_loaded() ) { return; } double factor = value; if ( factor <= 0.0 || factor > 4.0 ) { throw openmpt::exception("invalid tempo factor"); } m_sndFile->m_nTempoFactor = mpt::saturate_round( 65536.0 / factor ); m_sndFile->RecalculateSamplesPerTick(); } else if ( ctl == "play.pitch_factor" ) { if ( !is_loaded() ) { return; } double factor = value; if ( factor <= 0.0 || factor > 4.0 ) { throw openmpt::exception("invalid pitch factor"); } m_sndFile->m_nFreqFactor = mpt::saturate_round( 65536.0 * factor ); m_sndFile->RecalculateSamplesPerTick(); } else if ( ctl == "render.opl.volume_factor" ) { m_sndFile->m_OPLVolumeFactor = mpt::saturate_round( value * static_cast( OpenMPT::CSoundFile::m_OPLVolumeFactorScale ) ); } else { MPT_ASSERT_NOTREACHED(); } } void module_impl::ctl_set_text( std::string_view ctl, std::string_view value, bool throw_if_unknown ) { if ( !ctl.empty() ) { // cppcheck false-positive // cppcheck-suppress containerOutOfBounds char rightmost = ctl.back(); if ( rightmost == '!' || rightmost == '?' ) { if ( rightmost == '!' ) { throw_if_unknown = true; } else if ( rightmost == '?' ) { throw_if_unknown = false; } ctl = ctl.substr( 0, ctl.length() - 1 ); } } auto found_ctl = std::find_if(get_ctl_infos().first, get_ctl_infos().second, [&](const ctl_info & info) -> bool { return info.name == ctl; }); if ( found_ctl == get_ctl_infos().second ) { if ( ctl == "" ) { throw openmpt::exception("empty ctl: := " + std::string( value ) ); } else if ( throw_if_unknown ) { throw openmpt::exception("unknown ctl: " + std::string(ctl) + " := " + std::string(value)); } else { return; } } if ( ctl == "" ) { throw openmpt::exception("empty ctl: := " + std::string( value ) ); } else if ( ctl == "play.at_end" ) { if ( value == "fadeout" ) { m_ctl_play_at_end = song_end_action::fadeout_song; } else if(value == "continue") { m_ctl_play_at_end = song_end_action::continue_song; } else if(value == "stop") { m_ctl_play_at_end = song_end_action::stop_song; } else { throw openmpt::exception("unknown song end action:" + std::string(value)); } } else if ( ctl == "render.resampler.emulate_amiga_type" ) { if ( value == "a500" ) { m_ctl_render_resampler_emulate_amiga_type = amiga_filter_type::a500; } else if ( value == "a1200" ) { m_ctl_render_resampler_emulate_amiga_type = amiga_filter_type::a1200; } else if ( value == "unfiltered" ) { m_ctl_render_resampler_emulate_amiga_type = amiga_filter_type::unfiltered; } else if ( value == "auto" ) { m_ctl_render_resampler_emulate_amiga_type = amiga_filter_type::auto_filter; } else { throw openmpt::exception( "invalid amiga filter type" ); } if ( m_sndFile->m_Resampler.m_Settings.emulateAmiga != OpenMPT::Resampling::AmigaFilter::Off ) { OpenMPT::CResamplerSettings newsettings = m_sndFile->m_Resampler.m_Settings; newsettings.emulateAmiga = translate_amiga_filter_type( m_ctl_render_resampler_emulate_amiga_type ); if ( newsettings != m_sndFile->m_Resampler.m_Settings ) { m_sndFile->SetResamplerSettings( newsettings ); } } } else { MPT_ASSERT_NOTREACHED(); } } } // namespace openmpt libopenmpt-0.6.1+release.autotools/libopenmpt/libopenmpt_ext_impl.hpp0000644000175000017500000000741614151240412023212 00000000000000/* * libopenmpt_ext_impl.hpp * ----------------------- * Purpose: libopenmpt extensions - implementation header * Notes : * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #ifndef LIBOPENMPT_EXT_IMPL_HPP #define LIBOPENMPT_EXT_IMPL_HPP #include "libopenmpt_internal.h" #include "libopenmpt_impl.hpp" #include "libopenmpt_ext.hpp" namespace openmpt { class module_ext_impl : public module_impl , public ext::pattern_vis , public ext::interactive , public ext::interactive2 /* add stuff here */ { public: module_ext_impl( callback_stream_wrapper stream, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); module_ext_impl( std::istream & stream, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); module_ext_impl( const std::vector & data, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); module_ext_impl( const std::vector & data, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); module_ext_impl( const std::vector & data, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); module_ext_impl( const std::byte * data, std::size_t size, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); module_ext_impl( const std::uint8_t * data, std::size_t size, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); module_ext_impl( const char * data, std::size_t size, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); module_ext_impl( const void * data, std::size_t size, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); private: /* add stuff here */ private: void ctor(); public: ~module_ext_impl(); public: void * get_interface( const std::string & interface_id ); // pattern_vis effect_type get_pattern_row_channel_volume_effect_type( std::int32_t pattern, std::int32_t row, std::int32_t channel ) const override; effect_type get_pattern_row_channel_effect_type( std::int32_t pattern, std::int32_t row, std::int32_t channel ) const override; // interactive void set_current_speed( std::int32_t speed ) override; void set_current_tempo( std::int32_t tempo ) override; void set_tempo_factor( double factor ) override; double get_tempo_factor( ) const override; void set_pitch_factor( double factor ) override; double get_pitch_factor( ) const override; void set_global_volume( double volume ) override; double get_global_volume( ) const override; void set_channel_volume( std::int32_t channel, double volume ) override; double get_channel_volume( std::int32_t channel ) const override; void set_channel_mute_status( std::int32_t channel, bool mute ) override; bool get_channel_mute_status( std::int32_t channel ) const override; void set_instrument_mute_status( std::int32_t instrument, bool mute ) override; bool get_instrument_mute_status( std::int32_t instrument ) const override; std::int32_t play_note( std::int32_t instrument, std::int32_t note, double volume, double panning ) override; void stop_note( std::int32_t channel ) override; void note_off( std::int32_t channel ) override; void note_fade( std::int32_t channel ) override; void set_channel_panning( std::int32_t channel, double panning ) override; double get_channel_panning( std::int32_t channel ) override; void set_note_finetune( std::int32_t channel, double finetune ) override; double get_note_finetune( std::int32_t channel ) override; /* add stuff here */ }; // class module_ext_impl } // namespace openmpt #endif // LIBOPENMPT_EXT_IMPL_HPP libopenmpt-0.6.1+release.autotools/libopenmpt/libopenmpt_impl.hpp0000644000175000017500000003101214056206101022320 00000000000000/* * libopenmpt_impl.hpp * ------------------- * Purpose: libopenmpt private interface * Notes : This is not a public header. Do NOT ship in distributions dev packages. * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #ifndef LIBOPENMPT_IMPL_HPP #define LIBOPENMPT_IMPL_HPP #include "libopenmpt_internal.h" #include "libopenmpt.hpp" #include #include #include #if defined(_MSC_VER) #pragma warning(push) #pragma warning(disable:4512) // assignment operator could not be generated #endif // forward declarations namespace mpt { inline namespace mpt_libopenmpt { namespace IO { class FileCursorTraitsFileData; template class FileCursorFilenameTraits; template class FileCursor; } // namespace IO } // namespace mpt_libopenmpt } // namespace mpt namespace OpenMPT { namespace detail { template using FileCursor = mpt::IO::FileCursor; } // namespace detail namespace mpt { class PathString; } // namespace mpt using FileCursor = detail::FileCursor>; class CSoundFile; struct DithersWrapperOpenMPT; } // namespace OpenMPT namespace openmpt { namespace version { std::uint32_t get_library_version(); std::uint32_t get_core_version(); std::string get_string( const std::string & key ); } // namespace version class log_interface { protected: log_interface(); public: virtual ~log_interface(); virtual void log( const std::string & message ) const = 0; }; // class log_interface class std_ostream_log : public log_interface { private: std::ostream & destination; public: std_ostream_log( std::ostream & dst ); virtual ~std_ostream_log(); void log( const std::string & message ) const override; }; // class CSoundFileLog_std_ostream class log_forwarder; struct callback_stream_wrapper { void * stream; std::size_t (*read)( void * stream, void * dst, std::size_t bytes ); int (*seek)( void * stream, std::int64_t offset, int whence ); std::int64_t (*tell)( void * stream ); }; // struct callback_stream_wrapper class module_impl { public: enum class amiga_filter_type { a500, a1200, unfiltered, auto_filter, }; protected: struct subsong_data { double duration; std::int32_t start_row; std::int32_t start_order; std::int32_t sequence; subsong_data( double duration, std::int32_t start_row, std::int32_t start_order, std::int32_t sequence ); }; // struct subsong_data typedef std::vector subsongs_type; enum class song_end_action { fadeout_song, continue_song, stop_song, }; static constexpr std::int32_t all_subsongs = -1; enum class ctl_type { boolean, integer, floatingpoint, text, }; struct ctl_info { const char * name; ctl_type type; }; std::unique_ptr m_Log; std::unique_ptr m_LogForwarder; std::int32_t m_current_subsong; double m_currentPositionSeconds; std::unique_ptr m_sndFile; bool m_loaded; bool m_mixer_initialized; std::unique_ptr m_Dithers; subsongs_type m_subsongs; float m_Gain; song_end_action m_ctl_play_at_end; amiga_filter_type m_ctl_render_resampler_emulate_amiga_type = amiga_filter_type::auto_filter; bool m_ctl_load_skip_samples; bool m_ctl_load_skip_patterns; bool m_ctl_load_skip_plugins; bool m_ctl_load_skip_subsongs_init; bool m_ctl_seek_sync_samples; std::vector m_loaderMessages; public: void PushToCSoundFileLog( const std::string & text ) const; void PushToCSoundFileLog( int loglevel, const std::string & text ) const; protected: std::string mod_string_to_utf8( const std::string & encoded ) const; void apply_mixer_settings( std::int32_t samplerate, int channels ); void apply_libopenmpt_defaults(); subsongs_type get_subsongs() const; void init_subsongs( subsongs_type & subsongs ) const; bool has_subsongs_inited() const; void ctor( const std::map< std::string, std::string > & ctls ); void load( const OpenMPT::FileCursor & file, const std::map< std::string, std::string > & ctls ); bool is_loaded() const; std::size_t read_wrapper( std::size_t count, std::int16_t * left, std::int16_t * right, std::int16_t * rear_left, std::int16_t * rear_right ); std::size_t read_wrapper( std::size_t count, float * left, float * right, float * rear_left, float * rear_right ); std::size_t read_interleaved_wrapper( std::size_t count, std::size_t channels, std::int16_t * interleaved ); std::size_t read_interleaved_wrapper( std::size_t count, std::size_t channels, float * interleaved ); std::string get_message_instruments() const; std::string get_message_samples() const; std::pair< std::string, std::string > format_and_highlight_pattern_row_channel_command( std::int32_t p, std::int32_t r, std::int32_t c, int command ) const; std::pair< std::string, std::string > format_and_highlight_pattern_row_channel( std::int32_t p, std::int32_t r, std::int32_t c, std::size_t width, bool pad ) const; static double could_open_probability( const OpenMPT::FileCursor & file, double effort, std::unique_ptr log ); public: static std::vector get_supported_extensions(); static bool is_extension_supported( std::string_view extension ); static double could_open_probability( callback_stream_wrapper stream, double effort, std::unique_ptr log ); static double could_open_probability( std::istream & stream, double effort, std::unique_ptr log ); static std::size_t probe_file_header_get_recommended_size(); static int probe_file_header( std::uint64_t flags, const std::byte * data, std::size_t size, std::uint64_t filesize ); static int probe_file_header( std::uint64_t flags, const std::uint8_t * data, std::size_t size, std::uint64_t filesize ); static int probe_file_header( std::uint64_t flags, const void * data, std::size_t size, std::uint64_t filesize ); static int probe_file_header( std::uint64_t flags, const std::byte * data, std::size_t size ); static int probe_file_header( std::uint64_t flags, const std::uint8_t * data, std::size_t size ); static int probe_file_header( std::uint64_t flags, const void * data, std::size_t size ); static int probe_file_header( std::uint64_t flags, std::istream & stream ); static int probe_file_header( std::uint64_t flags, callback_stream_wrapper stream ); module_impl( callback_stream_wrapper stream, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); module_impl( std::istream & stream, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); module_impl( const std::vector & data, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); module_impl( const std::vector & data, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); module_impl( const std::vector & data, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); module_impl( const std::byte * data, std::size_t size, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); module_impl( const std::uint8_t * data, std::size_t size, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); module_impl( const char * data, std::size_t size, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); module_impl( const void * data, std::size_t size, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); ~module_impl(); public: void select_subsong( std::int32_t subsong ); std::int32_t get_selected_subsong() const; void set_repeat_count( std::int32_t repeat_count ); std::int32_t get_repeat_count() const; double get_duration_seconds() const; double set_position_seconds( double seconds ); double get_position_seconds() const; double set_position_order_row( std::int32_t order, std::int32_t row ); std::int32_t get_render_param( int param ) const; void set_render_param( int param, std::int32_t value ); std::size_t read( std::int32_t samplerate, std::size_t count, std::int16_t * mono ); std::size_t read( std::int32_t samplerate, std::size_t count, std::int16_t * left, std::int16_t * right ); std::size_t read( std::int32_t samplerate, std::size_t count, std::int16_t * left, std::int16_t * right, std::int16_t * rear_left, std::int16_t * rear_right ); std::size_t read( std::int32_t samplerate, std::size_t count, float * mono ); std::size_t read( std::int32_t samplerate, std::size_t count, float * left, float * right ); std::size_t read( std::int32_t samplerate, std::size_t count, float * left, float * right, float * rear_left, float * rear_right ); std::size_t read_interleaved_stereo( std::int32_t samplerate, std::size_t count, std::int16_t * interleaved_stereo ); std::size_t read_interleaved_quad( std::int32_t samplerate, std::size_t count, std::int16_t * interleaved_quad ); std::size_t read_interleaved_stereo( std::int32_t samplerate, std::size_t count, float * interleaved_stereo ); std::size_t read_interleaved_quad( std::int32_t samplerate, std::size_t count, float * interleaved_quad ); std::vector get_metadata_keys() const; std::string get_metadata( const std::string & key ) const; double get_current_estimated_bpm() const; std::int32_t get_current_speed() const; std::int32_t get_current_tempo() const; std::int32_t get_current_order() const; std::int32_t get_current_pattern() const; std::int32_t get_current_row() const; std::int32_t get_current_playing_channels() const; float get_current_channel_vu_mono( std::int32_t channel ) const; float get_current_channel_vu_left( std::int32_t channel ) const; float get_current_channel_vu_right( std::int32_t channel ) const; float get_current_channel_vu_rear_left( std::int32_t channel ) const; float get_current_channel_vu_rear_right( std::int32_t channel ) const; std::int32_t get_num_subsongs() const; std::int32_t get_num_channels() const; std::int32_t get_num_orders() const; std::int32_t get_num_patterns() const; std::int32_t get_num_instruments() const; std::int32_t get_num_samples() const; std::vector get_subsong_names() const; std::vector get_channel_names() const; std::vector get_order_names() const; std::vector get_pattern_names() const; std::vector get_instrument_names() const; std::vector get_sample_names() const; std::int32_t get_order_pattern( std::int32_t o ) const; std::int32_t get_pattern_num_rows( std::int32_t p ) const; std::uint8_t get_pattern_row_channel_command( std::int32_t p, std::int32_t r, std::int32_t c, int cmd ) const; std::string format_pattern_row_channel_command( std::int32_t p, std::int32_t r, std::int32_t c, int cmd ) const; std::string highlight_pattern_row_channel_command( std::int32_t p, std::int32_t r, std::int32_t c, int cmd ) const; std::string format_pattern_row_channel( std::int32_t p, std::int32_t r, std::int32_t c, std::size_t width, bool pad ) const; std::string highlight_pattern_row_channel( std::int32_t p, std::int32_t r, std::int32_t c, std::size_t width, bool pad ) const; std::pair get_ctl_infos() const; std::vector get_ctls() const; std::string ctl_get( std::string ctl, bool throw_if_unknown = true ) const; bool ctl_get_boolean( std::string_view ctl, bool throw_if_unknown = true ) const; std::int64_t ctl_get_integer( std::string_view ctl, bool throw_if_unknown = true ) const; double ctl_get_floatingpoint( std::string_view ctl, bool throw_if_unknown = true ) const; std::string ctl_get_text( std::string_view ctl, bool throw_if_unknown = true ) const; void ctl_set( std::string ctl, const std::string & value, bool throw_if_unknown = true ); void ctl_set_boolean( std::string_view ctl, bool value, bool throw_if_unknown = true ); void ctl_set_integer( std::string_view ctl, std::int64_t value, bool throw_if_unknown = true ); void ctl_set_floatingpoint( std::string_view ctl, double value, bool throw_if_unknown = true ); void ctl_set_text( std::string_view ctl, std::string_view value, bool throw_if_unknown = true ); }; // class module_impl namespace helper { template std::unique_ptr make_unique(Args&&... args) { return std::unique_ptr(new T(std::forward(args)...)); } } // namespace helper } // namespace openmpt #if defined(_MSC_VER) #pragma warning(pop) #endif #endif // LIBOPENMPT_IMPL_HPP libopenmpt-0.6.1+release.autotools/libopenmpt/libopenmpt_internal.h0000644000175000017500000000162014057134205022643 00000000000000/* * libopenmpt_internal.h * --------------------- * Purpose: libopenmpt internal interface configuration, overruling the public interface configuration (only used and needed when building libopenmpt) * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #ifndef LIBOPENMPT_INTERNAL_H #define LIBOPENMPT_INTERNAL_H #include "libopenmpt_config.h" #ifdef __cplusplus #if defined(LIBOPENMPT_BUILD_DLL) || defined(LIBOPENMPT_USE_DLL) #if defined(_MSC_VER) && !defined(_DLL) /* #pragma message( "libopenmpt C++ interface is disabled if libopenmpt is built as a DLL and the runtime is statically linked. This is not supported by microsoft and cannot possibly work. Ever." ) */ #undef LIBOPENMPT_CXX_API #define LIBOPENMPT_CXX_API LIBOPENMPT_API_HELPER_LOCAL #endif #endif #endif #endif /* LIBOPENMPT_INTERNAL_H */ libopenmpt-0.6.1+release.autotools/libopenmpt/libopenmpt_test.cpp0000644000175000017500000000334714052666041022354 00000000000000/* * libopenmpt_test.cpp * ------------------- * Purpose: libopenmpt test suite driver * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "openmpt/all/BuildSettings.hpp" #include "libopenmpt_internal.h" #include "test/test.h" #include #include #include #include using namespace OpenMPT; #if defined( LIBOPENMPT_BUILD_TEST ) #if (defined(_WIN32) || defined(WIN32)) && (defined(_UNICODE) || defined(UNICODE)) #if defined(__GNUC__) // mingw-w64 g++ does only default to special C linkage for "main", but not for "wmain" (see ). extern "C" int wmain( int /*argc*/ , wchar_t * /*argv*/ [] ); extern "C" #endif int wmain( int /*argc*/ , wchar_t * /*argv*/ [] ) { #else int main( int /*argc*/ , char * /*argv*/ [] ) { #endif try { // run test with "C" / classic() locale Test::DoTests(); // try setting the C locale to the user locale setlocale( LC_ALL, "" ); // run all tests again with a set C locale Test::DoTests(); // try to set the C and C++ locales to the user locale try { std::locale old = std::locale::global( std::locale( "" ) ); (void)old; } catch ( ... ) { // Setting c++ global locale does not work. // This is no problem for libopenmpt, just continue. } // and now, run all tests once again Test::DoTests(); } catch ( const std::exception & e ) { std::cerr << "TEST ERROR: exception: " << ( e.what() ? e.what() : "" ) << std::endl; return -1; } catch ( ... ) { std::cerr << "TEST ERROR: unknown exception" << std::endl; return -1; } return 0; } #endif // LIBOPENMPT_BUILD_TEST libopenmpt-0.6.1+release.autotools/libopenmpt/.clang-format0000644000175000017500000000726014044173026021006 00000000000000# clang-format 11 Language: Cpp Standard: c++17 AccessModifierOffset: -2 #? AlignAfterOpenBracket: AlwaysBreak AlignConsecutiveAssignments: false AlignConsecutiveBitFields: false AlignConsecutiveDeclarations: false AlignConsecutiveMacros: true AlignEscapedNewlines: DontAlign AlignOperands: DontAlign AlignTrailingComments: true AllowAllArgumentsOnNextLine: true AllowAllConstructorInitializersOnNextLine: true AllowAllParametersOfDeclarationOnNextLine: true AllowShortBlocksOnASingleLine: Never AllowShortCaseLabelsOnASingleLine: false AllowShortEnumsOnASingleLine: false AllowShortFunctionsOnASingleLine: Empty AllowShortIfStatementsOnASingleLine: false AllowShortLambdasOnASingleLine: Inline AllowShortLoopsOnASingleLine: false AlwaysBreakAfterReturnType: None AlwaysBreakBeforeMultilineStrings: true AlwaysBreakTemplateDeclarations: Yes BinPackArguments: true BinPackParameters: false BraceWrapping: AfterCaseLabel: true AfterClass: false AfterControlStatement: MultiLine AfterEnum: false AfterFunction: false AfterNamespace: false #AfterObjCDeclaration AfterStruct: false AfterUnion: false AfterExternBlock: false BeforeCatch: false BeforeElse: false BeforeLambdaBody: true BeforeWhile: false IndentBraces: false SplitEmptyFunction: true SplitEmptyRecord: false SplitEmptyNamespace: true #BreakAfterJavaFieldAnnotations BreakBeforeBinaryOperators: NonAssignment BreakBeforeBraces: Custom BreakBeforeTernaryOperators: true BreakConstructorInitializers: BeforeComma BreakInheritanceList: BeforeComma BreakStringLiterals: false ColumnLimit: 0 CommentPragmas: '' #? CompactNamespaces: false ConstructorInitializerAllOnOneLineOrOnePerLine: true ConstructorInitializerIndentWidth: 2 #? ContinuationIndentWidth: 2 #? Cpp11BracedListStyle: true DeriveLineEnding: true DerivePointerAlignment: false FixNamespaceComments: true ForEachMacros: [] IncludeBlocks: Preserve IncludeCategories: [] #? IncludeIsMainRegex: '' #? IncludeIsMainSourceRegex: '' #? IndentCaseBlocks: true IndentCaseLabels: true IndentExternBlock: NoIndent IndentGotoLabels: false IndentPPDirectives: None #BeforeHash IndentWidth: 2 IndentWrappedFunctionNames: true InsertTrailingCommas: None #JavaImportGroups #JavaScriptQuotes #JavaScriptWrapImports KeepEmptyLinesAtTheStartOfBlocks: true MacroBlockBegin: '^BEGIN_MESSAGE_MAP$' #? MacroBlockEnd: '^END_MESSAGE_MAP$' #? MaxEmptyLinesToKeep: 3 NamespaceIndentation: None NamespaceMacros: [] #? #ObjCBinPackProtocolList #ObjCBlockIndentWidth #ObjCBreakBeforeNestedBlockParam #ObjCSpaceAfterProperty #ObjCSpaceBeforeProtocolList #PenaltyBreakAssignment #PenaltyBreakBeforeFirstCallParameter #PenaltyBreakComment #PenaltyBreakFirstLessLess #PenaltyBreakString #PenaltyBreakTemplateDeclaration #PenaltyExcessCharacter #PenaltyReturnTypeOnItsOwnLine PointerAlignment: Middle #RawStringFormats ReflowComments: false SortIncludes: false SortUsingDeclarations: true SpaceAfterCStyleCast: false SpaceAfterLogicalNot: false SpaceAfterTemplateKeyword: true SpaceBeforeAssignmentOperators: true SpaceBeforeCpp11BracedList: false SpaceBeforeCtorInitializerColon: true SpaceBeforeInheritanceColon: true SpaceBeforeParens: ControlStatements SpaceBeforeRangeBasedForLoopColon: true SpaceBeforeSquareBrackets: false SpaceInEmptyBlock: true SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 2 SpacesInAngles: false SpacesInCStyleCastParentheses: false SpacesInConditionalStatement: true SpacesInContainerLiterals: true SpacesInParentheses: true SpacesInSquareBrackets: false StatementMacros: [ 'MPT_WARNING', 'MPT_TEST_GROUP_INLINE_IDENTIFIER', 'MPT_TEST_GROUP_INLINE', 'MPT_TEST_GROUP_STATIC' ] #? TabWidth: 2 TypenameMacros: [] #? UseCRLF: false UseTab: ForContinuationAndIndentation WhitespaceSensitiveMacros: - MPT_PP_STRINGIFY libopenmpt-0.6.1+release.autotools/libopenmpt/libopenmpt_version.mk0000644000175000017500000000027314175540615022706 00000000000000LIBOPENMPT_VERSION_MAJOR=0 LIBOPENMPT_VERSION_MINOR=6 LIBOPENMPT_VERSION_PATCH=1 LIBOPENMPT_VERSION_PREREL= LIBOPENMPT_LTVER_CURRENT=3 LIBOPENMPT_LTVER_REVISION=1 LIBOPENMPT_LTVER_AGE=3 libopenmpt-0.6.1+release.autotools/m4/0000755000175000017500000000000014175541576014673 500000000000000libopenmpt-0.6.1+release.autotools/m4/ax_append_flag.m40000644000175000017500000000302014175541524017771 00000000000000# =========================================================================== # https://www.gnu.org/software/autoconf-archive/ax_append_flag.html # =========================================================================== # # SYNOPSIS # # AX_APPEND_FLAG(FLAG, [FLAGS-VARIABLE]) # # DESCRIPTION # # FLAG is appended to the FLAGS-VARIABLE shell variable, with a space # added in between. # # If FLAGS-VARIABLE is not specified, the current language's flags (e.g. # CFLAGS) is used. FLAGS-VARIABLE is not changed if it already contains # FLAG. If FLAGS-VARIABLE is unset in the shell, it is set to exactly # FLAG. # # NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. # # LICENSE # # Copyright (c) 2008 Guido U. Draheim # Copyright (c) 2011 Maarten Bosmans # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 8 AC_DEFUN([AX_APPEND_FLAG], [dnl AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_SET_IF AS_VAR_PUSHDEF([FLAGS], [m4_default($2,_AC_LANG_PREFIX[FLAGS])]) AS_VAR_SET_IF(FLAGS,[ AS_CASE([" AS_VAR_GET(FLAGS) "], [*" $1 "*], [AC_RUN_LOG([: FLAGS already contains $1])], [ AS_VAR_APPEND(FLAGS,[" $1"]) AC_RUN_LOG([: FLAGS="$FLAGS"]) ]) ], [ AS_VAR_SET(FLAGS,[$1]) AC_RUN_LOG([: FLAGS="$FLAGS"]) ]) AS_VAR_POPDEF([FLAGS])dnl ])dnl AX_APPEND_FLAG libopenmpt-0.6.1+release.autotools/m4/ax_cflags_warn_all.m40000644000175000017500000001170314175541524020656 00000000000000# =========================================================================== # https://www.gnu.org/software/autoconf-archive/ax_cflags_warn_all.html # =========================================================================== # # SYNOPSIS # # AX_CFLAGS_WARN_ALL [(shellvar [,default, [A/NA]])] # AX_CXXFLAGS_WARN_ALL [(shellvar [,default, [A/NA]])] # AX_FCFLAGS_WARN_ALL [(shellvar [,default, [A/NA]])] # # DESCRIPTION # # Try to find a compiler option that enables most reasonable warnings. # # For the GNU compiler it will be -Wall (and -ansi -pedantic) The result # is added to the shellvar being CFLAGS, CXXFLAGS, or FCFLAGS by default. # # Currently this macro knows about the GCC, Solaris, Digital Unix, AIX, # HP-UX, IRIX, NEC SX-5 (Super-UX 10), Cray J90 (Unicos 10.0.0.8), and # Intel compilers. For a given compiler, the Fortran flags are much more # experimental than their C equivalents. # # - $1 shell-variable-to-add-to : CFLAGS, CXXFLAGS, or FCFLAGS # - $2 add-value-if-not-found : nothing # - $3 action-if-found : add value to shellvariable # - $4 action-if-not-found : nothing # # NOTE: These macros depend on AX_APPEND_FLAG. # # LICENSE # # Copyright (c) 2008 Guido U. Draheim # Copyright (c) 2010 Rhys Ulerich # # This program is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation; either version 3 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. #serial 16 AC_DEFUN([AX_FLAGS_WARN_ALL],[dnl AS_VAR_PUSHDEF([FLAGS],[_AC_LANG_PREFIX[]FLAGS])dnl AS_VAR_PUSHDEF([VAR],[ac_cv_[]_AC_LANG_ABBREV[]flags_warn_all])dnl AC_CACHE_CHECK([m4_ifval($1,$1,FLAGS) for maximum warnings], VAR,[VAR="no, unknown" ac_save_[]FLAGS="$[]FLAGS" for ac_arg dnl in "-warn all % -warn all" dnl Intel "-pedantic % -Wall" dnl GCC "-xstrconst % -v" dnl Solaris C "-std1 % -verbose -w0 -warnprotos" dnl Digital Unix "-qlanglvl=ansi % -qsrcmsg -qinfo=all:noppt:noppc:noobs:nocnd" dnl AIX "-ansi -ansiE % -fullwarn" dnl IRIX "+ESlit % +w1" dnl HP-UX C "-Xc % -pvctl[,]fullmsg" dnl NEC SX-5 (Super-UX 10) "-h conform % -h msglevel 2" dnl Cray C (Unicos) # do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'` AC_COMPILE_IFELSE([AC_LANG_PROGRAM], [VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break]) done FLAGS="$ac_save_[]FLAGS" ]) AS_VAR_POPDEF([FLAGS])dnl AX_REQUIRE_DEFINED([AX_APPEND_FLAG]) case ".$VAR" in .ok|.ok,*) m4_ifvaln($3,$3) ;; .|.no|.no,*) m4_default($4,[m4_ifval($2,[AX_APPEND_FLAG([$2], [$1])])]) ;; *) m4_default($3,[AX_APPEND_FLAG([$VAR], [$1])]) ;; esac AS_VAR_POPDEF([VAR])dnl ])dnl AX_FLAGS_WARN_ALL dnl implementation tactics: dnl the for-argument contains a list of options. The first part of dnl these does only exist to detect the compiler - usually it is dnl a global option to enable -ansi or -extrawarnings. All other dnl compilers will fail about it. That was needed since a lot of dnl compilers will give false positives for some option-syntax dnl like -Woption or -Xoption as they think of it is a pass-through dnl to later compile stages or something. The "%" is used as a dnl delimiter. A non-option comment can be given after "%%" marks dnl which will be shown but not added to the respective C/CXXFLAGS. AC_DEFUN([AX_CFLAGS_WARN_ALL],[dnl AC_LANG_PUSH([C]) AX_FLAGS_WARN_ALL([$1], [$2], [$3], [$4]) AC_LANG_POP([C]) ]) AC_DEFUN([AX_CXXFLAGS_WARN_ALL],[dnl AC_LANG_PUSH([C++]) AX_FLAGS_WARN_ALL([$1], [$2], [$3], [$4]) AC_LANG_POP([C++]) ]) AC_DEFUN([AX_FCFLAGS_WARN_ALL],[dnl AC_LANG_PUSH([Fortran]) AX_FLAGS_WARN_ALL([$1], [$2], [$3], [$4]) AC_LANG_POP([Fortran]) ]) libopenmpt-0.6.1+release.autotools/m4/ax_check_compile_flag.m40000644000175000017500000000407014175541524021315 00000000000000# =========================================================================== # https://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html # =========================================================================== # # SYNOPSIS # # AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT]) # # DESCRIPTION # # Check whether the given FLAG works with the current language's compiler # or gives an error. (Warnings, however, are ignored) # # ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on # success/failure. # # If EXTRA-FLAGS is defined, it is added to the current language's default # flags (e.g. CFLAGS) when the check is done. The check is thus made with # the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to # force the compiler to issue an error when a bad flag is given. # # INPUT gives an alternative input source to AC_COMPILE_IFELSE. # # NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this # macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG. # # LICENSE # # Copyright (c) 2008 Guido U. Draheim # Copyright (c) 2011 Maarten Bosmans # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 6 AC_DEFUN([AX_CHECK_COMPILE_FLAG], [AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [ ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1" AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], [AS_VAR_SET(CACHEVAR,[yes])], [AS_VAR_SET(CACHEVAR,[no])]) _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags]) AS_VAR_IF(CACHEVAR,yes, [m4_default([$2], :)], [m4_default([$3], :)]) AS_VAR_POPDEF([CACHEVAR])dnl ])dnl AX_CHECK_COMPILE_FLAGS libopenmpt-0.6.1+release.autotools/m4/ax_cxx_compile_stdcxx.m40000644000175000017500000004545614175541524021463 00000000000000# =========================================================================== # https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html # =========================================================================== # # SYNOPSIS # # AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional]) # # DESCRIPTION # # Check for baseline language coverage in the compiler for the specified # version of the C++ standard. If necessary, add switches to CXX and # CXXCPP to enable support. VERSION may be '11' (for the C++11 standard) # or '14' (for the C++14 standard). # # The second argument, if specified, indicates whether you insist on an # extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g. # -std=c++11). If neither is specified, you get whatever works, with # preference for an extended mode. # # The third argument, if specified 'mandatory' or if left unspecified, # indicates that baseline support for the specified C++ standard is # required and that the macro should error out if no mode with that # support is found. If specified 'optional', then configuration proceeds # regardless, after defining HAVE_CXX${VERSION} if and only if a # supporting mode is found. # # LICENSE # # Copyright (c) 2008 Benjamin Kosnik # Copyright (c) 2012 Zack Weinberg # Copyright (c) 2013 Roy Stogner # Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov # Copyright (c) 2015 Paul Norman # Copyright (c) 2015 Moritz Klammler # Copyright (c) 2016, 2018 Krzesimir Nowak # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 10 dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro dnl (serial version number 13). AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"], [$1], [14], [ax_cxx_compile_alternatives="14 1y"], [$1], [17], [ax_cxx_compile_alternatives="17 1z"], [m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl m4_if([$2], [], [], [$2], [ext], [], [$2], [noext], [], [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true], [$3], [mandatory], [ax_cxx_compile_cxx$1_required=true], [$3], [optional], [ax_cxx_compile_cxx$1_required=false], [m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])]) AC_LANG_PUSH([C++])dnl ac_success=no m4_if([$2], [noext], [], [dnl if test x$ac_success = xno; then for alternative in ${ax_cxx_compile_alternatives}; do switch="-std=gnu++${alternative}" cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, $cachevar, [ac_save_CXX="$CXX" CXX="$CXX $switch" AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], [eval $cachevar=yes], [eval $cachevar=no]) CXX="$ac_save_CXX"]) if eval test x\$$cachevar = xyes; then CXX="$CXX $switch" if test -n "$CXXCPP" ; then CXXCPP="$CXXCPP $switch" fi ac_success=yes break fi done fi]) m4_if([$2], [ext], [], [dnl if test x$ac_success = xno; then dnl HP's aCC needs +std=c++11 according to: dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf dnl Cray's crayCC needs "-h std=c++11" for alternative in ${ax_cxx_compile_alternatives}; do for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, $cachevar, [ac_save_CXX="$CXX" CXX="$CXX $switch" AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], [eval $cachevar=yes], [eval $cachevar=no]) CXX="$ac_save_CXX"]) if eval test x\$$cachevar = xyes; then CXX="$CXX $switch" if test -n "$CXXCPP" ; then CXXCPP="$CXXCPP $switch" fi ac_success=yes break fi done if test x$ac_success = xyes; then break fi done fi]) AC_LANG_POP([C++]) if test x$ax_cxx_compile_cxx$1_required = xtrue; then if test x$ac_success = xno; then AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.]) fi fi if test x$ac_success = xno; then HAVE_CXX$1=0 AC_MSG_NOTICE([No compiler with C++$1 support was found]) else HAVE_CXX$1=1 AC_DEFINE(HAVE_CXX$1,1, [define if the compiler supports basic C++$1 syntax]) fi AC_SUBST(HAVE_CXX$1) ]) dnl Test body for checking C++11 support m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11], _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 ) dnl Test body for checking C++14 support m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14], _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 ) m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17], _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 _AX_CXX_COMPILE_STDCXX_testbody_new_in_17 ) dnl Tests for new features in C++11 m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[ // If the compiler admits that it is not ready for C++11, why torture it? // Hopefully, this will speed up the test. #ifndef __cplusplus #error "This is not a C++ compiler" #elif __cplusplus < 201103L #error "This is not a C++11 compiler" #else namespace cxx11 { namespace test_static_assert { template struct check { static_assert(sizeof(int) <= sizeof(T), "not big enough"); }; } namespace test_final_override { struct Base { virtual void f() {} }; struct Derived : public Base { virtual void f() override {} }; } namespace test_double_right_angle_brackets { template < typename T > struct check {}; typedef check single_type; typedef check> double_type; typedef check>> triple_type; typedef check>>> quadruple_type; } namespace test_decltype { int f() { int a = 1; decltype(a) b = 2; return a + b; } } namespace test_type_deduction { template < typename T1, typename T2 > struct is_same { static const bool value = false; }; template < typename T > struct is_same { static const bool value = true; }; template < typename T1, typename T2 > auto add(T1 a1, T2 a2) -> decltype(a1 + a2) { return a1 + a2; } int test(const int c, volatile int v) { static_assert(is_same::value == true, ""); static_assert(is_same::value == false, ""); static_assert(is_same::value == false, ""); auto ac = c; auto av = v; auto sumi = ac + av + 'x'; auto sumf = ac + av + 1.0; static_assert(is_same::value == true, ""); static_assert(is_same::value == true, ""); static_assert(is_same::value == true, ""); static_assert(is_same::value == false, ""); static_assert(is_same::value == true, ""); return (sumf > 0.0) ? sumi : add(c, v); } } namespace test_noexcept { int f() { return 0; } int g() noexcept { return 0; } static_assert(noexcept(f()) == false, ""); static_assert(noexcept(g()) == true, ""); } namespace test_constexpr { template < typename CharT > unsigned long constexpr strlen_c_r(const CharT *const s, const unsigned long acc) noexcept { return *s ? strlen_c_r(s + 1, acc + 1) : acc; } template < typename CharT > unsigned long constexpr strlen_c(const CharT *const s) noexcept { return strlen_c_r(s, 0UL); } static_assert(strlen_c("") == 0UL, ""); static_assert(strlen_c("1") == 1UL, ""); static_assert(strlen_c("example") == 7UL, ""); static_assert(strlen_c("another\0example") == 7UL, ""); } namespace test_rvalue_references { template < int N > struct answer { static constexpr int value = N; }; answer<1> f(int&) { return answer<1>(); } answer<2> f(const int&) { return answer<2>(); } answer<3> f(int&&) { return answer<3>(); } void test() { int i = 0; const int c = 0; static_assert(decltype(f(i))::value == 1, ""); static_assert(decltype(f(c))::value == 2, ""); static_assert(decltype(f(0))::value == 3, ""); } } namespace test_uniform_initialization { struct test { static const int zero {}; static const int one {1}; }; static_assert(test::zero == 0, ""); static_assert(test::one == 1, ""); } namespace test_lambdas { void test1() { auto lambda1 = [](){}; auto lambda2 = lambda1; lambda1(); lambda2(); } int test2() { auto a = [](int i, int j){ return i + j; }(1, 2); auto b = []() -> int { return '0'; }(); auto c = [=](){ return a + b; }(); auto d = [&](){ return c; }(); auto e = [a, &b](int x) mutable { const auto identity = [](int y){ return y; }; for (auto i = 0; i < a; ++i) a += b--; return x + identity(a + b); }(0); return a + b + c + d + e; } int test3() { const auto nullary = [](){ return 0; }; const auto unary = [](int x){ return x; }; using nullary_t = decltype(nullary); using unary_t = decltype(unary); const auto higher1st = [](nullary_t f){ return f(); }; const auto higher2nd = [unary](nullary_t f1){ return [unary, f1](unary_t f2){ return f2(unary(f1())); }; }; return higher1st(nullary) + higher2nd(nullary)(unary); } } namespace test_variadic_templates { template struct sum; template struct sum { static constexpr auto value = N0 + sum::value; }; template <> struct sum<> { static constexpr auto value = 0; }; static_assert(sum<>::value == 0, ""); static_assert(sum<1>::value == 1, ""); static_assert(sum<23>::value == 23, ""); static_assert(sum<1, 2>::value == 3, ""); static_assert(sum<5, 5, 11>::value == 21, ""); static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, ""); } // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function // because of this. namespace test_template_alias_sfinae { struct foo {}; template using member = typename T::member_type; template void func(...) {} template void func(member*) {} void test(); void test() { func(0); } } } // namespace cxx11 #endif // __cplusplus >= 201103L ]]) dnl Tests for new features in C++14 m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[ // If the compiler admits that it is not ready for C++14, why torture it? // Hopefully, this will speed up the test. #ifndef __cplusplus #error "This is not a C++ compiler" #elif __cplusplus < 201402L #error "This is not a C++14 compiler" #else namespace cxx14 { namespace test_polymorphic_lambdas { int test() { const auto lambda = [](auto&&... args){ const auto istiny = [](auto x){ return (sizeof(x) == 1UL) ? 1 : 0; }; const int aretiny[] = { istiny(args)... }; return aretiny[0]; }; return lambda(1, 1L, 1.0f, '1'); } } namespace test_binary_literals { constexpr auto ivii = 0b0000000000101010; static_assert(ivii == 42, "wrong value"); } namespace test_generalized_constexpr { template < typename CharT > constexpr unsigned long strlen_c(const CharT *const s) noexcept { auto length = 0UL; for (auto p = s; *p; ++p) ++length; return length; } static_assert(strlen_c("") == 0UL, ""); static_assert(strlen_c("x") == 1UL, ""); static_assert(strlen_c("test") == 4UL, ""); static_assert(strlen_c("another\0test") == 7UL, ""); } namespace test_lambda_init_capture { int test() { auto x = 0; const auto lambda1 = [a = x](int b){ return a + b; }; const auto lambda2 = [a = lambda1(x)](){ return a; }; return lambda2(); } } namespace test_digit_separators { constexpr auto ten_million = 100'000'000; static_assert(ten_million == 100000000, ""); } namespace test_return_type_deduction { auto f(int& x) { return x; } decltype(auto) g(int& x) { return x; } template < typename T1, typename T2 > struct is_same { static constexpr auto value = false; }; template < typename T > struct is_same { static constexpr auto value = true; }; int test() { auto x = 0; static_assert(is_same::value, ""); static_assert(is_same::value, ""); return x; } } } // namespace cxx14 #endif // __cplusplus >= 201402L ]]) dnl Tests for new features in C++17 m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[ // If the compiler admits that it is not ready for C++17, why torture it? // Hopefully, this will speed up the test. #ifndef __cplusplus #error "This is not a C++ compiler" #elif __cplusplus < 201703L #error "This is not a C++17 compiler" #else #include #include #include namespace cxx17 { namespace test_constexpr_lambdas { constexpr int foo = [](){return 42;}(); } namespace test::nested_namespace::definitions { } namespace test_fold_expression { template int multiply(Args... args) { return (args * ... * 1); } template bool all(Args... args) { return (args && ...); } } namespace test_extended_static_assert { static_assert (true); } namespace test_auto_brace_init_list { auto foo = {5}; auto bar {5}; static_assert(std::is_same, decltype(foo)>::value); static_assert(std::is_same::value); } namespace test_typename_in_template_template_parameter { template typename X> struct D; } namespace test_fallthrough_nodiscard_maybe_unused_attributes { int f1() { return 42; } [[nodiscard]] int f2() { [[maybe_unused]] auto unused = f1(); switch (f1()) { case 17: f1(); [[fallthrough]]; case 42: f1(); } return f1(); } } namespace test_extended_aggregate_initialization { struct base1 { int b1, b2 = 42; }; struct base2 { base2() { b3 = 42; } int b3; }; struct derived : base1, base2 { int d; }; derived d1 {{1, 2}, {}, 4}; // full initialization derived d2 {{}, {}, 4}; // value-initialized bases } namespace test_general_range_based_for_loop { struct iter { int i; int& operator* () { return i; } const int& operator* () const { return i; } iter& operator++() { ++i; return *this; } }; struct sentinel { int i; }; bool operator== (const iter& i, const sentinel& s) { return i.i == s.i; } bool operator!= (const iter& i, const sentinel& s) { return !(i == s); } struct range { iter begin() const { return {0}; } sentinel end() const { return {5}; } }; void f() { range r {}; for (auto i : r) { [[maybe_unused]] auto v = i; } } } namespace test_lambda_capture_asterisk_this_by_value { struct t { int i; int foo() { return [*this]() { return i; }(); } }; } namespace test_enum_class_construction { enum class byte : unsigned char {}; byte foo {42}; } namespace test_constexpr_if { template int f () { if constexpr(cond) { return 13; } else { return 42; } } } namespace test_selection_statement_with_initializer { int f() { return 13; } int f2() { if (auto i = f(); i > 0) { return 3; } switch (auto i = f(); i + 4) { case 17: return 2; default: return 1; } } } namespace test_template_argument_deduction_for_class_templates { template struct pair { pair (T1 p1, T2 p2) : m1 {p1}, m2 {p2} {} T1 m1; T2 m2; }; void f() { [[maybe_unused]] auto p = pair{13, 42u}; } } namespace test_non_type_auto_template_parameters { template struct B {}; B<5> b1; B<'a'> b2; } namespace test_structured_bindings { int arr[2] = { 1, 2 }; std::pair pr = { 1, 2 }; auto f1() -> int(&)[2] { return arr; } auto f2() -> std::pair& { return pr; } struct S { int x1 : 2; volatile double y1; }; S f3() { return {}; } auto [ x1, y1 ] = f1(); auto& [ xr1, yr1 ] = f1(); auto [ x2, y2 ] = f2(); auto& [ xr2, yr2 ] = f2(); const auto [ x3, y3 ] = f3(); } namespace test_exception_spec_type_system { struct Good {}; struct Bad {}; void g1() noexcept; void g2(); template Bad f(T*, T*); template Good f(T1*, T2*); static_assert (std::is_same_v); } namespace test_inline_variables { template void f(T) {} template inline T g(T) { return T{}; } template<> inline void f<>(int) {} template<> int g<>(int) { return 5; } } } // namespace cxx17 #endif // __cplusplus < 201703L ]]) libopenmpt-0.6.1+release.autotools/m4/ax_prog_doxygen.m40000644000175000017500000005002214175541524020241 00000000000000# =========================================================================== # https://www.gnu.org/software/autoconf-archive/ax_prog_doxygen.html # =========================================================================== # # SYNOPSIS # # DX_INIT_DOXYGEN(PROJECT-NAME, [DOXYFILE-PATH], [OUTPUT-DIR], ...) # DX_DOXYGEN_FEATURE(ON|OFF) # DX_DOT_FEATURE(ON|OFF) # DX_HTML_FEATURE(ON|OFF) # DX_CHM_FEATURE(ON|OFF) # DX_CHI_FEATURE(ON|OFF) # DX_MAN_FEATURE(ON|OFF) # DX_RTF_FEATURE(ON|OFF) # DX_XML_FEATURE(ON|OFF) # DX_PDF_FEATURE(ON|OFF) # DX_PS_FEATURE(ON|OFF) # # DESCRIPTION # # The DX_*_FEATURE macros control the default setting for the given # Doxygen feature. Supported features are 'DOXYGEN' itself, 'DOT' for # generating graphics, 'HTML' for plain HTML, 'CHM' for compressed HTML # help (for MS users), 'CHI' for generating a separate .chi file by the # .chm file, and 'MAN', 'RTF', 'XML', 'PDF' and 'PS' for the appropriate # output formats. The environment variable DOXYGEN_PAPER_SIZE may be # specified to override the default 'a4wide' paper size. # # By default, HTML, PDF and PS documentation is generated as this seems to # be the most popular and portable combination. MAN pages created by # Doxygen are usually problematic, though by picking an appropriate subset # and doing some massaging they might be better than nothing. CHM and RTF # are specific for MS (note that you can't generate both HTML and CHM at # the same time). The XML is rather useless unless you apply specialized # post-processing to it. # # The macros mainly control the default state of the feature. The use can # override the default by specifying --enable or --disable. The macros # ensure that contradictory flags are not given (e.g., # --enable-doxygen-html and --enable-doxygen-chm, # --enable-doxygen-anything with --disable-doxygen, etc.) Finally, each # feature will be automatically disabled (with a warning) if the required # programs are missing. # # Once all the feature defaults have been specified, call DX_INIT_DOXYGEN # with the following parameters: a one-word name for the project for use # as a filename base etc., an optional configuration file name (the # default is '$(srcdir)/Doxyfile', the same as Doxygen's default), and an # optional output directory name (the default is 'doxygen-doc'). To run # doxygen multiple times for different configuration files and output # directories provide more parameters: the second, forth, sixth, etc # parameter are configuration file names and the third, fifth, seventh, # etc parameter are output directories. No checking is done to catch # duplicates. # # Automake Support # # The DX_RULES substitution can be used to add all needed rules to the # Makefile. Note that this is a substitution without being a variable: # only the @DX_RULES@ syntax will work. # # The provided targets are: # # doxygen-doc: Generate all doxygen documentation. # # doxygen-run: Run doxygen, which will generate some of the # documentation (HTML, CHM, CHI, MAN, RTF, XML) # but will not do the post processing required # for the rest of it (PS, PDF). # # doxygen-ps: Generate doxygen PostScript documentation. # # doxygen-pdf: Generate doxygen PDF documentation. # # Note that by default these are not integrated into the automake targets. # If doxygen is used to generate man pages, you can achieve this # integration by setting man3_MANS to the list of man pages generated and # then adding the dependency: # # $(man3_MANS): doxygen-doc # # This will cause make to run doxygen and generate all the documentation. # # The following variable is intended for use in Makefile.am: # # DX_CLEANFILES = everything to clean. # # Then add this variable to MOSTLYCLEANFILES. # # LICENSE # # Copyright (c) 2009 Oren Ben-Kiki # Copyright (c) 2015 Olaf Mandel # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 24 ## ----------## ## Defaults. ## ## ----------## DX_ENV="" AC_DEFUN([DX_FEATURE_doc], ON) AC_DEFUN([DX_FEATURE_dot], OFF) AC_DEFUN([DX_FEATURE_man], OFF) AC_DEFUN([DX_FEATURE_html], ON) AC_DEFUN([DX_FEATURE_chm], OFF) AC_DEFUN([DX_FEATURE_chi], OFF) AC_DEFUN([DX_FEATURE_rtf], OFF) AC_DEFUN([DX_FEATURE_xml], OFF) AC_DEFUN([DX_FEATURE_pdf], ON) AC_DEFUN([DX_FEATURE_ps], ON) ## --------------- ## ## Private macros. ## ## --------------- ## # DX_ENV_APPEND(VARIABLE, VALUE) # ------------------------------ # Append VARIABLE="VALUE" to DX_ENV for invoking doxygen and add it # as a substitution (but not a Makefile variable). The substitution # is skipped if the variable name is VERSION. AC_DEFUN([DX_ENV_APPEND], [AC_SUBST([DX_ENV], ["$DX_ENV $1='$2'"])dnl m4_if([$1], [VERSION], [], [AC_SUBST([$1], [$2])dnl AM_SUBST_NOTMAKE([$1])])dnl ]) # DX_DIRNAME_EXPR # --------------- # Expand into a shell expression prints the directory part of a path. AC_DEFUN([DX_DIRNAME_EXPR], [[expr ".$1" : '\(\.\)[^/]*$' \| "x$1" : 'x\(.*\)/[^/]*$']]) # DX_IF_FEATURE(FEATURE, IF-ON, IF-OFF) # ------------------------------------- # Expands according to the M4 (static) status of the feature. AC_DEFUN([DX_IF_FEATURE], [ifelse(DX_FEATURE_$1, ON, [$2], [$3])]) # DX_REQUIRE_PROG(VARIABLE, PROGRAM) # ---------------------------------- # Require the specified program to be found for the DX_CURRENT_FEATURE to work. AC_DEFUN([DX_REQUIRE_PROG], [ AC_PATH_TOOL([$1], [$2]) if test "$DX_FLAG_[]DX_CURRENT_FEATURE$$1" = 1; then AC_MSG_WARN([$2 not found - will not DX_CURRENT_DESCRIPTION]) AC_SUBST(DX_FLAG_[]DX_CURRENT_FEATURE, 0) fi ]) # DX_TEST_FEATURE(FEATURE) # ------------------------ # Expand to a shell expression testing whether the feature is active. AC_DEFUN([DX_TEST_FEATURE], [test "$DX_FLAG_$1" = 1]) # DX_CHECK_DEPEND(REQUIRED_FEATURE, REQUIRED_STATE) # ------------------------------------------------- # Verify that a required features has the right state before trying to turn on # the DX_CURRENT_FEATURE. AC_DEFUN([DX_CHECK_DEPEND], [ test "$DX_FLAG_$1" = "$2" \ || AC_MSG_ERROR([doxygen-DX_CURRENT_FEATURE ifelse([$2], 1, requires, contradicts) doxygen-$1]) ]) # DX_CLEAR_DEPEND(FEATURE, REQUIRED_FEATURE, REQUIRED_STATE) # ---------------------------------------------------------- # Turn off the DX_CURRENT_FEATURE if the required feature is off. AC_DEFUN([DX_CLEAR_DEPEND], [ test "$DX_FLAG_$1" = "$2" || AC_SUBST(DX_FLAG_[]DX_CURRENT_FEATURE, 0) ]) # DX_FEATURE_ARG(FEATURE, DESCRIPTION, # CHECK_DEPEND, CLEAR_DEPEND, # REQUIRE, DO-IF-ON, DO-IF-OFF) # -------------------------------------------- # Parse the command-line option controlling a feature. CHECK_DEPEND is called # if the user explicitly turns the feature on (and invokes DX_CHECK_DEPEND), # otherwise CLEAR_DEPEND is called to turn off the default state if a required # feature is disabled (using DX_CLEAR_DEPEND). REQUIRE performs additional # requirement tests (DX_REQUIRE_PROG). Finally, an automake flag is set and # DO-IF-ON or DO-IF-OFF are called according to the final state of the feature. AC_DEFUN([DX_ARG_ABLE], [ AC_DEFUN([DX_CURRENT_FEATURE], [$1]) AC_DEFUN([DX_CURRENT_DESCRIPTION], [$2]) AC_ARG_ENABLE(doxygen-$1, [AS_HELP_STRING(DX_IF_FEATURE([$1], [--disable-doxygen-$1], [--enable-doxygen-$1]), DX_IF_FEATURE([$1], [don't $2], [$2]))], [ case "$enableval" in #( y|Y|yes|Yes|YES) AC_SUBST([DX_FLAG_$1], 1) $3 ;; #( n|N|no|No|NO) AC_SUBST([DX_FLAG_$1], 0) ;; #( *) AC_MSG_ERROR([invalid value '$enableval' given to doxygen-$1]) ;; esac ], [ AC_SUBST([DX_FLAG_$1], [DX_IF_FEATURE([$1], 1, 0)]) $4 ]) if DX_TEST_FEATURE([$1]); then $5 : fi if DX_TEST_FEATURE([$1]); then $6 : else $7 : fi ]) ## -------------- ## ## Public macros. ## ## -------------- ## # DX_XXX_FEATURE(DEFAULT_STATE) # ----------------------------- AC_DEFUN([DX_DOXYGEN_FEATURE], [AC_DEFUN([DX_FEATURE_doc], [$1])]) AC_DEFUN([DX_DOT_FEATURE], [AC_DEFUN([DX_FEATURE_dot], [$1])]) AC_DEFUN([DX_MAN_FEATURE], [AC_DEFUN([DX_FEATURE_man], [$1])]) AC_DEFUN([DX_HTML_FEATURE], [AC_DEFUN([DX_FEATURE_html], [$1])]) AC_DEFUN([DX_CHM_FEATURE], [AC_DEFUN([DX_FEATURE_chm], [$1])]) AC_DEFUN([DX_CHI_FEATURE], [AC_DEFUN([DX_FEATURE_chi], [$1])]) AC_DEFUN([DX_RTF_FEATURE], [AC_DEFUN([DX_FEATURE_rtf], [$1])]) AC_DEFUN([DX_XML_FEATURE], [AC_DEFUN([DX_FEATURE_xml], [$1])]) AC_DEFUN([DX_XML_FEATURE], [AC_DEFUN([DX_FEATURE_xml], [$1])]) AC_DEFUN([DX_PDF_FEATURE], [AC_DEFUN([DX_FEATURE_pdf], [$1])]) AC_DEFUN([DX_PS_FEATURE], [AC_DEFUN([DX_FEATURE_ps], [$1])]) # DX_INIT_DOXYGEN(PROJECT, [CONFIG-FILE], [OUTPUT-DOC-DIR], ...) # -------------------------------------------------------------- # PROJECT also serves as the base name for the documentation files. # The default CONFIG-FILE is "$(srcdir)/Doxyfile" and OUTPUT-DOC-DIR is # "doxygen-doc". # More arguments are interpreted as interleaved CONFIG-FILE and # OUTPUT-DOC-DIR values. AC_DEFUN([DX_INIT_DOXYGEN], [ # Files: AC_SUBST([DX_PROJECT], [$1]) AC_SUBST([DX_CONFIG], ['ifelse([$2], [], [$(srcdir)/Doxyfile], [$2])']) AC_SUBST([DX_DOCDIR], ['ifelse([$3], [], [doxygen-doc], [$3])']) m4_if(m4_eval(3 < m4_count($@)), 1, [m4_for([DX_i], 4, m4_count($@), 2, [AC_SUBST([DX_CONFIG]m4_eval(DX_i[/2]), 'm4_default_nblank_quoted(m4_argn(DX_i, $@), [$(srcdir)/Doxyfile])')])])dnl m4_if(m4_eval(3 < m4_count($@)), 1, [m4_for([DX_i], 5, m4_count($@,), 2, [AC_SUBST([DX_DOCDIR]m4_eval([(]DX_i[-1)/2]), 'm4_default_nblank_quoted(m4_argn(DX_i, $@), [doxygen-doc])')])])dnl m4_define([DX_loop], m4_dquote(m4_if(m4_eval(3 < m4_count($@)), 1, [m4_for([DX_i], 4, m4_count($@), 2, [, m4_eval(DX_i[/2])])], [])))dnl # Environment variables used inside doxygen.cfg: DX_ENV_APPEND(SRCDIR, $srcdir) DX_ENV_APPEND(PROJECT, $DX_PROJECT) DX_ENV_APPEND(VERSION, $PACKAGE_VERSION) # Doxygen itself: DX_ARG_ABLE(doc, [generate any doxygen documentation], [], [], [DX_REQUIRE_PROG([DX_DOXYGEN], doxygen) DX_REQUIRE_PROG([DX_PERL], perl)], [DX_ENV_APPEND(PERL_PATH, $DX_PERL)]) # Dot for graphics: DX_ARG_ABLE(dot, [generate graphics for doxygen documentation], [DX_CHECK_DEPEND(doc, 1)], [DX_CLEAR_DEPEND(doc, 1)], [DX_REQUIRE_PROG([DX_DOT], dot)], [DX_ENV_APPEND(HAVE_DOT, YES) DX_ENV_APPEND(DOT_PATH, [`DX_DIRNAME_EXPR($DX_DOT)`])], [DX_ENV_APPEND(HAVE_DOT, NO)]) # Man pages generation: DX_ARG_ABLE(man, [generate doxygen manual pages], [DX_CHECK_DEPEND(doc, 1)], [DX_CLEAR_DEPEND(doc, 1)], [], [DX_ENV_APPEND(GENERATE_MAN, YES)], [DX_ENV_APPEND(GENERATE_MAN, NO)]) # RTF file generation: DX_ARG_ABLE(rtf, [generate doxygen RTF documentation], [DX_CHECK_DEPEND(doc, 1)], [DX_CLEAR_DEPEND(doc, 1)], [], [DX_ENV_APPEND(GENERATE_RTF, YES)], [DX_ENV_APPEND(GENERATE_RTF, NO)]) # XML file generation: DX_ARG_ABLE(xml, [generate doxygen XML documentation], [DX_CHECK_DEPEND(doc, 1)], [DX_CLEAR_DEPEND(doc, 1)], [], [DX_ENV_APPEND(GENERATE_XML, YES)], [DX_ENV_APPEND(GENERATE_XML, NO)]) # (Compressed) HTML help generation: DX_ARG_ABLE(chm, [generate doxygen compressed HTML help documentation], [DX_CHECK_DEPEND(doc, 1)], [DX_CLEAR_DEPEND(doc, 1)], [DX_REQUIRE_PROG([DX_HHC], hhc)], [DX_ENV_APPEND(HHC_PATH, $DX_HHC) DX_ENV_APPEND(GENERATE_HTML, YES) DX_ENV_APPEND(GENERATE_HTMLHELP, YES)], [DX_ENV_APPEND(GENERATE_HTMLHELP, NO)]) # Separate CHI file generation. DX_ARG_ABLE(chi, [generate doxygen separate compressed HTML help index file], [DX_CHECK_DEPEND(chm, 1)], [DX_CLEAR_DEPEND(chm, 1)], [], [DX_ENV_APPEND(GENERATE_CHI, YES)], [DX_ENV_APPEND(GENERATE_CHI, NO)]) # Plain HTML pages generation: DX_ARG_ABLE(html, [generate doxygen plain HTML documentation], [DX_CHECK_DEPEND(doc, 1) DX_CHECK_DEPEND(chm, 0)], [DX_CLEAR_DEPEND(doc, 1) DX_CLEAR_DEPEND(chm, 0)], [], [DX_ENV_APPEND(GENERATE_HTML, YES)], [DX_TEST_FEATURE(chm) || DX_ENV_APPEND(GENERATE_HTML, NO)]) # PostScript file generation: DX_ARG_ABLE(ps, [generate doxygen PostScript documentation], [DX_CHECK_DEPEND(doc, 1)], [DX_CLEAR_DEPEND(doc, 1)], [DX_REQUIRE_PROG([DX_LATEX], latex) DX_REQUIRE_PROG([DX_MAKEINDEX], makeindex) DX_REQUIRE_PROG([DX_DVIPS], dvips) DX_REQUIRE_PROG([DX_EGREP], egrep)]) # PDF file generation: DX_ARG_ABLE(pdf, [generate doxygen PDF documentation], [DX_CHECK_DEPEND(doc, 1)], [DX_CLEAR_DEPEND(doc, 1)], [DX_REQUIRE_PROG([DX_PDFLATEX], pdflatex) DX_REQUIRE_PROG([DX_MAKEINDEX], makeindex) DX_REQUIRE_PROG([DX_EGREP], egrep)]) # LaTeX generation for PS and/or PDF: if DX_TEST_FEATURE(ps) || DX_TEST_FEATURE(pdf); then DX_ENV_APPEND(GENERATE_LATEX, YES) else DX_ENV_APPEND(GENERATE_LATEX, NO) fi # Paper size for PS and/or PDF: AC_ARG_VAR(DOXYGEN_PAPER_SIZE, [a4wide (default), a4, letter, legal or executive]) case "$DOXYGEN_PAPER_SIZE" in #( "") AC_SUBST(DOXYGEN_PAPER_SIZE, "") ;; #( a4wide|a4|letter|legal|executive) DX_ENV_APPEND(PAPER_SIZE, $DOXYGEN_PAPER_SIZE) ;; #( *) AC_MSG_ERROR([unknown DOXYGEN_PAPER_SIZE='$DOXYGEN_PAPER_SIZE']) ;; esac # Rules: AS_IF([[test $DX_FLAG_html -eq 1]], [[DX_SNIPPET_html="## ------------------------------- ## ## Rules specific for HTML output. ## ## ------------------------------- ## DX_CLEAN_HTML = \$(DX_DOCDIR)/html]dnl m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\ \$(DX_DOCDIR]DX_i[)/html]])[ "]], [[DX_SNIPPET_html=""]]) AS_IF([[test $DX_FLAG_chi -eq 1]], [[DX_SNIPPET_chi=" DX_CLEAN_CHI = \$(DX_DOCDIR)/\$(PACKAGE).chi]dnl m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\ \$(DX_DOCDIR]DX_i[)/\$(PACKAGE).chi]])["]], [[DX_SNIPPET_chi=""]]) AS_IF([[test $DX_FLAG_chm -eq 1]], [[DX_SNIPPET_chm="## ------------------------------ ## ## Rules specific for CHM output. ## ## ------------------------------ ## DX_CLEAN_CHM = \$(DX_DOCDIR)/chm]dnl m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\ \$(DX_DOCDIR]DX_i[)/chm]])[\ ${DX_SNIPPET_chi} "]], [[DX_SNIPPET_chm=""]]) AS_IF([[test $DX_FLAG_man -eq 1]], [[DX_SNIPPET_man="## ------------------------------ ## ## Rules specific for MAN output. ## ## ------------------------------ ## DX_CLEAN_MAN = \$(DX_DOCDIR)/man]dnl m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\ \$(DX_DOCDIR]DX_i[)/man]])[ "]], [[DX_SNIPPET_man=""]]) AS_IF([[test $DX_FLAG_rtf -eq 1]], [[DX_SNIPPET_rtf="## ------------------------------ ## ## Rules specific for RTF output. ## ## ------------------------------ ## DX_CLEAN_RTF = \$(DX_DOCDIR)/rtf]dnl m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\ \$(DX_DOCDIR]DX_i[)/rtf]])[ "]], [[DX_SNIPPET_rtf=""]]) AS_IF([[test $DX_FLAG_xml -eq 1]], [[DX_SNIPPET_xml="## ------------------------------ ## ## Rules specific for XML output. ## ## ------------------------------ ## DX_CLEAN_XML = \$(DX_DOCDIR)/xml]dnl m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\ \$(DX_DOCDIR]DX_i[)/xml]])[ "]], [[DX_SNIPPET_xml=""]]) AS_IF([[test $DX_FLAG_ps -eq 1]], [[DX_SNIPPET_ps="## ----------------------------- ## ## Rules specific for PS output. ## ## ----------------------------- ## DX_CLEAN_PS = \$(DX_DOCDIR)/\$(PACKAGE).ps]dnl m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\ \$(DX_DOCDIR]DX_i[)/\$(PACKAGE).ps]])[ DX_PS_GOAL = doxygen-ps doxygen-ps: \$(DX_CLEAN_PS) ]m4_foreach([DX_i], [DX_loop], [[\$(DX_DOCDIR]DX_i[)/\$(PACKAGE).ps: \$(DX_DOCDIR]DX_i[)/\$(PACKAGE).tag \$(DX_V_LATEX)cd \$(DX_DOCDIR]DX_i[)/latex; \\ rm -f *.aux *.toc *.idx *.ind *.ilg *.log *.out; \\ \$(DX_LATEX) refman.tex; \\ \$(DX_MAKEINDEX) refman.idx; \\ \$(DX_LATEX) refman.tex; \\ countdown=5; \\ while \$(DX_EGREP) 'Rerun (LaTeX|to get cross-references right)' \\ refman.log > /dev/null 2>&1 \\ && test \$\$countdown -gt 0; do \\ \$(DX_LATEX) refman.tex; \\ countdown=\`expr \$\$countdown - 1\`; \\ done; \\ \$(DX_DVIPS) -o ../\$(PACKAGE).ps refman.dvi ]])["]], [[DX_SNIPPET_ps=""]]) AS_IF([[test $DX_FLAG_pdf -eq 1]], [[DX_SNIPPET_pdf="## ------------------------------ ## ## Rules specific for PDF output. ## ## ------------------------------ ## DX_CLEAN_PDF = \$(DX_DOCDIR)/\$(PACKAGE).pdf]dnl m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\ \$(DX_DOCDIR]DX_i[)/\$(PACKAGE).pdf]])[ DX_PDF_GOAL = doxygen-pdf doxygen-pdf: \$(DX_CLEAN_PDF) ]m4_foreach([DX_i], [DX_loop], [[\$(DX_DOCDIR]DX_i[)/\$(PACKAGE).pdf: \$(DX_DOCDIR]DX_i[)/\$(PACKAGE).tag \$(DX_V_LATEX)cd \$(DX_DOCDIR]DX_i[)/latex; \\ rm -f *.aux *.toc *.idx *.ind *.ilg *.log *.out; \\ \$(DX_PDFLATEX) refman.tex; \\ \$(DX_MAKEINDEX) refman.idx; \\ \$(DX_PDFLATEX) refman.tex; \\ countdown=5; \\ while \$(DX_EGREP) 'Rerun (LaTeX|to get cross-references right)' \\ refman.log > /dev/null 2>&1 \\ && test \$\$countdown -gt 0; do \\ \$(DX_PDFLATEX) refman.tex; \\ countdown=\`expr \$\$countdown - 1\`; \\ done; \\ mv refman.pdf ../\$(PACKAGE).pdf ]])["]], [[DX_SNIPPET_pdf=""]]) AS_IF([[test $DX_FLAG_ps -eq 1 -o $DX_FLAG_pdf -eq 1]], [[DX_SNIPPET_latex="## ------------------------------------------------- ## ## Rules specific for LaTeX (shared for PS and PDF). ## ## ------------------------------------------------- ## DX_V_LATEX = \$(_DX_v_LATEX_\$(V)) _DX_v_LATEX_ = \$(_DX_v_LATEX_\$(AM_DEFAULT_VERBOSITY)) _DX_v_LATEX_0 = @echo \" LATEX \" \$][@; DX_CLEAN_LATEX = \$(DX_DOCDIR)/latex]dnl m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\ \$(DX_DOCDIR]DX_i[)/latex]])[ "]], [[DX_SNIPPET_latex=""]]) AS_IF([[test $DX_FLAG_doc -eq 1]], [[DX_SNIPPET_doc="## --------------------------------- ## ## Format-independent Doxygen rules. ## ## --------------------------------- ## ${DX_SNIPPET_html}\ ${DX_SNIPPET_chm}\ ${DX_SNIPPET_man}\ ${DX_SNIPPET_rtf}\ ${DX_SNIPPET_xml}\ ${DX_SNIPPET_ps}\ ${DX_SNIPPET_pdf}\ ${DX_SNIPPET_latex}\ DX_V_DXGEN = \$(_DX_v_DXGEN_\$(V)) _DX_v_DXGEN_ = \$(_DX_v_DXGEN_\$(AM_DEFAULT_VERBOSITY)) _DX_v_DXGEN_0 = @echo \" DXGEN \" \$<; .PHONY: doxygen-run doxygen-doc \$(DX_PS_GOAL) \$(DX_PDF_GOAL) .INTERMEDIATE: doxygen-run \$(DX_PS_GOAL) \$(DX_PDF_GOAL) doxygen-run:]m4_foreach([DX_i], [DX_loop], [[ \$(DX_DOCDIR]DX_i[)/\$(PACKAGE).tag]])[ doxygen-doc: doxygen-run \$(DX_PS_GOAL) \$(DX_PDF_GOAL) ]m4_foreach([DX_i], [DX_loop], [[\$(DX_DOCDIR]DX_i[)/\$(PACKAGE).tag: \$(DX_CONFIG]DX_i[) \$(pkginclude_HEADERS) \$(A""M_V_at)rm -rf \$(DX_DOCDIR]DX_i[) \$(DX_V_DXGEN)\$(DX_ENV) DOCDIR=\$(DX_DOCDIR]DX_i[) \$(DX_DOXYGEN) \$(DX_CONFIG]DX_i[) \$(A""M_V_at)echo Timestamp >\$][@ ]])dnl [DX_CLEANFILES = \\] m4_foreach([DX_i], [DX_loop], [[ \$(DX_DOCDIR]DX_i[)/doxygen_sqlite3.db \\ \$(DX_DOCDIR]DX_i[)/\$(PACKAGE).tag \\ ]])dnl [ -r \\ \$(DX_CLEAN_HTML) \\ \$(DX_CLEAN_CHM) \\ \$(DX_CLEAN_CHI) \\ \$(DX_CLEAN_MAN) \\ \$(DX_CLEAN_RTF) \\ \$(DX_CLEAN_XML) \\ \$(DX_CLEAN_PS) \\ \$(DX_CLEAN_PDF) \\ \$(DX_CLEAN_LATEX)"]], [[DX_SNIPPET_doc=""]]) AC_SUBST([DX_RULES], ["${DX_SNIPPET_doc}"])dnl AM_SUBST_NOTMAKE([DX_RULES]) #For debugging: #echo DX_FLAG_doc=$DX_FLAG_doc #echo DX_FLAG_dot=$DX_FLAG_dot #echo DX_FLAG_man=$DX_FLAG_man #echo DX_FLAG_html=$DX_FLAG_html #echo DX_FLAG_chm=$DX_FLAG_chm #echo DX_FLAG_chi=$DX_FLAG_chi #echo DX_FLAG_rtf=$DX_FLAG_rtf #echo DX_FLAG_xml=$DX_FLAG_xml #echo DX_FLAG_pdf=$DX_FLAG_pdf #echo DX_FLAG_ps=$DX_FLAG_ps #echo DX_ENV=$DX_ENV ]) libopenmpt-0.6.1+release.autotools/m4/ax_require_defined.m40000644000175000017500000000230214175541524020665 00000000000000# =========================================================================== # https://www.gnu.org/software/autoconf-archive/ax_require_defined.html # =========================================================================== # # SYNOPSIS # # AX_REQUIRE_DEFINED(MACRO) # # DESCRIPTION # # AX_REQUIRE_DEFINED is a simple helper for making sure other macros have # been defined and thus are available for use. This avoids random issues # where a macro isn't expanded. Instead the configure script emits a # non-fatal: # # ./configure: line 1673: AX_CFLAGS_WARN_ALL: command not found # # It's like AC_REQUIRE except it doesn't expand the required macro. # # Here's an example: # # AX_REQUIRE_DEFINED([AX_CHECK_LINK_FLAG]) # # LICENSE # # Copyright (c) 2014 Mike Frysinger # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 2 AC_DEFUN([AX_REQUIRE_DEFINED], [dnl m4_ifndef([$1], [m4_fatal([macro ]$1[ is not defined; is a m4 file missing?])]) ])dnl AX_REQUIRE_DEFINED libopenmpt-0.6.1+release.autotools/m4/libtool.m40000644000175000017500000112677114175541524016531 00000000000000# libtool.m4 - Configure libtool for the host system. -*-Autoconf-*- # # Copyright (C) 1996-2001, 2003-2015 Free Software Foundation, Inc. # Written by Gordon Matzigkeit, 1996 # # This file 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. m4_define([_LT_COPYING], [dnl # Copyright (C) 2014 Free Software Foundation, Inc. # This is free software; see the source for copying conditions. There is NO # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # GNU Libtool is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of of the License, or # (at your option) any later version. # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program or library that is built # using GNU Libtool, you may include this file under the same # distribution terms that you use for the rest of that program. # # GNU Libtool is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . ]) # serial 58 LT_INIT # LT_PREREQ(VERSION) # ------------------ # Complain and exit if this libtool version is less that VERSION. m4_defun([LT_PREREQ], [m4_if(m4_version_compare(m4_defn([LT_PACKAGE_VERSION]), [$1]), -1, [m4_default([$3], [m4_fatal([Libtool version $1 or higher is required], 63)])], [$2])]) # _LT_CHECK_BUILDDIR # ------------------ # Complain if the absolute build directory name contains unusual characters m4_defun([_LT_CHECK_BUILDDIR], [case `pwd` in *\ * | *\ *) AC_MSG_WARN([Libtool does not cope well with whitespace in `pwd`]) ;; esac ]) # LT_INIT([OPTIONS]) # ------------------ AC_DEFUN([LT_INIT], [AC_PREREQ([2.62])dnl We use AC_PATH_PROGS_FEATURE_CHECK AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl AC_BEFORE([$0], [LT_LANG])dnl AC_BEFORE([$0], [LT_OUTPUT])dnl AC_BEFORE([$0], [LTDL_INIT])dnl m4_require([_LT_CHECK_BUILDDIR])dnl dnl Autoconf doesn't catch unexpanded LT_ macros by default: m4_pattern_forbid([^_?LT_[A-Z_]+$])dnl m4_pattern_allow([^(_LT_EOF|LT_DLGLOBAL|LT_DLLAZY_OR_NOW|LT_MULTI_MODULE)$])dnl dnl aclocal doesn't pull ltoptions.m4, ltsugar.m4, or ltversion.m4 dnl unless we require an AC_DEFUNed macro: AC_REQUIRE([LTOPTIONS_VERSION])dnl AC_REQUIRE([LTSUGAR_VERSION])dnl AC_REQUIRE([LTVERSION_VERSION])dnl AC_REQUIRE([LTOBSOLETE_VERSION])dnl m4_require([_LT_PROG_LTMAIN])dnl _LT_SHELL_INIT([SHELL=${CONFIG_SHELL-/bin/sh}]) dnl Parse OPTIONS _LT_SET_OPTIONS([$0], [$1]) # This can be used to rebuild libtool when needed LIBTOOL_DEPS=$ltmain # Always use our own libtool. LIBTOOL='$(SHELL) $(top_builddir)/libtool' AC_SUBST(LIBTOOL)dnl _LT_SETUP # Only expand once: m4_define([LT_INIT]) ])# LT_INIT # Old names: AU_ALIAS([AC_PROG_LIBTOOL], [LT_INIT]) AU_ALIAS([AM_PROG_LIBTOOL], [LT_INIT]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_PROG_LIBTOOL], []) dnl AC_DEFUN([AM_PROG_LIBTOOL], []) # _LT_PREPARE_CC_BASENAME # ----------------------- m4_defun([_LT_PREPARE_CC_BASENAME], [ # Calculate cc_basename. Skip known compiler wrappers and cross-prefix. func_cc_basename () { for cc_temp in @S|@*""; do case $cc_temp in compile | *[[\\/]]compile | ccache | *[[\\/]]ccache ) ;; distcc | *[[\\/]]distcc | purify | *[[\\/]]purify ) ;; \-*) ;; *) break;; esac done func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` } ])# _LT_PREPARE_CC_BASENAME # _LT_CC_BASENAME(CC) # ------------------- # It would be clearer to call AC_REQUIREs from _LT_PREPARE_CC_BASENAME, # but that macro is also expanded into generated libtool script, which # arranges for $SED and $ECHO to be set by different means. m4_defun([_LT_CC_BASENAME], [m4_require([_LT_PREPARE_CC_BASENAME])dnl AC_REQUIRE([_LT_DECL_SED])dnl AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])dnl func_cc_basename $1 cc_basename=$func_cc_basename_result ]) # _LT_FILEUTILS_DEFAULTS # ---------------------- # It is okay to use these file commands and assume they have been set # sensibly after 'm4_require([_LT_FILEUTILS_DEFAULTS])'. m4_defun([_LT_FILEUTILS_DEFAULTS], [: ${CP="cp -f"} : ${MV="mv -f"} : ${RM="rm -f"} ])# _LT_FILEUTILS_DEFAULTS # _LT_SETUP # --------- m4_defun([_LT_SETUP], [AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_CANONICAL_BUILD])dnl AC_REQUIRE([_LT_PREPARE_SED_QUOTE_VARS])dnl AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])dnl _LT_DECL([], [PATH_SEPARATOR], [1], [The PATH separator for the build system])dnl dnl _LT_DECL([], [host_alias], [0], [The host system])dnl _LT_DECL([], [host], [0])dnl _LT_DECL([], [host_os], [0])dnl dnl _LT_DECL([], [build_alias], [0], [The build system])dnl _LT_DECL([], [build], [0])dnl _LT_DECL([], [build_os], [0])dnl dnl AC_REQUIRE([AC_PROG_CC])dnl AC_REQUIRE([LT_PATH_LD])dnl AC_REQUIRE([LT_PATH_NM])dnl dnl AC_REQUIRE([AC_PROG_LN_S])dnl test -z "$LN_S" && LN_S="ln -s" _LT_DECL([], [LN_S], [1], [Whether we need soft or hard links])dnl dnl AC_REQUIRE([LT_CMD_MAX_LEN])dnl _LT_DECL([objext], [ac_objext], [0], [Object file suffix (normally "o")])dnl _LT_DECL([], [exeext], [0], [Executable file suffix (normally "")])dnl dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_CHECK_SHELL_FEATURES])dnl m4_require([_LT_PATH_CONVERSION_FUNCTIONS])dnl m4_require([_LT_CMD_RELOAD])dnl m4_require([_LT_CHECK_MAGIC_METHOD])dnl m4_require([_LT_CHECK_SHAREDLIB_FROM_LINKLIB])dnl m4_require([_LT_CMD_OLD_ARCHIVE])dnl m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl m4_require([_LT_WITH_SYSROOT])dnl m4_require([_LT_CMD_TRUNCATE])dnl _LT_CONFIG_LIBTOOL_INIT([ # See if we are running on zsh, and set the options that allow our # commands through without removal of \ escapes INIT. if test -n "\${ZSH_VERSION+set}"; then setopt NO_GLOB_SUBST fi ]) if test -n "${ZSH_VERSION+set}"; then setopt NO_GLOB_SUBST fi _LT_CHECK_OBJDIR m4_require([_LT_TAG_COMPILER])dnl case $host_os in aix3*) # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. if test set != "${COLLECT_NAMES+set}"; then COLLECT_NAMES= export COLLECT_NAMES fi ;; esac # Global variables: ofile=libtool can_build_shared=yes # All known linkers require a '.a' archive for static linking (except MSVC, # which needs '.lib'). libext=a with_gnu_ld=$lt_cv_prog_gnu_ld old_CC=$CC old_CFLAGS=$CFLAGS # Set sane defaults for various variables test -z "$CC" && CC=cc test -z "$LTCC" && LTCC=$CC test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS test -z "$LD" && LD=ld test -z "$ac_objext" && ac_objext=o _LT_CC_BASENAME([$compiler]) # Only perform the check for file, if the check method requires it test -z "$MAGIC_CMD" && MAGIC_CMD=file case $deplibs_check_method in file_magic*) if test "$file_magic_cmd" = '$MAGIC_CMD'; then _LT_PATH_MAGIC fi ;; esac # Use C for the default configuration in the libtool script LT_SUPPORTED_TAG([CC]) _LT_LANG_C_CONFIG _LT_LANG_DEFAULT_CONFIG _LT_CONFIG_COMMANDS ])# _LT_SETUP # _LT_PREPARE_SED_QUOTE_VARS # -------------------------- # Define a few sed substitution that help us do robust quoting. m4_defun([_LT_PREPARE_SED_QUOTE_VARS], [# Backslashify metacharacters that are still active within # double-quoted strings. sed_quote_subst='s/\([["`$\\]]\)/\\\1/g' # Same as above, but do not quote variable references. double_quote_subst='s/\([["`\\]]\)/\\\1/g' # Sed substitution to delay expansion of an escaped shell variable in a # double_quote_subst'ed string. delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' # Sed substitution to delay expansion of an escaped single quote. delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g' # Sed substitution to avoid accidental globbing in evaled expressions no_glob_subst='s/\*/\\\*/g' ]) # _LT_PROG_LTMAIN # --------------- # Note that this code is called both from 'configure', and 'config.status' # now that we use AC_CONFIG_COMMANDS to generate libtool. Notably, # 'config.status' has no value for ac_aux_dir unless we are using Automake, # so we pass a copy along to make sure it has a sensible value anyway. m4_defun([_LT_PROG_LTMAIN], [m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([ltmain.sh])])dnl _LT_CONFIG_LIBTOOL_INIT([ac_aux_dir='$ac_aux_dir']) ltmain=$ac_aux_dir/ltmain.sh ])# _LT_PROG_LTMAIN ## ------------------------------------- ## ## Accumulate code for creating libtool. ## ## ------------------------------------- ## # So that we can recreate a full libtool script including additional # tags, we accumulate the chunks of code to send to AC_CONFIG_COMMANDS # in macros and then make a single call at the end using the 'libtool' # label. # _LT_CONFIG_LIBTOOL_INIT([INIT-COMMANDS]) # ---------------------------------------- # Register INIT-COMMANDS to be passed to AC_CONFIG_COMMANDS later. m4_define([_LT_CONFIG_LIBTOOL_INIT], [m4_ifval([$1], [m4_append([_LT_OUTPUT_LIBTOOL_INIT], [$1 ])])]) # Initialize. m4_define([_LT_OUTPUT_LIBTOOL_INIT]) # _LT_CONFIG_LIBTOOL([COMMANDS]) # ------------------------------ # Register COMMANDS to be passed to AC_CONFIG_COMMANDS later. m4_define([_LT_CONFIG_LIBTOOL], [m4_ifval([$1], [m4_append([_LT_OUTPUT_LIBTOOL_COMMANDS], [$1 ])])]) # Initialize. m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS]) # _LT_CONFIG_SAVE_COMMANDS([COMMANDS], [INIT_COMMANDS]) # ----------------------------------------------------- m4_defun([_LT_CONFIG_SAVE_COMMANDS], [_LT_CONFIG_LIBTOOL([$1]) _LT_CONFIG_LIBTOOL_INIT([$2]) ]) # _LT_FORMAT_COMMENT([COMMENT]) # ----------------------------- # Add leading comment marks to the start of each line, and a trailing # full-stop to the whole comment if one is not present already. m4_define([_LT_FORMAT_COMMENT], [m4_ifval([$1], [ m4_bpatsubst([m4_bpatsubst([$1], [^ *], [# ])], [['`$\]], [\\\&])]m4_bmatch([$1], [[!?.]$], [], [.]) )]) ## ------------------------ ## ## FIXME: Eliminate VARNAME ## ## ------------------------ ## # _LT_DECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION], [IS-TAGGED?]) # ------------------------------------------------------------------- # CONFIGNAME is the name given to the value in the libtool script. # VARNAME is the (base) name used in the configure script. # VALUE may be 0, 1 or 2 for a computed quote escaped value based on # VARNAME. Any other value will be used directly. m4_define([_LT_DECL], [lt_if_append_uniq([lt_decl_varnames], [$2], [, ], [lt_dict_add_subkey([lt_decl_dict], [$2], [libtool_name], [m4_ifval([$1], [$1], [$2])]) lt_dict_add_subkey([lt_decl_dict], [$2], [value], [$3]) m4_ifval([$4], [lt_dict_add_subkey([lt_decl_dict], [$2], [description], [$4])]) lt_dict_add_subkey([lt_decl_dict], [$2], [tagged?], [m4_ifval([$5], [yes], [no])])]) ]) # _LT_TAGDECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION]) # -------------------------------------------------------- m4_define([_LT_TAGDECL], [_LT_DECL([$1], [$2], [$3], [$4], [yes])]) # lt_decl_tag_varnames([SEPARATOR], [VARNAME1...]) # ------------------------------------------------ m4_define([lt_decl_tag_varnames], [_lt_decl_filter([tagged?], [yes], $@)]) # _lt_decl_filter(SUBKEY, VALUE, [SEPARATOR], [VARNAME1..]) # --------------------------------------------------------- m4_define([_lt_decl_filter], [m4_case([$#], [0], [m4_fatal([$0: too few arguments: $#])], [1], [m4_fatal([$0: too few arguments: $#: $1])], [2], [lt_dict_filter([lt_decl_dict], [$1], [$2], [], lt_decl_varnames)], [3], [lt_dict_filter([lt_decl_dict], [$1], [$2], [$3], lt_decl_varnames)], [lt_dict_filter([lt_decl_dict], $@)])[]dnl ]) # lt_decl_quote_varnames([SEPARATOR], [VARNAME1...]) # -------------------------------------------------- m4_define([lt_decl_quote_varnames], [_lt_decl_filter([value], [1], $@)]) # lt_decl_dquote_varnames([SEPARATOR], [VARNAME1...]) # --------------------------------------------------- m4_define([lt_decl_dquote_varnames], [_lt_decl_filter([value], [2], $@)]) # lt_decl_varnames_tagged([SEPARATOR], [VARNAME1...]) # --------------------------------------------------- m4_define([lt_decl_varnames_tagged], [m4_assert([$# <= 2])dnl _$0(m4_quote(m4_default([$1], [[, ]])), m4_ifval([$2], [[$2]], [m4_dquote(lt_decl_tag_varnames)]), m4_split(m4_normalize(m4_quote(_LT_TAGS)), [ ]))]) m4_define([_lt_decl_varnames_tagged], [m4_ifval([$3], [lt_combine([$1], [$2], [_], $3)])]) # lt_decl_all_varnames([SEPARATOR], [VARNAME1...]) # ------------------------------------------------ m4_define([lt_decl_all_varnames], [_$0(m4_quote(m4_default([$1], [[, ]])), m4_if([$2], [], m4_quote(lt_decl_varnames), m4_quote(m4_shift($@))))[]dnl ]) m4_define([_lt_decl_all_varnames], [lt_join($@, lt_decl_varnames_tagged([$1], lt_decl_tag_varnames([[, ]], m4_shift($@))))dnl ]) # _LT_CONFIG_STATUS_DECLARE([VARNAME]) # ------------------------------------ # Quote a variable value, and forward it to 'config.status' so that its # declaration there will have the same value as in 'configure'. VARNAME # must have a single quote delimited value for this to work. m4_define([_LT_CONFIG_STATUS_DECLARE], [$1='`$ECHO "$][$1" | $SED "$delay_single_quote_subst"`']) # _LT_CONFIG_STATUS_DECLARATIONS # ------------------------------ # We delimit libtool config variables with single quotes, so when # we write them to config.status, we have to be sure to quote all # embedded single quotes properly. In configure, this macro expands # each variable declared with _LT_DECL (and _LT_TAGDECL) into: # # ='`$ECHO "$" | $SED "$delay_single_quote_subst"`' m4_defun([_LT_CONFIG_STATUS_DECLARATIONS], [m4_foreach([_lt_var], m4_quote(lt_decl_all_varnames), [m4_n([_LT_CONFIG_STATUS_DECLARE(_lt_var)])])]) # _LT_LIBTOOL_TAGS # ---------------- # Output comment and list of tags supported by the script m4_defun([_LT_LIBTOOL_TAGS], [_LT_FORMAT_COMMENT([The names of the tagged configurations supported by this script])dnl available_tags='_LT_TAGS'dnl ]) # _LT_LIBTOOL_DECLARE(VARNAME, [TAG]) # ----------------------------------- # Extract the dictionary values for VARNAME (optionally with TAG) and # expand to a commented shell variable setting: # # # Some comment about what VAR is for. # visible_name=$lt_internal_name m4_define([_LT_LIBTOOL_DECLARE], [_LT_FORMAT_COMMENT(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [description])))[]dnl m4_pushdef([_libtool_name], m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [libtool_name])))[]dnl m4_case(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [value])), [0], [_libtool_name=[$]$1], [1], [_libtool_name=$lt_[]$1], [2], [_libtool_name=$lt_[]$1], [_libtool_name=lt_dict_fetch([lt_decl_dict], [$1], [value])])[]dnl m4_ifval([$2], [_$2])[]m4_popdef([_libtool_name])[]dnl ]) # _LT_LIBTOOL_CONFIG_VARS # ----------------------- # Produce commented declarations of non-tagged libtool config variables # suitable for insertion in the LIBTOOL CONFIG section of the 'libtool' # script. Tagged libtool config variables (even for the LIBTOOL CONFIG # section) are produced by _LT_LIBTOOL_TAG_VARS. m4_defun([_LT_LIBTOOL_CONFIG_VARS], [m4_foreach([_lt_var], m4_quote(_lt_decl_filter([tagged?], [no], [], lt_decl_varnames)), [m4_n([_LT_LIBTOOL_DECLARE(_lt_var)])])]) # _LT_LIBTOOL_TAG_VARS(TAG) # ------------------------- m4_define([_LT_LIBTOOL_TAG_VARS], [m4_foreach([_lt_var], m4_quote(lt_decl_tag_varnames), [m4_n([_LT_LIBTOOL_DECLARE(_lt_var, [$1])])])]) # _LT_TAGVAR(VARNAME, [TAGNAME]) # ------------------------------ m4_define([_LT_TAGVAR], [m4_ifval([$2], [$1_$2], [$1])]) # _LT_CONFIG_COMMANDS # ------------------- # Send accumulated output to $CONFIG_STATUS. Thanks to the lists of # variables for single and double quote escaping we saved from calls # to _LT_DECL, we can put quote escaped variables declarations # into 'config.status', and then the shell code to quote escape them in # for loops in 'config.status'. Finally, any additional code accumulated # from calls to _LT_CONFIG_LIBTOOL_INIT is expanded. m4_defun([_LT_CONFIG_COMMANDS], [AC_PROVIDE_IFELSE([LT_OUTPUT], dnl If the libtool generation code has been placed in $CONFIG_LT, dnl instead of duplicating it all over again into config.status, dnl then we will have config.status run $CONFIG_LT later, so it dnl needs to know what name is stored there: [AC_CONFIG_COMMANDS([libtool], [$SHELL $CONFIG_LT || AS_EXIT(1)], [CONFIG_LT='$CONFIG_LT'])], dnl If the libtool generation code is destined for config.status, dnl expand the accumulated commands and init code now: [AC_CONFIG_COMMANDS([libtool], [_LT_OUTPUT_LIBTOOL_COMMANDS], [_LT_OUTPUT_LIBTOOL_COMMANDS_INIT])]) ])#_LT_CONFIG_COMMANDS # Initialize. m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS_INIT], [ # The HP-UX ksh and POSIX shell print the target directory to stdout # if CDPATH is set. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH sed_quote_subst='$sed_quote_subst' double_quote_subst='$double_quote_subst' delay_variable_subst='$delay_variable_subst' _LT_CONFIG_STATUS_DECLARATIONS LTCC='$LTCC' LTCFLAGS='$LTCFLAGS' compiler='$compiler_DEFAULT' # A function that is used when there is no print builtin or printf. func_fallback_echo () { eval 'cat <<_LTECHO_EOF \$[]1 _LTECHO_EOF' } # Quote evaled strings. for var in lt_decl_all_varnames([[ \ ]], lt_decl_quote_varnames); do case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in *[[\\\\\\\`\\"\\\$]]*) eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac done # Double-quote double-evaled strings. for var in lt_decl_all_varnames([[ \ ]], lt_decl_dquote_varnames); do case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in *[[\\\\\\\`\\"\\\$]]*) eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac done _LT_OUTPUT_LIBTOOL_INIT ]) # _LT_GENERATED_FILE_INIT(FILE, [COMMENT]) # ------------------------------------ # Generate a child script FILE with all initialization necessary to # reuse the environment learned by the parent script, and make the # file executable. If COMMENT is supplied, it is inserted after the # '#!' sequence but before initialization text begins. After this # macro, additional text can be appended to FILE to form the body of # the child script. The macro ends with non-zero status if the # file could not be fully written (such as if the disk is full). m4_ifdef([AS_INIT_GENERATED], [m4_defun([_LT_GENERATED_FILE_INIT],[AS_INIT_GENERATED($@)])], [m4_defun([_LT_GENERATED_FILE_INIT], [m4_require([AS_PREPARE])]dnl [m4_pushdef([AS_MESSAGE_LOG_FD])]dnl [lt_write_fail=0 cat >$1 <<_ASEOF || lt_write_fail=1 #! $SHELL # Generated by $as_me. $2 SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$1 <<\_ASEOF || lt_write_fail=1 AS_SHELL_SANITIZE _AS_PREPARE exec AS_MESSAGE_FD>&1 _ASEOF test 0 = "$lt_write_fail" && chmod +x $1[]dnl m4_popdef([AS_MESSAGE_LOG_FD])])])# _LT_GENERATED_FILE_INIT # LT_OUTPUT # --------- # This macro allows early generation of the libtool script (before # AC_OUTPUT is called), incase it is used in configure for compilation # tests. AC_DEFUN([LT_OUTPUT], [: ${CONFIG_LT=./config.lt} AC_MSG_NOTICE([creating $CONFIG_LT]) _LT_GENERATED_FILE_INIT(["$CONFIG_LT"], [# Run this file to recreate a libtool stub with the current configuration.]) cat >>"$CONFIG_LT" <<\_LTEOF lt_cl_silent=false exec AS_MESSAGE_LOG_FD>>config.log { echo AS_BOX([Running $as_me.]) } >&AS_MESSAGE_LOG_FD lt_cl_help="\ '$as_me' creates a local libtool stub from the current configuration, for use in further configure time tests before the real libtool is generated. Usage: $[0] [[OPTIONS]] -h, --help print this help, then exit -V, --version print version number, then exit -q, --quiet do not print progress messages -d, --debug don't remove temporary files Report bugs to ." lt_cl_version="\ m4_ifset([AC_PACKAGE_NAME], [AC_PACKAGE_NAME ])config.lt[]dnl m4_ifset([AC_PACKAGE_VERSION], [ AC_PACKAGE_VERSION]) configured by $[0], generated by m4_PACKAGE_STRING. Copyright (C) 2011 Free Software Foundation, Inc. This config.lt script is free software; the Free Software Foundation gives unlimited permision to copy, distribute and modify it." while test 0 != $[#] do case $[1] in --version | --v* | -V ) echo "$lt_cl_version"; exit 0 ;; --help | --h* | -h ) echo "$lt_cl_help"; exit 0 ;; --debug | --d* | -d ) debug=: ;; --quiet | --q* | --silent | --s* | -q ) lt_cl_silent=: ;; -*) AC_MSG_ERROR([unrecognized option: $[1] Try '$[0] --help' for more information.]) ;; *) AC_MSG_ERROR([unrecognized argument: $[1] Try '$[0] --help' for more information.]) ;; esac shift done if $lt_cl_silent; then exec AS_MESSAGE_FD>/dev/null fi _LTEOF cat >>"$CONFIG_LT" <<_LTEOF _LT_OUTPUT_LIBTOOL_COMMANDS_INIT _LTEOF cat >>"$CONFIG_LT" <<\_LTEOF AC_MSG_NOTICE([creating $ofile]) _LT_OUTPUT_LIBTOOL_COMMANDS AS_EXIT(0) _LTEOF chmod +x "$CONFIG_LT" # configure is writing to config.log, but config.lt does its own redirection, # appending to config.log, which fails on DOS, as config.log is still kept # open by configure. Here we exec the FD to /dev/null, effectively closing # config.log, so it can be properly (re)opened and appended to by config.lt. lt_cl_success=: test yes = "$silent" && lt_config_lt_args="$lt_config_lt_args --quiet" exec AS_MESSAGE_LOG_FD>/dev/null $SHELL "$CONFIG_LT" $lt_config_lt_args || lt_cl_success=false exec AS_MESSAGE_LOG_FD>>config.log $lt_cl_success || AS_EXIT(1) ])# LT_OUTPUT # _LT_CONFIG(TAG) # --------------- # If TAG is the built-in tag, create an initial libtool script with a # default configuration from the untagged config vars. Otherwise add code # to config.status for appending the configuration named by TAG from the # matching tagged config vars. m4_defun([_LT_CONFIG], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl _LT_CONFIG_SAVE_COMMANDS([ m4_define([_LT_TAG], m4_if([$1], [], [C], [$1]))dnl m4_if(_LT_TAG, [C], [ # See if we are running on zsh, and set the options that allow our # commands through without removal of \ escapes. if test -n "${ZSH_VERSION+set}"; then setopt NO_GLOB_SUBST fi cfgfile=${ofile}T trap "$RM \"$cfgfile\"; exit 1" 1 2 15 $RM "$cfgfile" cat <<_LT_EOF >> "$cfgfile" #! $SHELL # Generated automatically by $as_me ($PACKAGE) $VERSION # NOTE: Changes made to this file will be lost: look at ltmain.sh. # Provide generalized library-building support services. # Written by Gordon Matzigkeit, 1996 _LT_COPYING _LT_LIBTOOL_TAGS # Configured defaults for sys_lib_dlsearch_path munging. : \${LT_SYS_LIBRARY_PATH="$configure_time_lt_sys_library_path"} # ### BEGIN LIBTOOL CONFIG _LT_LIBTOOL_CONFIG_VARS _LT_LIBTOOL_TAG_VARS # ### END LIBTOOL CONFIG _LT_EOF cat <<'_LT_EOF' >> "$cfgfile" # ### BEGIN FUNCTIONS SHARED WITH CONFIGURE _LT_PREPARE_MUNGE_PATH_LIST _LT_PREPARE_CC_BASENAME # ### END FUNCTIONS SHARED WITH CONFIGURE _LT_EOF case $host_os in aix3*) cat <<\_LT_EOF >> "$cfgfile" # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. if test set != "${COLLECT_NAMES+set}"; then COLLECT_NAMES= export COLLECT_NAMES fi _LT_EOF ;; esac _LT_PROG_LTMAIN # We use sed instead of cat because bash on DJGPP gets confused if # if finds mixed CR/LF and LF-only lines. Since sed operates in # text mode, it properly converts lines to CR/LF. This bash problem # is reportedly fixed, but why not run on old versions too? sed '$q' "$ltmain" >> "$cfgfile" \ || (rm -f "$cfgfile"; exit 1) mv -f "$cfgfile" "$ofile" || (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") chmod +x "$ofile" ], [cat <<_LT_EOF >> "$ofile" dnl Unfortunately we have to use $1 here, since _LT_TAG is not expanded dnl in a comment (ie after a #). # ### BEGIN LIBTOOL TAG CONFIG: $1 _LT_LIBTOOL_TAG_VARS(_LT_TAG) # ### END LIBTOOL TAG CONFIG: $1 _LT_EOF ])dnl /m4_if ], [m4_if([$1], [], [ PACKAGE='$PACKAGE' VERSION='$VERSION' RM='$RM' ofile='$ofile'], []) ])dnl /_LT_CONFIG_SAVE_COMMANDS ])# _LT_CONFIG # LT_SUPPORTED_TAG(TAG) # --------------------- # Trace this macro to discover what tags are supported by the libtool # --tag option, using: # autoconf --trace 'LT_SUPPORTED_TAG:$1' AC_DEFUN([LT_SUPPORTED_TAG], []) # C support is built-in for now m4_define([_LT_LANG_C_enabled], []) m4_define([_LT_TAGS], []) # LT_LANG(LANG) # ------------- # Enable libtool support for the given language if not already enabled. AC_DEFUN([LT_LANG], [AC_BEFORE([$0], [LT_OUTPUT])dnl m4_case([$1], [C], [_LT_LANG(C)], [C++], [_LT_LANG(CXX)], [Go], [_LT_LANG(GO)], [Java], [_LT_LANG(GCJ)], [Fortran 77], [_LT_LANG(F77)], [Fortran], [_LT_LANG(FC)], [Windows Resource], [_LT_LANG(RC)], [m4_ifdef([_LT_LANG_]$1[_CONFIG], [_LT_LANG($1)], [m4_fatal([$0: unsupported language: "$1"])])])dnl ])# LT_LANG # _LT_LANG(LANGNAME) # ------------------ m4_defun([_LT_LANG], [m4_ifdef([_LT_LANG_]$1[_enabled], [], [LT_SUPPORTED_TAG([$1])dnl m4_append([_LT_TAGS], [$1 ])dnl m4_define([_LT_LANG_]$1[_enabled], [])dnl _LT_LANG_$1_CONFIG($1)])dnl ])# _LT_LANG m4_ifndef([AC_PROG_GO], [ ############################################################ # NOTE: This macro has been submitted for inclusion into # # GNU Autoconf as AC_PROG_GO. When it is available in # # a released version of Autoconf we should remove this # # macro and use it instead. # ############################################################ m4_defun([AC_PROG_GO], [AC_LANG_PUSH(Go)dnl AC_ARG_VAR([GOC], [Go compiler command])dnl AC_ARG_VAR([GOFLAGS], [Go compiler flags])dnl _AC_ARG_VAR_LDFLAGS()dnl AC_CHECK_TOOL(GOC, gccgo) if test -z "$GOC"; then if test -n "$ac_tool_prefix"; then AC_CHECK_PROG(GOC, [${ac_tool_prefix}gccgo], [${ac_tool_prefix}gccgo]) fi fi if test -z "$GOC"; then AC_CHECK_PROG(GOC, gccgo, gccgo, false) fi ])#m4_defun ])#m4_ifndef # _LT_LANG_DEFAULT_CONFIG # ----------------------- m4_defun([_LT_LANG_DEFAULT_CONFIG], [AC_PROVIDE_IFELSE([AC_PROG_CXX], [LT_LANG(CXX)], [m4_define([AC_PROG_CXX], defn([AC_PROG_CXX])[LT_LANG(CXX)])]) AC_PROVIDE_IFELSE([AC_PROG_F77], [LT_LANG(F77)], [m4_define([AC_PROG_F77], defn([AC_PROG_F77])[LT_LANG(F77)])]) AC_PROVIDE_IFELSE([AC_PROG_FC], [LT_LANG(FC)], [m4_define([AC_PROG_FC], defn([AC_PROG_FC])[LT_LANG(FC)])]) dnl The call to [A][M_PROG_GCJ] is quoted like that to stop aclocal dnl pulling things in needlessly. AC_PROVIDE_IFELSE([AC_PROG_GCJ], [LT_LANG(GCJ)], [AC_PROVIDE_IFELSE([A][M_PROG_GCJ], [LT_LANG(GCJ)], [AC_PROVIDE_IFELSE([LT_PROG_GCJ], [LT_LANG(GCJ)], [m4_ifdef([AC_PROG_GCJ], [m4_define([AC_PROG_GCJ], defn([AC_PROG_GCJ])[LT_LANG(GCJ)])]) m4_ifdef([A][M_PROG_GCJ], [m4_define([A][M_PROG_GCJ], defn([A][M_PROG_GCJ])[LT_LANG(GCJ)])]) m4_ifdef([LT_PROG_GCJ], [m4_define([LT_PROG_GCJ], defn([LT_PROG_GCJ])[LT_LANG(GCJ)])])])])]) AC_PROVIDE_IFELSE([AC_PROG_GO], [LT_LANG(GO)], [m4_define([AC_PROG_GO], defn([AC_PROG_GO])[LT_LANG(GO)])]) AC_PROVIDE_IFELSE([LT_PROG_RC], [LT_LANG(RC)], [m4_define([LT_PROG_RC], defn([LT_PROG_RC])[LT_LANG(RC)])]) ])# _LT_LANG_DEFAULT_CONFIG # Obsolete macros: AU_DEFUN([AC_LIBTOOL_CXX], [LT_LANG(C++)]) AU_DEFUN([AC_LIBTOOL_F77], [LT_LANG(Fortran 77)]) AU_DEFUN([AC_LIBTOOL_FC], [LT_LANG(Fortran)]) AU_DEFUN([AC_LIBTOOL_GCJ], [LT_LANG(Java)]) AU_DEFUN([AC_LIBTOOL_RC], [LT_LANG(Windows Resource)]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_CXX], []) dnl AC_DEFUN([AC_LIBTOOL_F77], []) dnl AC_DEFUN([AC_LIBTOOL_FC], []) dnl AC_DEFUN([AC_LIBTOOL_GCJ], []) dnl AC_DEFUN([AC_LIBTOOL_RC], []) # _LT_TAG_COMPILER # ---------------- m4_defun([_LT_TAG_COMPILER], [AC_REQUIRE([AC_PROG_CC])dnl _LT_DECL([LTCC], [CC], [1], [A C compiler])dnl _LT_DECL([LTCFLAGS], [CFLAGS], [1], [LTCC compiler flags])dnl _LT_TAGDECL([CC], [compiler], [1], [A language specific compiler])dnl _LT_TAGDECL([with_gcc], [GCC], [0], [Is the compiler the GNU compiler?])dnl # If no C compiler was specified, use CC. LTCC=${LTCC-"$CC"} # If no C compiler flags were specified, use CFLAGS. LTCFLAGS=${LTCFLAGS-"$CFLAGS"} # Allow CC to be a program name with arguments. compiler=$CC ])# _LT_TAG_COMPILER # _LT_COMPILER_BOILERPLATE # ------------------------ # Check for compiler boilerplate output or warnings with # the simple compiler test code. m4_defun([_LT_COMPILER_BOILERPLATE], [m4_require([_LT_DECL_SED])dnl ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" >conftest.$ac_ext eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_compiler_boilerplate=`cat conftest.err` $RM conftest* ])# _LT_COMPILER_BOILERPLATE # _LT_LINKER_BOILERPLATE # ---------------------- # Check for linker boilerplate output or warnings with # the simple link test code. m4_defun([_LT_LINKER_BOILERPLATE], [m4_require([_LT_DECL_SED])dnl ac_outfile=conftest.$ac_objext echo "$lt_simple_link_test_code" >conftest.$ac_ext eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_linker_boilerplate=`cat conftest.err` $RM -r conftest* ])# _LT_LINKER_BOILERPLATE # _LT_REQUIRED_DARWIN_CHECKS # ------------------------- m4_defun_once([_LT_REQUIRED_DARWIN_CHECKS],[ case $host_os in rhapsody* | darwin*) AC_CHECK_TOOL([DSYMUTIL], [dsymutil], [:]) AC_CHECK_TOOL([NMEDIT], [nmedit], [:]) AC_CHECK_TOOL([LIPO], [lipo], [:]) AC_CHECK_TOOL([OTOOL], [otool], [:]) AC_CHECK_TOOL([OTOOL64], [otool64], [:]) _LT_DECL([], [DSYMUTIL], [1], [Tool to manipulate archived DWARF debug symbol files on Mac OS X]) _LT_DECL([], [NMEDIT], [1], [Tool to change global to local symbols on Mac OS X]) _LT_DECL([], [LIPO], [1], [Tool to manipulate fat objects and archives on Mac OS X]) _LT_DECL([], [OTOOL], [1], [ldd/readelf like tool for Mach-O binaries on Mac OS X]) _LT_DECL([], [OTOOL64], [1], [ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4]) AC_CACHE_CHECK([for -single_module linker flag],[lt_cv_apple_cc_single_mod], [lt_cv_apple_cc_single_mod=no if test -z "$LT_MULTI_MODULE"; then # By default we will add the -single_module flag. You can override # by either setting the environment variable LT_MULTI_MODULE # non-empty at configure time, or by adding -multi_module to the # link flags. rm -rf libconftest.dylib* echo "int foo(void){return 1;}" > conftest.c echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c" >&AS_MESSAGE_LOG_FD $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c 2>conftest.err _lt_result=$? # If there is a non-empty error log, and "single_module" # appears in it, assume the flag caused a linker warning if test -s conftest.err && $GREP single_module conftest.err; then cat conftest.err >&AS_MESSAGE_LOG_FD # Otherwise, if the output was created with a 0 exit code from # the compiler, it worked. elif test -f libconftest.dylib && test 0 = "$_lt_result"; then lt_cv_apple_cc_single_mod=yes else cat conftest.err >&AS_MESSAGE_LOG_FD fi rm -rf libconftest.dylib* rm -f conftest.* fi]) AC_CACHE_CHECK([for -exported_symbols_list linker flag], [lt_cv_ld_exported_symbols_list], [lt_cv_ld_exported_symbols_list=no save_LDFLAGS=$LDFLAGS echo "_main" > conftest.sym LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], [lt_cv_ld_exported_symbols_list=yes], [lt_cv_ld_exported_symbols_list=no]) LDFLAGS=$save_LDFLAGS ]) AC_CACHE_CHECK([for -force_load linker flag],[lt_cv_ld_force_load], [lt_cv_ld_force_load=no cat > conftest.c << _LT_EOF int forced_loaded() { return 2;} _LT_EOF echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&AS_MESSAGE_LOG_FD $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&AS_MESSAGE_LOG_FD echo "$AR cr libconftest.a conftest.o" >&AS_MESSAGE_LOG_FD $AR cr libconftest.a conftest.o 2>&AS_MESSAGE_LOG_FD echo "$RANLIB libconftest.a" >&AS_MESSAGE_LOG_FD $RANLIB libconftest.a 2>&AS_MESSAGE_LOG_FD cat > conftest.c << _LT_EOF int main() { return 0;} _LT_EOF echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&AS_MESSAGE_LOG_FD $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err _lt_result=$? if test -s conftest.err && $GREP force_load conftest.err; then cat conftest.err >&AS_MESSAGE_LOG_FD elif test -f conftest && test 0 = "$_lt_result" && $GREP forced_load conftest >/dev/null 2>&1; then lt_cv_ld_force_load=yes else cat conftest.err >&AS_MESSAGE_LOG_FD fi rm -f conftest.err libconftest.a conftest conftest.c rm -rf conftest.dSYM ]) case $host_os in rhapsody* | darwin1.[[012]]) _lt_dar_allow_undefined='$wl-undefined ${wl}suppress' ;; darwin1.*) _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; darwin*) # darwin 5.x on # if running on 10.5 or later, the deployment target defaults # to the OS version, if on x86, and 10.4, the deployment # target defaults to 10.4. Don't you love it? case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in 10.0,*86*-darwin8*|10.0,*-darwin[[912]]*) _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; 10.[[012]][[,.]]*) _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; 10.*|11.*) _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; esac ;; esac if test yes = "$lt_cv_apple_cc_single_mod"; then _lt_dar_single_mod='$single_module' fi if test yes = "$lt_cv_ld_exported_symbols_list"; then _lt_dar_export_syms=' $wl-exported_symbols_list,$output_objdir/$libname-symbols.expsym' else _lt_dar_export_syms='~$NMEDIT -s $output_objdir/$libname-symbols.expsym $lib' fi if test : != "$DSYMUTIL" && test no = "$lt_cv_ld_force_load"; then _lt_dsymutil='~$DSYMUTIL $lib || :' else _lt_dsymutil= fi ;; esac ]) # _LT_DARWIN_LINKER_FEATURES([TAG]) # --------------------------------- # Checks for linker and compiler features on darwin m4_defun([_LT_DARWIN_LINKER_FEATURES], [ m4_require([_LT_REQUIRED_DARWIN_CHECKS]) _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_automatic, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported if test yes = "$lt_cv_ld_force_load"; then _LT_TAGVAR(whole_archive_flag_spec, $1)='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience $wl-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' m4_case([$1], [F77], [_LT_TAGVAR(compiler_needs_object, $1)=yes], [FC], [_LT_TAGVAR(compiler_needs_object, $1)=yes]) else _LT_TAGVAR(whole_archive_flag_spec, $1)='' fi _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(allow_undefined_flag, $1)=$_lt_dar_allow_undefined case $cc_basename in ifort*|nagfor*) _lt_dar_can_shared=yes ;; *) _lt_dar_can_shared=$GCC ;; esac if test yes = "$_lt_dar_can_shared"; then output_verbose_link_cmd=func_echo_all _LT_TAGVAR(archive_cmds, $1)="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dsymutil" _LT_TAGVAR(module_cmds, $1)="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dsymutil" _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil" _LT_TAGVAR(module_expsym_cmds, $1)="sed -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil" m4_if([$1], [CXX], [ if test yes != "$lt_cv_apple_cc_single_mod"; then _LT_TAGVAR(archive_cmds, $1)="\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dsymutil" _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dar_export_syms$_lt_dsymutil" fi ],[]) else _LT_TAGVAR(ld_shlibs, $1)=no fi ]) # _LT_SYS_MODULE_PATH_AIX([TAGNAME]) # ---------------------------------- # Links a minimal program and checks the executable # for the system default hardcoded library path. In most cases, # this is /usr/lib:/lib, but when the MPI compilers are used # the location of the communication and MPI libs are included too. # If we don't find anything, use the default library path according # to the aix ld manual. # Store the results from the different compilers for each TAGNAME. # Allow to override them for all tags through lt_cv_aix_libpath. m4_defun([_LT_SYS_MODULE_PATH_AIX], [m4_require([_LT_DECL_SED])dnl if test set = "${lt_cv_aix_libpath+set}"; then aix_libpath=$lt_cv_aix_libpath else AC_CACHE_VAL([_LT_TAGVAR([lt_cv_aix_libpath_], [$1])], [AC_LINK_IFELSE([AC_LANG_PROGRAM],[ lt_aix_libpath_sed='[ /Import File Strings/,/^$/ { /^0/ { s/^0 *\([^ ]*\) *$/\1/ p } }]' _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` # Check for a 64-bit object if we didn't find anything. if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi],[]) if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=/usr/lib:/lib fi ]) aix_libpath=$_LT_TAGVAR([lt_cv_aix_libpath_], [$1]) fi ])# _LT_SYS_MODULE_PATH_AIX # _LT_SHELL_INIT(ARG) # ------------------- m4_define([_LT_SHELL_INIT], [m4_divert_text([M4SH-INIT], [$1 ])])# _LT_SHELL_INIT # _LT_PROG_ECHO_BACKSLASH # ----------------------- # Find how we can fake an echo command that does not interpret backslash. # In particular, with Autoconf 2.60 or later we add some code to the start # of the generated configure script that will find a shell with a builtin # printf (that we can use as an echo command). m4_defun([_LT_PROG_ECHO_BACKSLASH], [ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO AC_MSG_CHECKING([how to print strings]) # Test print first, because it will be a builtin if present. if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \ test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then ECHO='print -r --' elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then ECHO='printf %s\n' else # Use this function as a fallback that always works. func_fallback_echo () { eval 'cat <<_LTECHO_EOF $[]1 _LTECHO_EOF' } ECHO='func_fallback_echo' fi # func_echo_all arg... # Invoke $ECHO with all args, space-separated. func_echo_all () { $ECHO "$*" } case $ECHO in printf*) AC_MSG_RESULT([printf]) ;; print*) AC_MSG_RESULT([print -r]) ;; *) AC_MSG_RESULT([cat]) ;; esac m4_ifdef([_AS_DETECT_SUGGESTED], [_AS_DETECT_SUGGESTED([ test -n "${ZSH_VERSION+set}${BASH_VERSION+set}" || ( ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO PATH=/empty FPATH=/empty; export PATH FPATH test "X`printf %s $ECHO`" = "X$ECHO" \ || test "X`print -r -- $ECHO`" = "X$ECHO" )])]) _LT_DECL([], [SHELL], [1], [Shell to use when invoking shell scripts]) _LT_DECL([], [ECHO], [1], [An echo program that protects backslashes]) ])# _LT_PROG_ECHO_BACKSLASH # _LT_WITH_SYSROOT # ---------------- AC_DEFUN([_LT_WITH_SYSROOT], [AC_MSG_CHECKING([for sysroot]) AC_ARG_WITH([sysroot], [AS_HELP_STRING([--with-sysroot@<:@=DIR@:>@], [Search for dependent libraries within DIR (or the compiler's sysroot if not specified).])], [], [with_sysroot=no]) dnl lt_sysroot will always be passed unquoted. We quote it here dnl in case the user passed a directory name. lt_sysroot= case $with_sysroot in #( yes) if test yes = "$GCC"; then lt_sysroot=`$CC --print-sysroot 2>/dev/null` fi ;; #( /*) lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"` ;; #( no|'') ;; #( *) AC_MSG_RESULT([$with_sysroot]) AC_MSG_ERROR([The sysroot must be an absolute path.]) ;; esac AC_MSG_RESULT([${lt_sysroot:-no}]) _LT_DECL([], [lt_sysroot], [0], [The root where to search for ]dnl [dependent libraries, and where our libraries should be installed.])]) # _LT_ENABLE_LOCK # --------------- m4_defun([_LT_ENABLE_LOCK], [AC_ARG_ENABLE([libtool-lock], [AS_HELP_STRING([--disable-libtool-lock], [avoid locking (might break parallel builds)])]) test no = "$enable_libtool_lock" || enable_libtool_lock=yes # Some flags need to be propagated to the compiler or linker for good # libtool support. case $host in ia64-*-hpux*) # Find out what ABI is being produced by ac_compile, and set mode # options accordingly. echo 'int i;' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then case `/usr/bin/file conftest.$ac_objext` in *ELF-32*) HPUX_IA64_MODE=32 ;; *ELF-64*) HPUX_IA64_MODE=64 ;; esac fi rm -rf conftest* ;; *-*-irix6*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. echo '[#]line '$LINENO' "configure"' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then if test yes = "$lt_cv_prog_gnu_ld"; then case `/usr/bin/file conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -melf32bsmip" ;; *N32*) LD="${LD-ld} -melf32bmipn32" ;; *64-bit*) LD="${LD-ld} -melf64bmip" ;; esac else case `/usr/bin/file conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -32" ;; *N32*) LD="${LD-ld} -n32" ;; *64-bit*) LD="${LD-ld} -64" ;; esac fi fi rm -rf conftest* ;; mips64*-*linux*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. echo '[#]line '$LINENO' "configure"' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then emul=elf case `/usr/bin/file conftest.$ac_objext` in *32-bit*) emul="${emul}32" ;; *64-bit*) emul="${emul}64" ;; esac case `/usr/bin/file conftest.$ac_objext` in *MSB*) emul="${emul}btsmip" ;; *LSB*) emul="${emul}ltsmip" ;; esac case `/usr/bin/file conftest.$ac_objext` in *N32*) emul="${emul}n32" ;; esac LD="${LD-ld} -m $emul" fi rm -rf conftest* ;; x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \ s390*-*linux*|s390*-*tpf*|sparc*-*linux*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. Note that the listed cases only cover the # situations where additional linker options are needed (such as when # doing 32-bit compilation for a host where ld defaults to 64-bit, or # vice versa); the common cases where no linker options are needed do # not appear in the list. echo 'int i;' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then case `/usr/bin/file conftest.o` in *32-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_i386_fbsd" ;; x86_64-*linux*) case `/usr/bin/file conftest.o` in *x86-64*) LD="${LD-ld} -m elf32_x86_64" ;; *) LD="${LD-ld} -m elf_i386" ;; esac ;; powerpc64le-*linux*) LD="${LD-ld} -m elf32lppclinux" ;; powerpc64-*linux*) LD="${LD-ld} -m elf32ppclinux" ;; s390x-*linux*) LD="${LD-ld} -m elf_s390" ;; sparc64-*linux*) LD="${LD-ld} -m elf32_sparc" ;; esac ;; *64-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_x86_64_fbsd" ;; x86_64-*linux*) LD="${LD-ld} -m elf_x86_64" ;; powerpcle-*linux*) LD="${LD-ld} -m elf64lppc" ;; powerpc-*linux*) LD="${LD-ld} -m elf64ppc" ;; s390*-*linux*|s390*-*tpf*) LD="${LD-ld} -m elf64_s390" ;; sparc*-*linux*) LD="${LD-ld} -m elf64_sparc" ;; esac ;; esac fi rm -rf conftest* ;; *-*-sco3.2v5*) # On SCO OpenServer 5, we need -belf to get full-featured binaries. SAVE_CFLAGS=$CFLAGS CFLAGS="$CFLAGS -belf" AC_CACHE_CHECK([whether the C compiler needs -belf], lt_cv_cc_needs_belf, [AC_LANG_PUSH(C) AC_LINK_IFELSE([AC_LANG_PROGRAM([[]],[[]])],[lt_cv_cc_needs_belf=yes],[lt_cv_cc_needs_belf=no]) AC_LANG_POP]) if test yes != "$lt_cv_cc_needs_belf"; then # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf CFLAGS=$SAVE_CFLAGS fi ;; *-*solaris*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. echo 'int i;' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then case `/usr/bin/file conftest.o` in *64-bit*) case $lt_cv_prog_gnu_ld in yes*) case $host in i?86-*-solaris*|x86_64-*-solaris*) LD="${LD-ld} -m elf_x86_64" ;; sparc*-*-solaris*) LD="${LD-ld} -m elf64_sparc" ;; esac # GNU ld 2.21 introduced _sol2 emulations. Use them if available. if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then LD=${LD-ld}_sol2 fi ;; *) if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then LD="${LD-ld} -64" fi ;; esac ;; esac fi rm -rf conftest* ;; esac need_locks=$enable_libtool_lock ])# _LT_ENABLE_LOCK # _LT_PROG_AR # ----------- m4_defun([_LT_PROG_AR], [AC_CHECK_TOOLS(AR, [ar], false) : ${AR=ar} : ${AR_FLAGS=cr} _LT_DECL([], [AR], [1], [The archiver]) _LT_DECL([], [AR_FLAGS], [1], [Flags to create an archive]) AC_CACHE_CHECK([for archiver @FILE support], [lt_cv_ar_at_file], [lt_cv_ar_at_file=no AC_COMPILE_IFELSE([AC_LANG_PROGRAM], [echo conftest.$ac_objext > conftest.lst lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&AS_MESSAGE_LOG_FD' AC_TRY_EVAL([lt_ar_try]) if test 0 -eq "$ac_status"; then # Ensure the archiver fails upon bogus file names. rm -f conftest.$ac_objext libconftest.a AC_TRY_EVAL([lt_ar_try]) if test 0 -ne "$ac_status"; then lt_cv_ar_at_file=@ fi fi rm -f conftest.* libconftest.a ]) ]) if test no = "$lt_cv_ar_at_file"; then archiver_list_spec= else archiver_list_spec=$lt_cv_ar_at_file fi _LT_DECL([], [archiver_list_spec], [1], [How to feed a file listing to the archiver]) ])# _LT_PROG_AR # _LT_CMD_OLD_ARCHIVE # ------------------- m4_defun([_LT_CMD_OLD_ARCHIVE], [_LT_PROG_AR AC_CHECK_TOOL(STRIP, strip, :) test -z "$STRIP" && STRIP=: _LT_DECL([], [STRIP], [1], [A symbol stripping program]) AC_CHECK_TOOL(RANLIB, ranlib, :) test -z "$RANLIB" && RANLIB=: _LT_DECL([], [RANLIB], [1], [Commands used to install an old-style archive]) # Determine commands to create old-style static archives. old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs' old_postinstall_cmds='chmod 644 $oldlib' old_postuninstall_cmds= if test -n "$RANLIB"; then case $host_os in bitrig* | openbsd*) old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib" ;; *) old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib" ;; esac old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib" fi case $host_os in darwin*) lock_old_archive_extraction=yes ;; *) lock_old_archive_extraction=no ;; esac _LT_DECL([], [old_postinstall_cmds], [2]) _LT_DECL([], [old_postuninstall_cmds], [2]) _LT_TAGDECL([], [old_archive_cmds], [2], [Commands used to build an old-style archive]) _LT_DECL([], [lock_old_archive_extraction], [0], [Whether to use a lock for old archive extraction]) ])# _LT_CMD_OLD_ARCHIVE # _LT_COMPILER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, # [OUTPUT-FILE], [ACTION-SUCCESS], [ACTION-FAILURE]) # ---------------------------------------------------------------- # Check whether the given compiler option works AC_DEFUN([_LT_COMPILER_OPTION], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_SED])dnl AC_CACHE_CHECK([$1], [$2], [$2=no m4_if([$4], , [ac_outfile=conftest.$ac_objext], [ac_outfile=$4]) echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="$3" ## exclude from sc_useless_quotes_in_assignment # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. # The option is referenced via a variable to avoid confusing sed. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&AS_MESSAGE_LOG_FD echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then $2=yes fi fi $RM conftest* ]) if test yes = "[$]$2"; then m4_if([$5], , :, [$5]) else m4_if([$6], , :, [$6]) fi ])# _LT_COMPILER_OPTION # Old name: AU_ALIAS([AC_LIBTOOL_COMPILER_OPTION], [_LT_COMPILER_OPTION]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_COMPILER_OPTION], []) # _LT_LINKER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, # [ACTION-SUCCESS], [ACTION-FAILURE]) # ---------------------------------------------------- # Check whether the given linker option works AC_DEFUN([_LT_LINKER_OPTION], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_SED])dnl AC_CACHE_CHECK([$1], [$2], [$2=no save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS $3" echo "$lt_simple_link_test_code" > conftest.$ac_ext if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then # The linker can only warn and ignore the option if not recognized # So say no if there are warnings if test -s conftest.err; then # Append any errors to the config.log. cat conftest.err 1>&AS_MESSAGE_LOG_FD $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if diff conftest.exp conftest.er2 >/dev/null; then $2=yes fi else $2=yes fi fi $RM -r conftest* LDFLAGS=$save_LDFLAGS ]) if test yes = "[$]$2"; then m4_if([$4], , :, [$4]) else m4_if([$5], , :, [$5]) fi ])# _LT_LINKER_OPTION # Old name: AU_ALIAS([AC_LIBTOOL_LINKER_OPTION], [_LT_LINKER_OPTION]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_LINKER_OPTION], []) # LT_CMD_MAX_LEN #--------------- AC_DEFUN([LT_CMD_MAX_LEN], [AC_REQUIRE([AC_CANONICAL_HOST])dnl # find the maximum length of command line arguments AC_MSG_CHECKING([the maximum length of command line arguments]) AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl i=0 teststring=ABCD case $build_os in msdosdjgpp*) # On DJGPP, this test can blow up pretty badly due to problems in libc # (any single argument exceeding 2000 bytes causes a buffer overrun # during glob expansion). Even if it were fixed, the result of this # check would be larger than it should be. lt_cv_sys_max_cmd_len=12288; # 12K is about right ;; gnu*) # Under GNU Hurd, this test is not required because there is # no limit to the length of command line arguments. # Libtool will interpret -1 as no limit whatsoever lt_cv_sys_max_cmd_len=-1; ;; cygwin* | mingw* | cegcc*) # On Win9x/ME, this test blows up -- it succeeds, but takes # about 5 minutes as the teststring grows exponentially. # Worse, since 9x/ME are not pre-emptively multitasking, # you end up with a "frozen" computer, even though with patience # the test eventually succeeds (with a max line length of 256k). # Instead, let's just punt: use the minimum linelength reported by # all of the supported platforms: 8192 (on NT/2K/XP). lt_cv_sys_max_cmd_len=8192; ;; mint*) # On MiNT this can take a long time and run out of memory. lt_cv_sys_max_cmd_len=8192; ;; amigaos*) # On AmigaOS with pdksh, this test takes hours, literally. # So we just punt and use a minimum line length of 8192. lt_cv_sys_max_cmd_len=8192; ;; bitrig* | darwin* | dragonfly* | freebsd* | netbsd* | openbsd*) # This has been around since 386BSD, at least. Likely further. if test -x /sbin/sysctl; then lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` elif test -x /usr/sbin/sysctl; then lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` else lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs fi # And add a safety zone lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` ;; interix*) # We know the value 262144 and hardcode it with a safety zone (like BSD) lt_cv_sys_max_cmd_len=196608 ;; os2*) # The test takes a long time on OS/2. lt_cv_sys_max_cmd_len=8192 ;; osf*) # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not # nice to cause kernel panics so lets avoid the loop below. # First set a reasonable default. lt_cv_sys_max_cmd_len=16384 # if test -x /sbin/sysconfig; then case `/sbin/sysconfig -q proc exec_disable_arg_limit` in *1*) lt_cv_sys_max_cmd_len=-1 ;; esac fi ;; sco3.2v5*) lt_cv_sys_max_cmd_len=102400 ;; sysv5* | sco5v6* | sysv4.2uw2*) kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` if test -n "$kargmax"; then lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[[ ]]//'` else lt_cv_sys_max_cmd_len=32768 fi ;; *) lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null` if test -n "$lt_cv_sys_max_cmd_len" && \ test undefined != "$lt_cv_sys_max_cmd_len"; then lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` else # Make teststring a little bigger before we do anything with it. # a 1K string should be a reasonable start. for i in 1 2 3 4 5 6 7 8; do teststring=$teststring$teststring done SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} # If test is not a shell built-in, we'll probably end up computing a # maximum length that is only half of the actual maximum length, but # we can't tell. while { test X`env echo "$teststring$teststring" 2>/dev/null` \ = "X$teststring$teststring"; } >/dev/null 2>&1 && test 17 != "$i" # 1/2 MB should be enough do i=`expr $i + 1` teststring=$teststring$teststring done # Only check the string length outside the loop. lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1` teststring= # Add a significant safety factor because C++ compilers can tack on # massive amounts of additional arguments before passing them to the # linker. It appears as though 1/2 is a usable value. lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` fi ;; esac ]) if test -n "$lt_cv_sys_max_cmd_len"; then AC_MSG_RESULT($lt_cv_sys_max_cmd_len) else AC_MSG_RESULT(none) fi max_cmd_len=$lt_cv_sys_max_cmd_len _LT_DECL([], [max_cmd_len], [0], [What is the maximum length of a command?]) ])# LT_CMD_MAX_LEN # Old name: AU_ALIAS([AC_LIBTOOL_SYS_MAX_CMD_LEN], [LT_CMD_MAX_LEN]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN], []) # _LT_HEADER_DLFCN # ---------------- m4_defun([_LT_HEADER_DLFCN], [AC_CHECK_HEADERS([dlfcn.h], [], [], [AC_INCLUDES_DEFAULT])dnl ])# _LT_HEADER_DLFCN # _LT_TRY_DLOPEN_SELF (ACTION-IF-TRUE, ACTION-IF-TRUE-W-USCORE, # ACTION-IF-FALSE, ACTION-IF-CROSS-COMPILING) # ---------------------------------------------------------------- m4_defun([_LT_TRY_DLOPEN_SELF], [m4_require([_LT_HEADER_DLFCN])dnl if test yes = "$cross_compiling"; then : [$4] else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF [#line $LINENO "configure" #include "confdefs.h" #if HAVE_DLFCN_H #include #endif #include #ifdef RTLD_GLOBAL # define LT_DLGLOBAL RTLD_GLOBAL #else # ifdef DL_GLOBAL # define LT_DLGLOBAL DL_GLOBAL # else # define LT_DLGLOBAL 0 # endif #endif /* We may have to define LT_DLLAZY_OR_NOW in the command line if we find out it does not work in some platform. */ #ifndef LT_DLLAZY_OR_NOW # ifdef RTLD_LAZY # define LT_DLLAZY_OR_NOW RTLD_LAZY # else # ifdef DL_LAZY # define LT_DLLAZY_OR_NOW DL_LAZY # else # ifdef RTLD_NOW # define LT_DLLAZY_OR_NOW RTLD_NOW # else # ifdef DL_NOW # define LT_DLLAZY_OR_NOW DL_NOW # else # define LT_DLLAZY_OR_NOW 0 # endif # endif # endif # endif #endif /* When -fvisibility=hidden is used, assume the code has been annotated correspondingly for the symbols needed. */ #if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) int fnord () __attribute__((visibility("default"))); #endif int fnord () { return 42; } int main () { void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); int status = $lt_dlunknown; if (self) { if (dlsym (self,"fnord")) status = $lt_dlno_uscore; else { if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; else puts (dlerror ()); } /* dlclose (self); */ } else puts (dlerror ()); return status; }] _LT_EOF if AC_TRY_EVAL(ac_link) && test -s "conftest$ac_exeext" 2>/dev/null; then (./conftest; exit; ) >&AS_MESSAGE_LOG_FD 2>/dev/null lt_status=$? case x$lt_status in x$lt_dlno_uscore) $1 ;; x$lt_dlneed_uscore) $2 ;; x$lt_dlunknown|x*) $3 ;; esac else : # compilation failed $3 fi fi rm -fr conftest* ])# _LT_TRY_DLOPEN_SELF # LT_SYS_DLOPEN_SELF # ------------------ AC_DEFUN([LT_SYS_DLOPEN_SELF], [m4_require([_LT_HEADER_DLFCN])dnl if test yes != "$enable_dlopen"; then enable_dlopen=unknown enable_dlopen_self=unknown enable_dlopen_self_static=unknown else lt_cv_dlopen=no lt_cv_dlopen_libs= case $host_os in beos*) lt_cv_dlopen=load_add_on lt_cv_dlopen_libs= lt_cv_dlopen_self=yes ;; mingw* | pw32* | cegcc*) lt_cv_dlopen=LoadLibrary lt_cv_dlopen_libs= ;; cygwin*) lt_cv_dlopen=dlopen lt_cv_dlopen_libs= ;; darwin*) # if libdl is installed we need to link against it AC_CHECK_LIB([dl], [dlopen], [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl],[ lt_cv_dlopen=dyld lt_cv_dlopen_libs= lt_cv_dlopen_self=yes ]) ;; tpf*) # Don't try to run any link tests for TPF. We know it's impossible # because TPF is a cross-compiler, and we know how we open DSOs. lt_cv_dlopen=dlopen lt_cv_dlopen_libs= lt_cv_dlopen_self=no ;; *) AC_CHECK_FUNC([shl_load], [lt_cv_dlopen=shl_load], [AC_CHECK_LIB([dld], [shl_load], [lt_cv_dlopen=shl_load lt_cv_dlopen_libs=-ldld], [AC_CHECK_FUNC([dlopen], [lt_cv_dlopen=dlopen], [AC_CHECK_LIB([dl], [dlopen], [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl], [AC_CHECK_LIB([svld], [dlopen], [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-lsvld], [AC_CHECK_LIB([dld], [dld_link], [lt_cv_dlopen=dld_link lt_cv_dlopen_libs=-ldld]) ]) ]) ]) ]) ]) ;; esac if test no = "$lt_cv_dlopen"; then enable_dlopen=no else enable_dlopen=yes fi case $lt_cv_dlopen in dlopen) save_CPPFLAGS=$CPPFLAGS test yes = "$ac_cv_header_dlfcn_h" && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" save_LDFLAGS=$LDFLAGS wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" save_LIBS=$LIBS LIBS="$lt_cv_dlopen_libs $LIBS" AC_CACHE_CHECK([whether a program can dlopen itself], lt_cv_dlopen_self, [dnl _LT_TRY_DLOPEN_SELF( lt_cv_dlopen_self=yes, lt_cv_dlopen_self=yes, lt_cv_dlopen_self=no, lt_cv_dlopen_self=cross) ]) if test yes = "$lt_cv_dlopen_self"; then wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" AC_CACHE_CHECK([whether a statically linked program can dlopen itself], lt_cv_dlopen_self_static, [dnl _LT_TRY_DLOPEN_SELF( lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=no, lt_cv_dlopen_self_static=cross) ]) fi CPPFLAGS=$save_CPPFLAGS LDFLAGS=$save_LDFLAGS LIBS=$save_LIBS ;; esac case $lt_cv_dlopen_self in yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; *) enable_dlopen_self=unknown ;; esac case $lt_cv_dlopen_self_static in yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; *) enable_dlopen_self_static=unknown ;; esac fi _LT_DECL([dlopen_support], [enable_dlopen], [0], [Whether dlopen is supported]) _LT_DECL([dlopen_self], [enable_dlopen_self], [0], [Whether dlopen of programs is supported]) _LT_DECL([dlopen_self_static], [enable_dlopen_self_static], [0], [Whether dlopen of statically linked programs is supported]) ])# LT_SYS_DLOPEN_SELF # Old name: AU_ALIAS([AC_LIBTOOL_DLOPEN_SELF], [LT_SYS_DLOPEN_SELF]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_DLOPEN_SELF], []) # _LT_COMPILER_C_O([TAGNAME]) # --------------------------- # Check to see if options -c and -o are simultaneously supported by compiler. # This macro does not hard code the compiler like AC_PROG_CC_C_O. m4_defun([_LT_COMPILER_C_O], [m4_require([_LT_DECL_SED])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_TAG_COMPILER])dnl AC_CACHE_CHECK([if $compiler supports -c -o file.$ac_objext], [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)], [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=no $RM -r conftest 2>/dev/null mkdir conftest cd conftest mkdir out echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-o out/conftest2.$ac_objext" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&AS_MESSAGE_LOG_FD echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes fi fi chmod u+w . 2>&AS_MESSAGE_LOG_FD $RM conftest* # SGI C++ compiler will create directory out/ii_files/ for # template instantiation test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files $RM out/* && rmdir out cd .. $RM -r conftest $RM conftest* ]) _LT_TAGDECL([compiler_c_o], [lt_cv_prog_compiler_c_o], [1], [Does compiler simultaneously support -c and -o options?]) ])# _LT_COMPILER_C_O # _LT_COMPILER_FILE_LOCKS([TAGNAME]) # ---------------------------------- # Check to see if we can do hard links to lock some files if needed m4_defun([_LT_COMPILER_FILE_LOCKS], [m4_require([_LT_ENABLE_LOCK])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl _LT_COMPILER_C_O([$1]) hard_links=nottested if test no = "$_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)" && test no != "$need_locks"; then # do not overwrite the value of need_locks provided by the user AC_MSG_CHECKING([if we can lock with hard links]) hard_links=yes $RM conftest* ln conftest.a conftest.b 2>/dev/null && hard_links=no touch conftest.a ln conftest.a conftest.b 2>&5 || hard_links=no ln conftest.a conftest.b 2>/dev/null && hard_links=no AC_MSG_RESULT([$hard_links]) if test no = "$hard_links"; then AC_MSG_WARN(['$CC' does not support '-c -o', so 'make -j' may be unsafe]) need_locks=warn fi else need_locks=no fi _LT_DECL([], [need_locks], [1], [Must we lock files when doing compilation?]) ])# _LT_COMPILER_FILE_LOCKS # _LT_CHECK_OBJDIR # ---------------- m4_defun([_LT_CHECK_OBJDIR], [AC_CACHE_CHECK([for objdir], [lt_cv_objdir], [rm -f .libs 2>/dev/null mkdir .libs 2>/dev/null if test -d .libs; then lt_cv_objdir=.libs else # MS-DOS does not allow filenames that begin with a dot. lt_cv_objdir=_libs fi rmdir .libs 2>/dev/null]) objdir=$lt_cv_objdir _LT_DECL([], [objdir], [0], [The name of the directory that contains temporary libtool files])dnl m4_pattern_allow([LT_OBJDIR])dnl AC_DEFINE_UNQUOTED([LT_OBJDIR], "$lt_cv_objdir/", [Define to the sub-directory where libtool stores uninstalled libraries.]) ])# _LT_CHECK_OBJDIR # _LT_LINKER_HARDCODE_LIBPATH([TAGNAME]) # -------------------------------------- # Check hardcoding attributes. m4_defun([_LT_LINKER_HARDCODE_LIBPATH], [AC_MSG_CHECKING([how to hardcode library paths into programs]) _LT_TAGVAR(hardcode_action, $1)= if test -n "$_LT_TAGVAR(hardcode_libdir_flag_spec, $1)" || test -n "$_LT_TAGVAR(runpath_var, $1)" || test yes = "$_LT_TAGVAR(hardcode_automatic, $1)"; then # We can hardcode non-existent directories. if test no != "$_LT_TAGVAR(hardcode_direct, $1)" && # If the only mechanism to avoid hardcoding is shlibpath_var, we # have to relink, otherwise we might link with an installed library # when we should be linking with a yet-to-be-installed one ## test no != "$_LT_TAGVAR(hardcode_shlibpath_var, $1)" && test no != "$_LT_TAGVAR(hardcode_minus_L, $1)"; then # Linking always hardcodes the temporary library directory. _LT_TAGVAR(hardcode_action, $1)=relink else # We can link without hardcoding, and we can hardcode nonexisting dirs. _LT_TAGVAR(hardcode_action, $1)=immediate fi else # We cannot hardcode anything, or else we can only hardcode existing # directories. _LT_TAGVAR(hardcode_action, $1)=unsupported fi AC_MSG_RESULT([$_LT_TAGVAR(hardcode_action, $1)]) if test relink = "$_LT_TAGVAR(hardcode_action, $1)" || test yes = "$_LT_TAGVAR(inherit_rpath, $1)"; then # Fast installation is not supported enable_fast_install=no elif test yes = "$shlibpath_overrides_runpath" || test no = "$enable_shared"; then # Fast installation is not necessary enable_fast_install=needless fi _LT_TAGDECL([], [hardcode_action], [0], [How to hardcode a shared library path into an executable]) ])# _LT_LINKER_HARDCODE_LIBPATH # _LT_CMD_STRIPLIB # ---------------- m4_defun([_LT_CMD_STRIPLIB], [m4_require([_LT_DECL_EGREP]) striplib= old_striplib= AC_MSG_CHECKING([whether stripping libraries is possible]) if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" test -z "$striplib" && striplib="$STRIP --strip-unneeded" AC_MSG_RESULT([yes]) else # FIXME - insert some real tests, host_os isn't really good enough case $host_os in darwin*) if test -n "$STRIP"; then striplib="$STRIP -x" old_striplib="$STRIP -S" AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) fi ;; *) AC_MSG_RESULT([no]) ;; esac fi _LT_DECL([], [old_striplib], [1], [Commands to strip libraries]) _LT_DECL([], [striplib], [1]) ])# _LT_CMD_STRIPLIB # _LT_PREPARE_MUNGE_PATH_LIST # --------------------------- # Make sure func_munge_path_list() is defined correctly. m4_defun([_LT_PREPARE_MUNGE_PATH_LIST], [[# func_munge_path_list VARIABLE PATH # ----------------------------------- # VARIABLE is name of variable containing _space_ separated list of # directories to be munged by the contents of PATH, which is string # having a format: # "DIR[:DIR]:" # string "DIR[ DIR]" will be prepended to VARIABLE # ":DIR[:DIR]" # string "DIR[ DIR]" will be appended to VARIABLE # "DIRP[:DIRP]::[DIRA:]DIRA" # string "DIRP[ DIRP]" will be prepended to VARIABLE and string # "DIRA[ DIRA]" will be appended to VARIABLE # "DIR[:DIR]" # VARIABLE will be replaced by "DIR[ DIR]" func_munge_path_list () { case x@S|@2 in x) ;; *:) eval @S|@1=\"`$ECHO @S|@2 | $SED 's/:/ /g'` \@S|@@S|@1\" ;; x:*) eval @S|@1=\"\@S|@@S|@1 `$ECHO @S|@2 | $SED 's/:/ /g'`\" ;; *::*) eval @S|@1=\"\@S|@@S|@1\ `$ECHO @S|@2 | $SED -e 's/.*:://' -e 's/:/ /g'`\" eval @S|@1=\"`$ECHO @S|@2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \@S|@@S|@1\" ;; *) eval @S|@1=\"`$ECHO @S|@2 | $SED 's/:/ /g'`\" ;; esac } ]])# _LT_PREPARE_PATH_LIST # _LT_SYS_DYNAMIC_LINKER([TAG]) # ----------------------------- # PORTME Fill in your ld.so characteristics m4_defun([_LT_SYS_DYNAMIC_LINKER], [AC_REQUIRE([AC_CANONICAL_HOST])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_OBJDUMP])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_CHECK_SHELL_FEATURES])dnl m4_require([_LT_PREPARE_MUNGE_PATH_LIST])dnl AC_MSG_CHECKING([dynamic linker characteristics]) m4_if([$1], [], [ if test yes = "$GCC"; then case $host_os in darwin*) lt_awk_arg='/^libraries:/,/LR/' ;; *) lt_awk_arg='/^libraries:/' ;; esac case $host_os in mingw* | cegcc*) lt_sed_strip_eq='s|=\([[A-Za-z]]:\)|\1|g' ;; *) lt_sed_strip_eq='s|=/|/|g' ;; esac lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq` case $lt_search_path_spec in *\;*) # if the path contains ";" then we assume it to be the separator # otherwise default to the standard path separator (i.e. ":") - it is # assumed that no part of a normal pathname contains ";" but that should # okay in the real world where ";" in dirpaths is itself problematic. lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'` ;; *) lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"` ;; esac # Ok, now we have the path, separated by spaces, we can step through it # and add multilib dir if necessary... lt_tmp_lt_search_path_spec= lt_multi_os_dir=/`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` # ...but if some path component already ends with the multilib dir we assume # that all is fine and trust -print-search-dirs as is (GCC 4.2? or newer). case "$lt_multi_os_dir; $lt_search_path_spec " in "/; "* | "/.; "* | "/./; "* | *"$lt_multi_os_dir "* | *"$lt_multi_os_dir/ "*) lt_multi_os_dir= ;; esac for lt_sys_path in $lt_search_path_spec; do if test -d "$lt_sys_path$lt_multi_os_dir"; then lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path$lt_multi_os_dir" elif test -n "$lt_multi_os_dir"; then test -d "$lt_sys_path" && \ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path" fi done lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk ' BEGIN {RS = " "; FS = "/|\n";} { lt_foo = ""; lt_count = 0; for (lt_i = NF; lt_i > 0; lt_i--) { if ($lt_i != "" && $lt_i != ".") { if ($lt_i == "..") { lt_count++; } else { if (lt_count == 0) { lt_foo = "/" $lt_i lt_foo; } else { lt_count--; } } } } if (lt_foo != "") { lt_freq[[lt_foo]]++; } if (lt_freq[[lt_foo]] == 1) { print lt_foo; } }'` # AWK program above erroneously prepends '/' to C:/dos/paths # for these hosts. case $host_os in mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\ $SED 's|/\([[A-Za-z]]:\)|\1|g'` ;; esac sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP` else sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" fi]) library_names_spec= libname_spec='lib$name' soname_spec= shrext_cmds=.so postinstall_cmds= postuninstall_cmds= finish_cmds= finish_eval= shlibpath_var= shlibpath_overrides_runpath=unknown version_type=none dynamic_linker="$host_os ld.so" sys_lib_dlsearch_path_spec="/lib /usr/lib" need_lib_prefix=unknown hardcode_into_libs=no # when you set need_version to no, make sure it does not cause -set_version # flags to be left without arguments need_version=unknown AC_ARG_VAR([LT_SYS_LIBRARY_PATH], [User-defined run-time library search path.]) case $host_os in aix3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname.a' shlibpath_var=LIBPATH # AIX 3 has no versioning support, so we append a major version to the name. soname_spec='$libname$release$shared_ext$major' ;; aix[[4-9]]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no hardcode_into_libs=yes if test ia64 = "$host_cpu"; then # AIX 5 supports IA64 library_names_spec='$libname$release$shared_ext$major $libname$release$shared_ext$versuffix $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH else # With GCC up to 2.95.x, collect2 would create an import file # for dependence libraries. The import file would start with # the line '#! .'. This would cause the generated library to # depend on '.', always an invalid library. This was fixed in # development snapshots of GCC prior to 3.0. case $host_os in aix4 | aix4.[[01]] | aix4.[[01]].*) if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' echo ' yes ' echo '#endif'; } | $CC -E - | $GREP yes > /dev/null; then : else can_build_shared=no fi ;; esac # Using Import Files as archive members, it is possible to support # filename-based versioning of shared library archives on AIX. While # this would work for both with and without runtime linking, it will # prevent static linking of such archives. So we do filename-based # shared library versioning with .so extension only, which is used # when both runtime linking and shared linking is enabled. # Unfortunately, runtime linking may impact performance, so we do # not want this to be the default eventually. Also, we use the # versioned .so libs for executables only if there is the -brtl # linker flag in LDFLAGS as well, or --with-aix-soname=svr4 only. # To allow for filename-based versioning support, we need to create # libNAME.so.V as an archive file, containing: # *) an Import File, referring to the versioned filename of the # archive as well as the shared archive member, telling the # bitwidth (32 or 64) of that shared object, and providing the # list of exported symbols of that shared object, eventually # decorated with the 'weak' keyword # *) the shared object with the F_LOADONLY flag set, to really avoid # it being seen by the linker. # At run time we better use the real file rather than another symlink, # but for link time we create the symlink libNAME.so -> libNAME.so.V case $with_aix_soname,$aix_use_runtimelinking in # AIX (on Power*) has no versioning support, so currently we cannot hardcode correct # soname into executable. Probably we can add versioning support to # collect2, so additional links can be useful in future. aix,yes) # traditional libtool dynamic_linker='AIX unversionable lib.so' # If using run time linking (on AIX 4.2 or later) use lib.so # instead of lib.a to let people know that these are not # typical AIX shared libraries. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' ;; aix,no) # traditional AIX only dynamic_linker='AIX lib.a[(]lib.so.V[)]' # We preserve .a as extension for shared libraries through AIX4.2 # and later when we are not doing run time linking. library_names_spec='$libname$release.a $libname.a' soname_spec='$libname$release$shared_ext$major' ;; svr4,*) # full svr4 only dynamic_linker="AIX lib.so.V[(]$shared_archive_member_spec.o[)]" library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' # We do not specify a path in Import Files, so LIBPATH fires. shlibpath_overrides_runpath=yes ;; *,yes) # both, prefer svr4 dynamic_linker="AIX lib.so.V[(]$shared_archive_member_spec.o[)], lib.a[(]lib.so.V[)]" library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' # unpreferred sharedlib libNAME.a needs extra handling postinstall_cmds='test -n "$linkname" || linkname="$realname"~func_stripname "" ".so" "$linkname"~$install_shared_prog "$dir/$func_stripname_result.$libext" "$destdir/$func_stripname_result.$libext"~test -z "$tstripme" || test -z "$striplib" || $striplib "$destdir/$func_stripname_result.$libext"' postuninstall_cmds='for n in $library_names $old_library; do :; done~func_stripname "" ".so" "$n"~test "$func_stripname_result" = "$n" || func_append rmfiles " $odir/$func_stripname_result.$libext"' # We do not specify a path in Import Files, so LIBPATH fires. shlibpath_overrides_runpath=yes ;; *,no) # both, prefer aix dynamic_linker="AIX lib.a[(]lib.so.V[)], lib.so.V[(]$shared_archive_member_spec.o[)]" library_names_spec='$libname$release.a $libname.a' soname_spec='$libname$release$shared_ext$major' # unpreferred sharedlib libNAME.so.V and symlink libNAME.so need extra handling postinstall_cmds='test -z "$dlname" || $install_shared_prog $dir/$dlname $destdir/$dlname~test -z "$tstripme" || test -z "$striplib" || $striplib $destdir/$dlname~test -n "$linkname" || linkname=$realname~func_stripname "" ".a" "$linkname"~(cd "$destdir" && $LN_S -f $dlname $func_stripname_result.so)' postuninstall_cmds='test -z "$dlname" || func_append rmfiles " $odir/$dlname"~for n in $old_library $library_names; do :; done~func_stripname "" ".a" "$n"~func_append rmfiles " $odir/$func_stripname_result.so"' ;; esac shlibpath_var=LIBPATH fi ;; amigaos*) case $host_cpu in powerpc) # Since July 2007 AmigaOS4 officially supports .so libraries. # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' ;; m68k) library_names_spec='$libname.ixlibrary $libname.a' # Create ${libname}_ixlibrary.a entries in /sys/libs. finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([[^/]]*\)\.ixlibrary$%\1%'\''`; $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' ;; esac ;; beos*) library_names_spec='$libname$shared_ext' dynamic_linker="$host_os ld.so" shlibpath_var=LIBRARY_PATH ;; bsdi[[45]]*) version_type=linux # correct to gnu/linux during the next big refactor need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" # the default ld.so.conf also contains /usr/contrib/lib and # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow # libtool to hard-code these into programs ;; cygwin* | mingw* | pw32* | cegcc*) version_type=windows shrext_cmds=.dll need_version=no need_lib_prefix=no case $GCC,$cc_basename in yes,*) # gcc library_names_spec='$libname.dll.a' # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes case $host_os in cygwin*) # Cygwin DLLs use 'cyg' prefix rather than 'lib' soname_spec='`echo $libname | sed -e 's/^lib/cyg/'``echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' m4_if([$1], [],[ sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api"]) ;; mingw* | cegcc*) # MinGW DLLs use traditional 'lib' prefix soname_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' ;; pw32*) # pw32 DLLs use 'pw' prefix rather than 'lib' library_names_spec='`echo $libname | sed -e 's/^lib/pw/'``echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' ;; esac dynamic_linker='Win32 ld.exe' ;; *,cl*) # Native MSVC libname_spec='$name' soname_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' library_names_spec='$libname.dll.lib' case $build_os in mingw*) sys_lib_search_path_spec= lt_save_ifs=$IFS IFS=';' for lt_path in $LIB do IFS=$lt_save_ifs # Let DOS variable expansion print the short 8.3 style file name. lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" done IFS=$lt_save_ifs # Convert to MSYS style. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([[a-zA-Z]]\\):| /\\1|g' -e 's|^ ||'` ;; cygwin*) # Convert to unix form, then to dos form, then back to unix form # but this time dos style (no spaces!) so that the unix form looks # like /cygdrive/c/PROGRA~1:/cygdr... sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` ;; *) sys_lib_search_path_spec=$LIB if $ECHO "$sys_lib_search_path_spec" | [$GREP ';[c-zC-Z]:/' >/dev/null]; then # It is most probably a Windows format PATH. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` else sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` fi # FIXME: find the short name or the path components, as spaces are # common. (e.g. "Program Files" -> "PROGRA~1") ;; esac # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes dynamic_linker='Win32 link.exe' ;; *) # Assume MSVC wrapper library_names_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext $libname.lib' dynamic_linker='Win32 ld.exe' ;; esac # FIXME: first we should search . and the directory the executable is in shlibpath_var=PATH ;; darwin* | rhapsody*) dynamic_linker="$host_os dyld" version_type=darwin need_lib_prefix=no need_version=no library_names_spec='$libname$release$major$shared_ext $libname$shared_ext' soname_spec='$libname$release$major$shared_ext' shlibpath_overrides_runpath=yes shlibpath_var=DYLD_LIBRARY_PATH shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' m4_if([$1], [],[ sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"]) sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' ;; dgux*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH ;; freebsd* | dragonfly*) # DragonFly does not have aout. When/if they implement a new # versioning mechanism, adjust this. if test -x /usr/bin/objformat; then objformat=`/usr/bin/objformat` else case $host_os in freebsd[[23]].*) objformat=aout ;; *) objformat=elf ;; esac fi version_type=freebsd-$objformat case $version_type in freebsd-elf*) library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' need_version=no need_lib_prefix=no ;; freebsd-*) library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' need_version=yes ;; esac shlibpath_var=LD_LIBRARY_PATH case $host_os in freebsd2.*) shlibpath_overrides_runpath=yes ;; freebsd3.[[01]]* | freebsdelf3.[[01]]*) shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; freebsd3.[[2-9]]* | freebsdelf3.[[2-9]]* | \ freebsd4.[[0-5]] | freebsdelf4.[[0-5]] | freebsd4.1.1 | freebsdelf4.1.1) shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; *) # from 4.6 on, and DragonFly shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; esac ;; haiku*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no dynamic_linker="$host_os runtime_loader" library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LIBRARY_PATH shlibpath_overrides_runpath=no sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' hardcode_into_libs=yes ;; hpux9* | hpux10* | hpux11*) # Give a soname corresponding to the major version so that dld.sl refuses to # link against other versions. version_type=sunos need_lib_prefix=no need_version=no case $host_cpu in ia64*) shrext_cmds='.so' hardcode_into_libs=yes dynamic_linker="$host_os dld.so" shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' if test 32 = "$HPUX_IA64_MODE"; then sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" sys_lib_dlsearch_path_spec=/usr/lib/hpux32 else sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" sys_lib_dlsearch_path_spec=/usr/lib/hpux64 fi ;; hppa*64*) shrext_cmds='.sl' hardcode_into_libs=yes dynamic_linker="$host_os dld.sl" shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; *) shrext_cmds='.sl' dynamic_linker="$host_os dld.sl" shlibpath_var=SHLIB_PATH shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' ;; esac # HP-UX runs *really* slowly unless shared libraries are mode 555, ... postinstall_cmds='chmod 555 $lib' # or fails outright, so override atomically: install_override_mode=555 ;; interix[[3-9]]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; irix5* | irix6* | nonstopux*) case $host_os in nonstopux*) version_type=nonstopux ;; *) if test yes = "$lt_cv_prog_gnu_ld"; then version_type=linux # correct to gnu/linux during the next big refactor else version_type=irix fi ;; esac need_lib_prefix=no need_version=no soname_spec='$libname$release$shared_ext$major' library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$release$shared_ext $libname$shared_ext' case $host_os in irix5* | nonstopux*) libsuff= shlibsuff= ;; *) case $LD in # libtool.m4 will add one of these switches to LD *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") libsuff= shlibsuff= libmagic=32-bit;; *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") libsuff=32 shlibsuff=N32 libmagic=N32;; *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") libsuff=64 shlibsuff=64 libmagic=64-bit;; *) libsuff= shlibsuff= libmagic=never-match;; esac ;; esac shlibpath_var=LD_LIBRARY${shlibsuff}_PATH shlibpath_overrides_runpath=no sys_lib_search_path_spec="/usr/lib$libsuff /lib$libsuff /usr/local/lib$libsuff" sys_lib_dlsearch_path_spec="/usr/lib$libsuff /lib$libsuff" hardcode_into_libs=yes ;; # No shared lib support for Linux oldld, aout, or coff. linux*oldld* | linux*aout* | linux*coff*) dynamic_linker=no ;; linux*android*) version_type=none # Android doesn't support versioned libraries. need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext' soname_spec='$libname$release$shared_ext' finish_cmds= shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes dynamic_linker='Android linker' # Don't embed -rpath directories since the linker doesn't support them. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' ;; # This must be glibc/ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no # Some binutils ld are patched to set DT_RUNPATH AC_CACHE_VAL([lt_cv_shlibpath_overrides_runpath], [lt_cv_shlibpath_overrides_runpath=no save_LDFLAGS=$LDFLAGS save_libdir=$libdir eval "libdir=/foo; wl=\"$_LT_TAGVAR(lt_prog_compiler_wl, $1)\"; \ LDFLAGS=\"\$LDFLAGS $_LT_TAGVAR(hardcode_libdir_flag_spec, $1)\"" AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], [AS_IF([ ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null], [lt_cv_shlibpath_overrides_runpath=yes])]) LDFLAGS=$save_LDFLAGS libdir=$save_libdir ]) shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes # Ideally, we could use ldconfig to report *all* directores which are # searched for libraries, however this is still not possible. Aside from not # being certain /sbin/ldconfig is available, command # 'ldconfig -N -X -v | grep ^/' on 64bit Fedora does not report /usr/lib64, # even though it is searched at run-time. Try to do the best guess by # appending ld.so.conf contents (and includes) to the search path. if test -f /etc/ld.so.conf; then lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \[$]2)); skip = 1; } { if (!skip) print \[$]0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" fi # We used to test for /lib/ld.so.1 and disable shared libraries on # powerpc, because MkLinux only supported shared libraries with the # GNU dynamic linker. Since this was broken with cross compilers, # most powerpc-linux boxes support dynamic linking these days and # people can always --disable-shared, the test was removed, and we # assume the GNU/Linux dynamic linker is in use. dynamic_linker='GNU/Linux ld.so' ;; netbsdelf*-gnu) version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='NetBSD ld.elf_so' ;; netbsd*) version_type=sunos need_lib_prefix=no need_version=no if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' dynamic_linker='NetBSD (a.out) ld.so' else library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' dynamic_linker='NetBSD ld.elf_so' fi shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; newsos6) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; *nto* | *qnx*) version_type=qnx need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='ldqnx.so' ;; openbsd* | bitrig*) version_type=sunos sys_lib_dlsearch_path_spec=/usr/lib need_lib_prefix=no if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then need_version=no else need_version=yes fi library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; os2*) libname_spec='$name' version_type=windows shrext_cmds=.dll need_version=no need_lib_prefix=no # OS/2 can only load a DLL with a base name of 8 characters or less. soname_spec='`test -n "$os2dllname" && libname="$os2dllname"; v=$($ECHO $release$versuffix | tr -d .-); n=$($ECHO $libname | cut -b -$((8 - ${#v})) | tr . _); $ECHO $n$v`$shared_ext' library_names_spec='${libname}_dll.$libext' dynamic_linker='OS/2 ld.exe' shlibpath_var=BEGINLIBPATH sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; $ECHO \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; $ECHO \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' ;; osf3* | osf4* | osf5*) version_type=osf need_lib_prefix=no need_version=no soname_spec='$libname$release$shared_ext$major' library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; rdos*) dynamic_linker=no ;; solaris*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes # ldd complains unless libraries are executable postinstall_cmds='chmod +x $lib' ;; sunos4*) version_type=sunos library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes if test yes = "$with_gnu_ld"; then need_lib_prefix=no fi need_version=yes ;; sysv4 | sysv4.3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH case $host_vendor in sni) shlibpath_overrides_runpath=no need_lib_prefix=no runpath_var=LD_RUN_PATH ;; siemens) need_lib_prefix=no ;; motorola) need_lib_prefix=no need_version=no shlibpath_overrides_runpath=no sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' ;; esac ;; sysv4*MP*) if test -d /usr/nec; then version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$shared_ext.$versuffix $libname$shared_ext.$major $libname$shared_ext' soname_spec='$libname$shared_ext.$major' shlibpath_var=LD_LIBRARY_PATH fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) version_type=sco need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes if test yes = "$with_gnu_ld"; then sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' else sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' case $host_os in sco3.2v5*) sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" ;; esac fi sys_lib_dlsearch_path_spec='/usr/lib' ;; tpf*) # TPF is a cross-target only. Preferred cross-host = GNU/Linux. version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; uts4*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH ;; *) dynamic_linker=no ;; esac AC_MSG_RESULT([$dynamic_linker]) test no = "$dynamic_linker" && can_build_shared=no variables_saved_for_relink="PATH $shlibpath_var $runpath_var" if test yes = "$GCC"; then variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" fi if test set = "${lt_cv_sys_lib_search_path_spec+set}"; then sys_lib_search_path_spec=$lt_cv_sys_lib_search_path_spec fi if test set = "${lt_cv_sys_lib_dlsearch_path_spec+set}"; then sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec fi # remember unaugmented sys_lib_dlsearch_path content for libtool script decls... configure_time_dlsearch_path=$sys_lib_dlsearch_path_spec # ... but it needs LT_SYS_LIBRARY_PATH munging for other configure-time code func_munge_path_list sys_lib_dlsearch_path_spec "$LT_SYS_LIBRARY_PATH" # to be used as default LT_SYS_LIBRARY_PATH value in generated libtool configure_time_lt_sys_library_path=$LT_SYS_LIBRARY_PATH _LT_DECL([], [variables_saved_for_relink], [1], [Variables whose values should be saved in libtool wrapper scripts and restored at link time]) _LT_DECL([], [need_lib_prefix], [0], [Do we need the "lib" prefix for modules?]) _LT_DECL([], [need_version], [0], [Do we need a version for libraries?]) _LT_DECL([], [version_type], [0], [Library versioning type]) _LT_DECL([], [runpath_var], [0], [Shared library runtime path variable]) _LT_DECL([], [shlibpath_var], [0],[Shared library path variable]) _LT_DECL([], [shlibpath_overrides_runpath], [0], [Is shlibpath searched before the hard-coded library search path?]) _LT_DECL([], [libname_spec], [1], [Format of library name prefix]) _LT_DECL([], [library_names_spec], [1], [[List of archive names. First name is the real one, the rest are links. The last name is the one that the linker finds with -lNAME]]) _LT_DECL([], [soname_spec], [1], [[The coded name of the library, if different from the real name]]) _LT_DECL([], [install_override_mode], [1], [Permission mode override for installation of shared libraries]) _LT_DECL([], [postinstall_cmds], [2], [Command to use after installation of a shared archive]) _LT_DECL([], [postuninstall_cmds], [2], [Command to use after uninstallation of a shared archive]) _LT_DECL([], [finish_cmds], [2], [Commands used to finish a libtool library installation in a directory]) _LT_DECL([], [finish_eval], [1], [[As "finish_cmds", except a single script fragment to be evaled but not shown]]) _LT_DECL([], [hardcode_into_libs], [0], [Whether we should hardcode library paths into libraries]) _LT_DECL([], [sys_lib_search_path_spec], [2], [Compile-time system search path for libraries]) _LT_DECL([sys_lib_dlsearch_path_spec], [configure_time_dlsearch_path], [2], [Detected run-time system search path for libraries]) _LT_DECL([], [configure_time_lt_sys_library_path], [2], [Explicit LT_SYS_LIBRARY_PATH set during ./configure time]) ])# _LT_SYS_DYNAMIC_LINKER # _LT_PATH_TOOL_PREFIX(TOOL) # -------------------------- # find a file program that can recognize shared library AC_DEFUN([_LT_PATH_TOOL_PREFIX], [m4_require([_LT_DECL_EGREP])dnl AC_MSG_CHECKING([for $1]) AC_CACHE_VAL(lt_cv_path_MAGIC_CMD, [case $MAGIC_CMD in [[\\/*] | ?:[\\/]*]) lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path. ;; *) lt_save_MAGIC_CMD=$MAGIC_CMD lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR dnl $ac_dummy forces splitting on constant user-supplied paths. dnl POSIX.2 word splitting is done only on the output of word expansions, dnl not every word. This closes a longstanding sh security hole. ac_dummy="m4_if([$2], , $PATH, [$2])" for ac_dir in $ac_dummy; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/$1"; then lt_cv_path_MAGIC_CMD=$ac_dir/"$1" if test -n "$file_magic_test_file"; then case $deplibs_check_method in "file_magic "*) file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` MAGIC_CMD=$lt_cv_path_MAGIC_CMD if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | $EGREP "$file_magic_regex" > /dev/null; then : else cat <<_LT_EOF 1>&2 *** Warning: the command libtool uses to detect shared libraries, *** $file_magic_cmd, produces output that libtool cannot recognize. *** The result is that libtool may fail to recognize shared libraries *** as such. This will affect the creation of libtool libraries that *** depend on shared libraries, but programs linked with such libtool *** libraries will work regardless of this problem. Nevertheless, you *** may want to report the problem to your system manager and/or to *** bug-libtool@gnu.org _LT_EOF fi ;; esac fi break fi done IFS=$lt_save_ifs MAGIC_CMD=$lt_save_MAGIC_CMD ;; esac]) MAGIC_CMD=$lt_cv_path_MAGIC_CMD if test -n "$MAGIC_CMD"; then AC_MSG_RESULT($MAGIC_CMD) else AC_MSG_RESULT(no) fi _LT_DECL([], [MAGIC_CMD], [0], [Used to examine libraries when file_magic_cmd begins with "file"])dnl ])# _LT_PATH_TOOL_PREFIX # Old name: AU_ALIAS([AC_PATH_TOOL_PREFIX], [_LT_PATH_TOOL_PREFIX]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_PATH_TOOL_PREFIX], []) # _LT_PATH_MAGIC # -------------- # find a file program that can recognize a shared library m4_defun([_LT_PATH_MAGIC], [_LT_PATH_TOOL_PREFIX(${ac_tool_prefix}file, /usr/bin$PATH_SEPARATOR$PATH) if test -z "$lt_cv_path_MAGIC_CMD"; then if test -n "$ac_tool_prefix"; then _LT_PATH_TOOL_PREFIX(file, /usr/bin$PATH_SEPARATOR$PATH) else MAGIC_CMD=: fi fi ])# _LT_PATH_MAGIC # LT_PATH_LD # ---------- # find the pathname to the GNU or non-GNU linker AC_DEFUN([LT_PATH_LD], [AC_REQUIRE([AC_PROG_CC])dnl AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_CANONICAL_BUILD])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_PROG_ECHO_BACKSLASH])dnl AC_ARG_WITH([gnu-ld], [AS_HELP_STRING([--with-gnu-ld], [assume the C compiler uses GNU ld @<:@default=no@:>@])], [test no = "$withval" || with_gnu_ld=yes], [with_gnu_ld=no])dnl ac_prog=ld if test yes = "$GCC"; then # Check if gcc -print-prog-name=ld gives a path. AC_MSG_CHECKING([for ld used by $CC]) case $host in *-*-mingw*) # gcc leaves a trailing carriage return, which upsets mingw ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; *) ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; esac case $ac_prog in # Accept absolute paths. [[\\/]]* | ?:[[\\/]]*) re_direlt='/[[^/]][[^/]]*/\.\./' # Canonicalize the pathname of ld ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` done test -z "$LD" && LD=$ac_prog ;; "") # If it fails, then pretend we aren't using GCC. ac_prog=ld ;; *) # If it is relative, then search for the first ld in PATH. with_gnu_ld=unknown ;; esac elif test yes = "$with_gnu_ld"; then AC_MSG_CHECKING([for GNU ld]) else AC_MSG_CHECKING([for non-GNU ld]) fi AC_CACHE_VAL(lt_cv_path_LD, [if test -z "$LD"; then lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR for ac_dir in $PATH; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then lt_cv_path_LD=$ac_dir/$ac_prog # Check to see if the program is GNU ld. I'd rather use --version, # but apparently some variants of GNU ld only accept -v. # Break only if it was the GNU/non-GNU ld that we prefer. case `"$lt_cv_path_LD" -v 2>&1 &1 conftest.i cat conftest.i conftest.i >conftest2.i : ${lt_DD:=$DD} AC_PATH_PROGS_FEATURE_CHECK([lt_DD], [dd], [if "$ac_path_lt_DD" bs=32 count=1 conftest.out 2>/dev/null; then cmp -s conftest.i conftest.out \ && ac_cv_path_lt_DD="$ac_path_lt_DD" ac_path_lt_DD_found=: fi]) rm -f conftest.i conftest2.i conftest.out]) ])# _LT_PATH_DD # _LT_CMD_TRUNCATE # ---------------- # find command to truncate a binary pipe m4_defun([_LT_CMD_TRUNCATE], [m4_require([_LT_PATH_DD]) AC_CACHE_CHECK([how to truncate binary pipes], [lt_cv_truncate_bin], [printf 0123456789abcdef0123456789abcdef >conftest.i cat conftest.i conftest.i >conftest2.i lt_cv_truncate_bin= if "$ac_cv_path_lt_DD" bs=32 count=1 conftest.out 2>/dev/null; then cmp -s conftest.i conftest.out \ && lt_cv_truncate_bin="$ac_cv_path_lt_DD bs=4096 count=1" fi rm -f conftest.i conftest2.i conftest.out test -z "$lt_cv_truncate_bin" && lt_cv_truncate_bin="$SED -e 4q"]) _LT_DECL([lt_truncate_bin], [lt_cv_truncate_bin], [1], [Command to truncate a binary pipe]) ])# _LT_CMD_TRUNCATE # _LT_CHECK_MAGIC_METHOD # ---------------------- # how to check for library dependencies # -- PORTME fill in with the dynamic library characteristics m4_defun([_LT_CHECK_MAGIC_METHOD], [m4_require([_LT_DECL_EGREP]) m4_require([_LT_DECL_OBJDUMP]) AC_CACHE_CHECK([how to recognize dependent libraries], lt_cv_deplibs_check_method, [lt_cv_file_magic_cmd='$MAGIC_CMD' lt_cv_file_magic_test_file= lt_cv_deplibs_check_method='unknown' # Need to set the preceding variable on all platforms that support # interlibrary dependencies. # 'none' -- dependencies not supported. # 'unknown' -- same as none, but documents that we really don't know. # 'pass_all' -- all dependencies passed with no checks. # 'test_compile' -- check by making test program. # 'file_magic [[regex]]' -- check by looking for files in library path # that responds to the $file_magic_cmd with a given extended regex. # If you have 'file' or equivalent on your system and you're not sure # whether 'pass_all' will *always* work, you probably want this one. case $host_os in aix[[4-9]]*) lt_cv_deplibs_check_method=pass_all ;; beos*) lt_cv_deplibs_check_method=pass_all ;; bsdi[[45]]*) lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib)' lt_cv_file_magic_cmd='/usr/bin/file -L' lt_cv_file_magic_test_file=/shlib/libc.so ;; cygwin*) # func_win32_libid is a shell function defined in ltmain.sh lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' lt_cv_file_magic_cmd='func_win32_libid' ;; mingw* | pw32*) # Base MSYS/MinGW do not provide the 'file' command needed by # func_win32_libid shell function, so use a weaker test based on 'objdump', # unless we find 'file', for example because we are cross-compiling. if ( file / ) >/dev/null 2>&1; then lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' lt_cv_file_magic_cmd='func_win32_libid' else # Keep this pattern in sync with the one in func_win32_libid. lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' lt_cv_file_magic_cmd='$OBJDUMP -f' fi ;; cegcc*) # use the weaker test based on 'objdump'. See mingw*. lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?' lt_cv_file_magic_cmd='$OBJDUMP -f' ;; darwin* | rhapsody*) lt_cv_deplibs_check_method=pass_all ;; freebsd* | dragonfly*) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then case $host_cpu in i*86 ) # Not sure whether the presence of OpenBSD here was a mistake. # Let's accept both of them until this is cleared up. lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[[3-9]]86 (compact )?demand paged shared library' lt_cv_file_magic_cmd=/usr/bin/file lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` ;; esac else lt_cv_deplibs_check_method=pass_all fi ;; haiku*) lt_cv_deplibs_check_method=pass_all ;; hpux10.20* | hpux11*) lt_cv_file_magic_cmd=/usr/bin/file case $host_cpu in ia64*) lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|ELF-[[0-9]][[0-9]]) shared object file - IA64' lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so ;; hppa*64*) [lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]'] lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl ;; *) lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|PA-RISC[[0-9]]\.[[0-9]]) shared library' lt_cv_file_magic_test_file=/usr/lib/libc.sl ;; esac ;; interix[[3-9]]*) # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|\.a)$' ;; irix5* | irix6* | nonstopux*) case $LD in *-32|*"-32 ") libmagic=32-bit;; *-n32|*"-n32 ") libmagic=N32;; *-64|*"-64 ") libmagic=64-bit;; *) libmagic=never-match;; esac lt_cv_deplibs_check_method=pass_all ;; # This must be glibc/ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) lt_cv_deplibs_check_method=pass_all ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|_pic\.a)$' fi ;; newos6*) lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (executable|dynamic lib)' lt_cv_file_magic_cmd=/usr/bin/file lt_cv_file_magic_test_file=/usr/lib/libnls.so ;; *nto* | *qnx*) lt_cv_deplibs_check_method=pass_all ;; openbsd* | bitrig*) if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|\.so|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' fi ;; osf3* | osf4* | osf5*) lt_cv_deplibs_check_method=pass_all ;; rdos*) lt_cv_deplibs_check_method=pass_all ;; solaris*) lt_cv_deplibs_check_method=pass_all ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) lt_cv_deplibs_check_method=pass_all ;; sysv4 | sysv4.3*) case $host_vendor in motorola) lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib) M[[0-9]][[0-9]]* Version [[0-9]]' lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` ;; ncr) lt_cv_deplibs_check_method=pass_all ;; sequent) lt_cv_file_magic_cmd='/bin/file' lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB (shared object|dynamic lib )' ;; sni) lt_cv_file_magic_cmd='/bin/file' lt_cv_deplibs_check_method="file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB dynamic lib" lt_cv_file_magic_test_file=/lib/libc.so ;; siemens) lt_cv_deplibs_check_method=pass_all ;; pc) lt_cv_deplibs_check_method=pass_all ;; esac ;; tpf*) lt_cv_deplibs_check_method=pass_all ;; os2*) lt_cv_deplibs_check_method=pass_all ;; esac ]) file_magic_glob= want_nocaseglob=no if test "$build" = "$host"; then case $host_os in mingw* | pw32*) if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then want_nocaseglob=yes else file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[[\1]]\/[[\1]]\/g;/g"` fi ;; esac fi file_magic_cmd=$lt_cv_file_magic_cmd deplibs_check_method=$lt_cv_deplibs_check_method test -z "$deplibs_check_method" && deplibs_check_method=unknown _LT_DECL([], [deplibs_check_method], [1], [Method to check whether dependent libraries are shared objects]) _LT_DECL([], [file_magic_cmd], [1], [Command to use when deplibs_check_method = "file_magic"]) _LT_DECL([], [file_magic_glob], [1], [How to find potential files when deplibs_check_method = "file_magic"]) _LT_DECL([], [want_nocaseglob], [1], [Find potential files using nocaseglob when deplibs_check_method = "file_magic"]) ])# _LT_CHECK_MAGIC_METHOD # LT_PATH_NM # ---------- # find the pathname to a BSD- or MS-compatible name lister AC_DEFUN([LT_PATH_NM], [AC_REQUIRE([AC_PROG_CC])dnl AC_CACHE_CHECK([for BSD- or MS-compatible name lister (nm)], lt_cv_path_NM, [if test -n "$NM"; then # Let the user override the test. lt_cv_path_NM=$NM else lt_nm_to_check=${ac_tool_prefix}nm if test -n "$ac_tool_prefix" && test "$build" = "$host"; then lt_nm_to_check="$lt_nm_to_check nm" fi for lt_tmp_nm in $lt_nm_to_check; do lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. tmp_nm=$ac_dir/$lt_tmp_nm if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext"; then # Check to see if the nm accepts a BSD-compat flag. # Adding the 'sed 1q' prevents false positives on HP-UX, which says: # nm: unknown option "B" ignored # Tru64's nm complains that /dev/null is an invalid object file # MSYS converts /dev/null to NUL, MinGW nm treats NUL as empty case $build_os in mingw*) lt_bad_file=conftest.nm/nofile ;; *) lt_bad_file=/dev/null ;; esac case `"$tmp_nm" -B $lt_bad_file 2>&1 | sed '1q'` in *$lt_bad_file* | *'Invalid file or object type'*) lt_cv_path_NM="$tmp_nm -B" break 2 ;; *) case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in */dev/null*) lt_cv_path_NM="$tmp_nm -p" break 2 ;; *) lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but continue # so that we can try to find one that supports BSD flags ;; esac ;; esac fi done IFS=$lt_save_ifs done : ${lt_cv_path_NM=no} fi]) if test no != "$lt_cv_path_NM"; then NM=$lt_cv_path_NM else # Didn't find any BSD compatible name lister, look for dumpbin. if test -n "$DUMPBIN"; then : # Let the user override the test. else AC_CHECK_TOOLS(DUMPBIN, [dumpbin "link -dump"], :) case `$DUMPBIN -symbols -headers /dev/null 2>&1 | sed '1q'` in *COFF*) DUMPBIN="$DUMPBIN -symbols -headers" ;; *) DUMPBIN=: ;; esac fi AC_SUBST([DUMPBIN]) if test : != "$DUMPBIN"; then NM=$DUMPBIN fi fi test -z "$NM" && NM=nm AC_SUBST([NM]) _LT_DECL([], [NM], [1], [A BSD- or MS-compatible name lister])dnl AC_CACHE_CHECK([the name lister ($NM) interface], [lt_cv_nm_interface], [lt_cv_nm_interface="BSD nm" echo "int some_variable = 0;" > conftest.$ac_ext (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&AS_MESSAGE_LOG_FD) (eval "$ac_compile" 2>conftest.err) cat conftest.err >&AS_MESSAGE_LOG_FD (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&AS_MESSAGE_LOG_FD) (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) cat conftest.err >&AS_MESSAGE_LOG_FD (eval echo "\"\$as_me:$LINENO: output\"" >&AS_MESSAGE_LOG_FD) cat conftest.out >&AS_MESSAGE_LOG_FD if $GREP 'External.*some_variable' conftest.out > /dev/null; then lt_cv_nm_interface="MS dumpbin" fi rm -f conftest*]) ])# LT_PATH_NM # Old names: AU_ALIAS([AM_PROG_NM], [LT_PATH_NM]) AU_ALIAS([AC_PROG_NM], [LT_PATH_NM]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AM_PROG_NM], []) dnl AC_DEFUN([AC_PROG_NM], []) # _LT_CHECK_SHAREDLIB_FROM_LINKLIB # -------------------------------- # how to determine the name of the shared library # associated with a specific link library. # -- PORTME fill in with the dynamic library characteristics m4_defun([_LT_CHECK_SHAREDLIB_FROM_LINKLIB], [m4_require([_LT_DECL_EGREP]) m4_require([_LT_DECL_OBJDUMP]) m4_require([_LT_DECL_DLLTOOL]) AC_CACHE_CHECK([how to associate runtime and link libraries], lt_cv_sharedlib_from_linklib_cmd, [lt_cv_sharedlib_from_linklib_cmd='unknown' case $host_os in cygwin* | mingw* | pw32* | cegcc*) # two different shell functions defined in ltmain.sh; # decide which one to use based on capabilities of $DLLTOOL case `$DLLTOOL --help 2>&1` in *--identify-strict*) lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib ;; *) lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback ;; esac ;; *) # fallback: assume linklib IS sharedlib lt_cv_sharedlib_from_linklib_cmd=$ECHO ;; esac ]) sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO _LT_DECL([], [sharedlib_from_linklib_cmd], [1], [Command to associate shared and link libraries]) ])# _LT_CHECK_SHAREDLIB_FROM_LINKLIB # _LT_PATH_MANIFEST_TOOL # ---------------------- # locate the manifest tool m4_defun([_LT_PATH_MANIFEST_TOOL], [AC_CHECK_TOOL(MANIFEST_TOOL, mt, :) test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt AC_CACHE_CHECK([if $MANIFEST_TOOL is a manifest tool], [lt_cv_path_mainfest_tool], [lt_cv_path_mainfest_tool=no echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&AS_MESSAGE_LOG_FD $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out cat conftest.err >&AS_MESSAGE_LOG_FD if $GREP 'Manifest Tool' conftest.out > /dev/null; then lt_cv_path_mainfest_tool=yes fi rm -f conftest*]) if test yes != "$lt_cv_path_mainfest_tool"; then MANIFEST_TOOL=: fi _LT_DECL([], [MANIFEST_TOOL], [1], [Manifest tool])dnl ])# _LT_PATH_MANIFEST_TOOL # _LT_DLL_DEF_P([FILE]) # --------------------- # True iff FILE is a Windows DLL '.def' file. # Keep in sync with func_dll_def_p in the libtool script AC_DEFUN([_LT_DLL_DEF_P], [dnl test DEF = "`$SED -n dnl -e '\''s/^[[ ]]*//'\'' dnl Strip leading whitespace -e '\''/^\(;.*\)*$/d'\'' dnl Delete empty lines and comments -e '\''s/^\(EXPORTS\|LIBRARY\)\([[ ]].*\)*$/DEF/p'\'' dnl -e q dnl Only consider the first "real" line $1`" dnl ])# _LT_DLL_DEF_P # LT_LIB_M # -------- # check for math library AC_DEFUN([LT_LIB_M], [AC_REQUIRE([AC_CANONICAL_HOST])dnl LIBM= case $host in *-*-beos* | *-*-cegcc* | *-*-cygwin* | *-*-haiku* | *-*-pw32* | *-*-darwin*) # These system don't have libm, or don't need it ;; *-ncr-sysv4.3*) AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM=-lmw) AC_CHECK_LIB(m, cos, LIBM="$LIBM -lm") ;; *) AC_CHECK_LIB(m, cos, LIBM=-lm) ;; esac AC_SUBST([LIBM]) ])# LT_LIB_M # Old name: AU_ALIAS([AC_CHECK_LIBM], [LT_LIB_M]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_CHECK_LIBM], []) # _LT_COMPILER_NO_RTTI([TAGNAME]) # ------------------------------- m4_defun([_LT_COMPILER_NO_RTTI], [m4_require([_LT_TAG_COMPILER])dnl _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= if test yes = "$GCC"; then case $cc_basename in nvcc*) _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -Xcompiler -fno-builtin' ;; *) _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' ;; esac _LT_COMPILER_OPTION([if $compiler supports -fno-rtti -fno-exceptions], lt_cv_prog_compiler_rtti_exceptions, [-fno-rtti -fno-exceptions], [], [_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)="$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) -fno-rtti -fno-exceptions"]) fi _LT_TAGDECL([no_builtin_flag], [lt_prog_compiler_no_builtin_flag], [1], [Compiler flag to turn off builtin functions]) ])# _LT_COMPILER_NO_RTTI # _LT_CMD_GLOBAL_SYMBOLS # ---------------------- m4_defun([_LT_CMD_GLOBAL_SYMBOLS], [AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_PROG_CC])dnl AC_REQUIRE([AC_PROG_AWK])dnl AC_REQUIRE([LT_PATH_NM])dnl AC_REQUIRE([LT_PATH_LD])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_TAG_COMPILER])dnl # Check for command to grab the raw symbol name followed by C symbol from nm. AC_MSG_CHECKING([command to parse $NM output from $compiler object]) AC_CACHE_VAL([lt_cv_sys_global_symbol_pipe], [ # These are sane defaults that work on at least a few old systems. # [They come from Ultrix. What could be older than Ultrix?!! ;)] # Character class describing NM global symbol codes. symcode='[[BCDEGRST]]' # Regexp to match symbols that can be accessed directly from C. sympat='\([[_A-Za-z]][[_A-Za-z0-9]]*\)' # Define system-specific variables. case $host_os in aix*) symcode='[[BCDT]]' ;; cygwin* | mingw* | pw32* | cegcc*) symcode='[[ABCDGISTW]]' ;; hpux*) if test ia64 = "$host_cpu"; then symcode='[[ABCDEGRST]]' fi ;; irix* | nonstopux*) symcode='[[BCDEGRST]]' ;; osf*) symcode='[[BCDEGQRST]]' ;; solaris*) symcode='[[BDRT]]' ;; sco3.2v5*) symcode='[[DT]]' ;; sysv4.2uw2*) symcode='[[DT]]' ;; sysv5* | sco5v6* | unixware* | OpenUNIX*) symcode='[[ABDT]]' ;; sysv4) symcode='[[DFNSTU]]' ;; esac # If we're using GNU nm, then use its standard symbol codes. case `$NM -V 2>&1` in *GNU* | *'with BFD'*) symcode='[[ABCDGIRSTW]]' ;; esac if test "$lt_cv_nm_interface" = "MS dumpbin"; then # Gets list of data symbols to import. lt_cv_sys_global_symbol_to_import="sed -n -e 's/^I .* \(.*\)$/\1/p'" # Adjust the below global symbol transforms to fixup imported variables. lt_cdecl_hook=" -e 's/^I .* \(.*\)$/extern __declspec(dllimport) char \1;/p'" lt_c_name_hook=" -e 's/^I .* \(.*\)$/ {\"\1\", (void *) 0},/p'" lt_c_name_lib_hook="\ -e 's/^I .* \(lib.*\)$/ {\"\1\", (void *) 0},/p'\ -e 's/^I .* \(.*\)$/ {\"lib\1\", (void *) 0},/p'" else # Disable hooks by default. lt_cv_sys_global_symbol_to_import= lt_cdecl_hook= lt_c_name_hook= lt_c_name_lib_hook= fi # Transform an extracted symbol line into a proper C declaration. # Some systems (esp. on ia64) link data and code symbols differently, # so use this general approach. lt_cv_sys_global_symbol_to_cdecl="sed -n"\ $lt_cdecl_hook\ " -e 's/^T .* \(.*\)$/extern int \1();/p'"\ " -e 's/^$symcode$symcode* .* \(.*\)$/extern char \1;/p'" # Transform an extracted symbol line into symbol name and symbol address lt_cv_sys_global_symbol_to_c_name_address="sed -n"\ $lt_c_name_hook\ " -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ " -e 's/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/p'" # Transform an extracted symbol line into symbol name with lib prefix and # symbol address. lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n"\ $lt_c_name_lib_hook\ " -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ " -e 's/^$symcode$symcode* .* \(lib.*\)$/ {\"\1\", (void *) \&\1},/p'"\ " -e 's/^$symcode$symcode* .* \(.*\)$/ {\"lib\1\", (void *) \&\1},/p'" # Handle CRLF in mingw tool chain opt_cr= case $build_os in mingw*) opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp ;; esac # Try without a prefix underscore, then with it. for ac_symprfx in "" "_"; do # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. symxfrm="\\1 $ac_symprfx\\2 \\2" # Write the raw and C identifiers. if test "$lt_cv_nm_interface" = "MS dumpbin"; then # Fake it for dumpbin and say T for any non-static function, # D for any global variable and I for any imported variable. # Also find C++ and __fastcall symbols from MSVC++, # which start with @ or ?. lt_cv_sys_global_symbol_pipe="$AWK ['"\ " {last_section=section; section=\$ 3};"\ " /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\ " /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ " /^ *Symbol name *: /{split(\$ 0,sn,\":\"); si=substr(sn[2],2)};"\ " /^ *Type *: code/{print \"T\",si,substr(si,length(prfx))};"\ " /^ *Type *: data/{print \"I\",si,substr(si,length(prfx))};"\ " \$ 0!~/External *\|/{next};"\ " / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ " {if(hide[section]) next};"\ " {f=\"D\"}; \$ 0~/\(\).*\|/{f=\"T\"};"\ " {split(\$ 0,a,/\||\r/); split(a[2],s)};"\ " s[1]~/^[@?]/{print f,s[1],s[1]; next};"\ " s[1]~prfx {split(s[1],t,\"@\"); print f,t[1],substr(t[1],length(prfx))}"\ " ' prfx=^$ac_symprfx]" else lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[[ ]]\($symcode$symcode*\)[[ ]][[ ]]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" fi lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'" # Check to see that the pipe works correctly. pipe_works=no rm -f conftest* cat > conftest.$ac_ext <<_LT_EOF #ifdef __cplusplus extern "C" { #endif char nm_test_var; void nm_test_func(void); void nm_test_func(void){} #ifdef __cplusplus } #endif int main(){nm_test_var='a';nm_test_func();return(0);} _LT_EOF if AC_TRY_EVAL(ac_compile); then # Now try to grab the symbols. nlist=conftest.nm $ECHO "$as_me:$LINENO: $NM conftest.$ac_objext | $lt_cv_sys_global_symbol_pipe > $nlist" >&AS_MESSAGE_LOG_FD if eval "$NM" conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist 2>&AS_MESSAGE_LOG_FD && test -s "$nlist"; then # Try sorting and uniquifying the output. if sort "$nlist" | uniq > "$nlist"T; then mv -f "$nlist"T "$nlist" else rm -f "$nlist"T fi # Make sure that we snagged all the symbols we need. if $GREP ' nm_test_var$' "$nlist" >/dev/null; then if $GREP ' nm_test_func$' "$nlist" >/dev/null; then cat <<_LT_EOF > conftest.$ac_ext /* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ #if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE /* DATA imports from DLLs on WIN32 can't be const, because runtime relocations are performed -- see ld's documentation on pseudo-relocs. */ # define LT@&t@_DLSYM_CONST #elif defined __osf__ /* This system does not cope well with relocations in const data. */ # define LT@&t@_DLSYM_CONST #else # define LT@&t@_DLSYM_CONST const #endif #ifdef __cplusplus extern "C" { #endif _LT_EOF # Now generate the symbol file. eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext' cat <<_LT_EOF >> conftest.$ac_ext /* The mapping between symbol names and symbols. */ LT@&t@_DLSYM_CONST struct { const char *name; void *address; } lt__PROGRAM__LTX_preloaded_symbols[[]] = { { "@PROGRAM@", (void *) 0 }, _LT_EOF $SED "s/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext cat <<\_LT_EOF >> conftest.$ac_ext {0, (void *) 0} }; /* This works around a problem in FreeBSD linker */ #ifdef FREEBSD_WORKAROUND static const void *lt_preloaded_setup() { return lt__PROGRAM__LTX_preloaded_symbols; } #endif #ifdef __cplusplus } #endif _LT_EOF # Now try linking the two files. mv conftest.$ac_objext conftstm.$ac_objext lt_globsym_save_LIBS=$LIBS lt_globsym_save_CFLAGS=$CFLAGS LIBS=conftstm.$ac_objext CFLAGS="$CFLAGS$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)" if AC_TRY_EVAL(ac_link) && test -s conftest$ac_exeext; then pipe_works=yes fi LIBS=$lt_globsym_save_LIBS CFLAGS=$lt_globsym_save_CFLAGS else echo "cannot find nm_test_func in $nlist" >&AS_MESSAGE_LOG_FD fi else echo "cannot find nm_test_var in $nlist" >&AS_MESSAGE_LOG_FD fi else echo "cannot run $lt_cv_sys_global_symbol_pipe" >&AS_MESSAGE_LOG_FD fi else echo "$progname: failed program was:" >&AS_MESSAGE_LOG_FD cat conftest.$ac_ext >&5 fi rm -rf conftest* conftst* # Do not use the global_symbol_pipe unless it works. if test yes = "$pipe_works"; then break else lt_cv_sys_global_symbol_pipe= fi done ]) if test -z "$lt_cv_sys_global_symbol_pipe"; then lt_cv_sys_global_symbol_to_cdecl= fi if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then AC_MSG_RESULT(failed) else AC_MSG_RESULT(ok) fi # Response file support. if test "$lt_cv_nm_interface" = "MS dumpbin"; then nm_file_list_spec='@' elif $NM --help 2>/dev/null | grep '[[@]]FILE' >/dev/null; then nm_file_list_spec='@' fi _LT_DECL([global_symbol_pipe], [lt_cv_sys_global_symbol_pipe], [1], [Take the output of nm and produce a listing of raw symbols and C names]) _LT_DECL([global_symbol_to_cdecl], [lt_cv_sys_global_symbol_to_cdecl], [1], [Transform the output of nm in a proper C declaration]) _LT_DECL([global_symbol_to_import], [lt_cv_sys_global_symbol_to_import], [1], [Transform the output of nm into a list of symbols to manually relocate]) _LT_DECL([global_symbol_to_c_name_address], [lt_cv_sys_global_symbol_to_c_name_address], [1], [Transform the output of nm in a C name address pair]) _LT_DECL([global_symbol_to_c_name_address_lib_prefix], [lt_cv_sys_global_symbol_to_c_name_address_lib_prefix], [1], [Transform the output of nm in a C name address pair when lib prefix is needed]) _LT_DECL([nm_interface], [lt_cv_nm_interface], [1], [The name lister interface]) _LT_DECL([], [nm_file_list_spec], [1], [Specify filename containing input files for $NM]) ]) # _LT_CMD_GLOBAL_SYMBOLS # _LT_COMPILER_PIC([TAGNAME]) # --------------------------- m4_defun([_LT_COMPILER_PIC], [m4_require([_LT_TAG_COMPILER])dnl _LT_TAGVAR(lt_prog_compiler_wl, $1)= _LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_static, $1)= m4_if([$1], [CXX], [ # C++ specific cases for pic, static, wl, etc. if test yes = "$GXX"; then _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' case $host_os in aix*) # All AIX code is PIC. if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' fi _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; m68k) # FIXME: we need at least 68020 code to build shared libraries, but # adding the '-m68020' flag to GCC prevents building anything better, # like '-m68040'. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' ;; esac ;; beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) # PIC is the default for these OSes. ;; mingw* | cygwin* | os2* | pw32* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). # Although the cygwin gcc ignores -fPIC, still need this for old-style # (--disable-auto-import) libraries m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) case $host_os in os2*) _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static' ;; esac ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' ;; *djgpp*) # DJGPP does not support shared libraries at all _LT_TAGVAR(lt_prog_compiler_pic, $1)= ;; haiku*) # PIC is the default for Haiku. # The "-static" flag exists, but is broken. _LT_TAGVAR(lt_prog_compiler_static, $1)= ;; interix[[3-9]]*) # Interix 3.x gcc -fpic/-fPIC options generate broken code. # Instead, we relocate shared libraries at runtime. ;; sysv4*MP*) if test -d /usr/nec; then _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic fi ;; hpux*) # PIC is the default for 64-bit PA HP-UX, but not for 32-bit # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag # sets the default TLS model and affects inlining. case $host_cpu in hppa*64*) ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac ;; *qnx* | *nto*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac else case $host_os in aix[[4-9]]*) # All AIX code is PIC. if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' else _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' fi ;; chorus*) case $cc_basename in cxch68*) # Green Hills C++ Compiler # _LT_TAGVAR(lt_prog_compiler_static, $1)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a" ;; esac ;; mingw* | cygwin* | os2* | pw32* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) ;; dgux*) case $cc_basename in ec++*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' ;; ghcx*) # Green Hills C++ Compiler _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' ;; *) ;; esac ;; freebsd* | dragonfly*) # FreeBSD uses GNU C++ ;; hpux9* | hpux10* | hpux11*) case $cc_basename in CC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive' if test ia64 != "$host_cpu"; then _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' fi ;; aCC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive' case $host_cpu in hppa*64*|ia64*) # +Z the default ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' ;; esac ;; *) ;; esac ;; interix*) # This is c89, which is MS Visual C++ (no shared libs) # Anyone wants to do a port? ;; irix5* | irix6* | nonstopux*) case $cc_basename in CC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' # CC pic flag -KPIC is the default. ;; *) ;; esac ;; linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in KCC*) # KAI C++ Compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; ecpc* ) # old Intel C++ for x86_64, which still supported -KPIC. _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; icpc* ) # Intel C++, used to be incompatible with GCC. # ICC 10 doesn't accept -KPIC any more. _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; pgCC* | pgcpp*) # Portland Group C++ compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; cxx*) # Compaq C++ # Make sure the PIC flag is empty. It appears that all Alpha # Linux and Compaq Tru64 Unix objects are PIC. _LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; xlc* | xlC* | bgxl[[cC]]* | mpixl[[cC]]*) # IBM XL 8.0, 9.0 on PPC and BlueGene _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' ;; esac ;; esac ;; lynxos*) ;; m88k*) ;; mvs*) case $cc_basename in cxx*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-W c,exportall' ;; *) ;; esac ;; netbsd* | netbsdelf*-gnu) ;; *qnx* | *nto*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; osf3* | osf4* | osf5*) case $cc_basename in KCC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' ;; RCC*) # Rational C++ 2.4.1 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' ;; cxx*) # Digital/Compaq C++ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # Make sure the PIC flag is empty. It appears that all Alpha # Linux and Compaq Tru64 Unix objects are PIC. _LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; *) ;; esac ;; psos*) ;; solaris*) case $cc_basename in CC* | sunCC*) # Sun C++ 4.2, 5.x and Centerline C++ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' ;; gcx*) # Green Hills C++ Compiler _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' ;; *) ;; esac ;; sunos4*) case $cc_basename in CC*) # Sun C++ 4.x _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; lcc*) # Lucid _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' ;; *) ;; esac ;; sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) case $cc_basename in CC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; esac ;; tandem*) case $cc_basename in NCC*) # NonStop-UX NCC 3.20 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' ;; *) ;; esac ;; vxworks*) ;; *) _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no ;; esac fi ], [ if test yes = "$GCC"; then _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' case $host_os in aix*) # All AIX code is PIC. if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' fi _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; m68k) # FIXME: we need at least 68020 code to build shared libraries, but # adding the '-m68020' flag to GCC prevents building anything better, # like '-m68040'. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' ;; esac ;; beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) # PIC is the default for these OSes. ;; mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). # Although the cygwin gcc ignores -fPIC, still need this for old-style # (--disable-auto-import) libraries m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) case $host_os in os2*) _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static' ;; esac ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' ;; haiku*) # PIC is the default for Haiku. # The "-static" flag exists, but is broken. _LT_TAGVAR(lt_prog_compiler_static, $1)= ;; hpux*) # PIC is the default for 64-bit PA HP-UX, but not for 32-bit # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag # sets the default TLS model and affects inlining. case $host_cpu in hppa*64*) # +Z the default ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac ;; interix[[3-9]]*) # Interix 3.x gcc -fpic/-fPIC options generate broken code. # Instead, we relocate shared libraries at runtime. ;; msdosdjgpp*) # Just because we use GCC doesn't mean we suddenly get shared libraries # on systems that don't support them. _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no enable_shared=no ;; *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; sysv4*MP*) if test -d /usr/nec; then _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic fi ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac case $cc_basename in nvcc*) # Cuda Compiler Driver 2.2 _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Xlinker ' if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then _LT_TAGVAR(lt_prog_compiler_pic, $1)="-Xcompiler $_LT_TAGVAR(lt_prog_compiler_pic, $1)" fi ;; esac else # PORTME Check for flag to pass linker flags through the system compiler. case $host_os in aix*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' else _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' fi ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' case $cc_basename in nagfor*) # NAG Fortran compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,-Wl,,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; esac ;; mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) case $host_os in os2*) _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static' ;; esac ;; hpux9* | hpux10* | hpux11*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but # not for PA HP-UX. case $host_cpu in hppa*64*|ia64*) # +Z the default ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' ;; esac # Is there a better lt_prog_compiler_static that works with the bundled CC? _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive' ;; irix5* | irix6* | nonstopux*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # PIC (with -KPIC) is the default. _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in # old Intel for x86_64, which still supported -KPIC. ecc*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; # flang / f18. f95 an alias for gfortran or flang on Debian flang* | f18* | f95*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; # icc used to be incompatible with GCC. # ICC 10 doesn't accept -KPIC any more. icc* | ifort*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; # Lahey Fortran 8.1. lf95*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='--shared' _LT_TAGVAR(lt_prog_compiler_static, $1)='--static' ;; nagfor*) # NAG Fortran compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,-Wl,,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; tcc*) # Fabrice Bellard et al's Tiny C Compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*) # Portland Group compilers (*not* the Pentium gcc compiler, # which looks to be a dead project) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; ccc*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # All Alpha code is PIC. _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; xl* | bgxl* | bgf* | mpixl*) # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [[1-7]].* | *Sun*Fortran*\ 8.[[0-3]]*) # Sun Fortran 8.3 passes all unrecognized flags to the linker _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='' ;; *Sun\ F* | *Sun*Fortran*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' ;; *Sun\ C*) # Sun C 5.9 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' ;; *Intel*\ [[CF]]*Compiler*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; *Portland\ Group*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; esac ;; esac ;; newsos6) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; osf3* | osf4* | osf5*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # All OSF/1 code is PIC. _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; rdos*) _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; solaris*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' case $cc_basename in f77* | f90* | f95* | sunf77* | sunf90* | sunf95*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ';; *) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,';; esac ;; sunos4*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; sysv4 | sysv4.2uw2* | sysv4.3*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; sysv4*MP*) if test -d /usr/nec; then _LT_TAGVAR(lt_prog_compiler_pic, $1)='-Kconform_pic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' fi ;; sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; unicos*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no ;; uts4*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; *) _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no ;; esac fi ]) case $host_os in # For platforms that do not support PIC, -DPIC is meaningless: *djgpp*) _LT_TAGVAR(lt_prog_compiler_pic, $1)= ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)="$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])" ;; esac AC_CACHE_CHECK([for $compiler option to produce PIC], [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)], [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_prog_compiler_pic, $1)]) _LT_TAGVAR(lt_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_cv_prog_compiler_pic, $1) # # Check to make sure the PIC flag actually works. # if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then _LT_COMPILER_OPTION([if $compiler PIC flag $_LT_TAGVAR(lt_prog_compiler_pic, $1) works], [_LT_TAGVAR(lt_cv_prog_compiler_pic_works, $1)], [$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])], [], [case $_LT_TAGVAR(lt_prog_compiler_pic, $1) in "" | " "*) ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)=" $_LT_TAGVAR(lt_prog_compiler_pic, $1)" ;; esac], [_LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no]) fi _LT_TAGDECL([pic_flag], [lt_prog_compiler_pic], [1], [Additional compiler flags for building library objects]) _LT_TAGDECL([wl], [lt_prog_compiler_wl], [1], [How to pass a linker flag through the compiler]) # # Check to make sure the static flag actually works. # wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) eval lt_tmp_static_flag=\"$_LT_TAGVAR(lt_prog_compiler_static, $1)\" _LT_LINKER_OPTION([if $compiler static flag $lt_tmp_static_flag works], _LT_TAGVAR(lt_cv_prog_compiler_static_works, $1), $lt_tmp_static_flag, [], [_LT_TAGVAR(lt_prog_compiler_static, $1)=]) _LT_TAGDECL([link_static_flag], [lt_prog_compiler_static], [1], [Compiler flag to prevent dynamic linking]) ])# _LT_COMPILER_PIC # _LT_LINKER_SHLIBS([TAGNAME]) # ---------------------------- # See if the linker supports building shared libraries. m4_defun([_LT_LINKER_SHLIBS], [AC_REQUIRE([LT_PATH_LD])dnl AC_REQUIRE([LT_PATH_NM])dnl m4_require([_LT_PATH_MANIFEST_TOOL])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl m4_require([_LT_TAG_COMPILER])dnl AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) m4_if([$1], [CXX], [ _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] case $host_os in aix[[4-9]]*) # If we're using GNU nm, then we don't want the "-C" option. # -C means demangle to GNU nm, but means don't demangle to AIX nm. # Without the "-l" option, or with the "-B" option, AIX nm treats # weak defined symbols like other global defined symbols, whereas # GNU nm marks them as "W". # While the 'weak' keyword is ignored in the Export File, we need # it in the Import File for the 'aix-soname' feature, so we have # to replace the "-B" option with "-P" for AIX nm. if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' else _LT_TAGVAR(export_symbols_cmds, $1)='`func_echo_all $NM | $SED -e '\''s/B\([[^B]]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && ([substr](\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' fi ;; pw32*) _LT_TAGVAR(export_symbols_cmds, $1)=$ltdll_cmds ;; cygwin* | mingw* | cegcc*) case $cc_basename in cl*) _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' ;; *) _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols' _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'] ;; esac ;; linux* | k*bsd*-gnu | gnu*) _LT_TAGVAR(link_all_deplibs, $1)=no ;; *) _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' ;; esac ], [ runpath_var= _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_cmds, $1)= _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(compiler_needs_object, $1)=no _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(old_archive_from_new_cmds, $1)= _LT_TAGVAR(old_archive_from_expsyms_cmds, $1)= _LT_TAGVAR(thread_safe_flag_spec, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= # include_expsyms should be a list of space-separated symbols to be *always* # included in the symbol list _LT_TAGVAR(include_expsyms, $1)= # exclude_expsyms can be an extended regexp of symbols to exclude # it will be wrapped by ' (' and ')$', so one must not match beginning or # end of line. Example: 'a|bc|.*d.*' will exclude the symbols 'a' and 'bc', # as well as any symbol that contains 'd'. _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out # platforms (ab)use it in PIC code, but their linkers get confused if # the symbol is explicitly referenced. Since portable code cannot # rely on this symbol name, it's probably fine to never include it in # preloaded symbol tables. # Exclude shared library initialization/finalization symbols. dnl Note also adjust exclude_expsyms for C++ above. extract_expsyms_cmds= case $host_os in cygwin* | mingw* | pw32* | cegcc*) # FIXME: the MSVC++ port hasn't been tested in a loooong time # When not using gcc, we currently assume that we are using # Microsoft Visual C++. if test yes != "$GCC"; then with_gnu_ld=no fi ;; interix*) # we just hope/assume this is gcc and not c89 (= MSVC++) with_gnu_ld=yes ;; openbsd* | bitrig*) with_gnu_ld=no ;; linux* | k*bsd*-gnu | gnu*) _LT_TAGVAR(link_all_deplibs, $1)=no ;; esac _LT_TAGVAR(ld_shlibs, $1)=yes # On some targets, GNU ld is compatible enough with the native linker # that we're better off using the native interface for both. lt_use_gnu_ld_interface=no if test yes = "$with_gnu_ld"; then case $host_os in aix*) # The AIX port of GNU ld has always aspired to compatibility # with the native linker. However, as the warning in the GNU ld # block says, versions before 2.19.5* couldn't really create working # shared libraries, regardless of the interface used. case `$LD -v 2>&1` in *\ \(GNU\ Binutils\)\ 2.19.5*) ;; *\ \(GNU\ Binutils\)\ 2.[[2-9]]*) ;; *\ \(GNU\ Binutils\)\ [[3-9]]*) ;; *) lt_use_gnu_ld_interface=yes ;; esac ;; *) lt_use_gnu_ld_interface=yes ;; esac fi if test yes = "$lt_use_gnu_ld_interface"; then # If archive_cmds runs LD, not CC, wlarc should be empty wlarc='$wl' # Set some defaults for GNU ld with shared library support. These # are reset later if shared libraries are not supported. Putting them # here allows them to be overridden if necessary. runpath_var=LD_RUN_PATH _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' # ancient GNU ld didn't support --whole-archive et. al. if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' else _LT_TAGVAR(whole_archive_flag_spec, $1)= fi supports_anon_versioning=no case `$LD -v | $SED -e 's/([^)]\+)\s\+//' 2>&1` in *GNU\ gold*) supports_anon_versioning=yes ;; *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.10.*) ;; # catch versions < 2.11 *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... *\ 2.11.*) ;; # other 2.11 versions *) supports_anon_versioning=yes ;; esac # See if GNU ld supports shared libraries. case $host_os in aix[[3-9]]*) # On AIX/PPC, the GNU linker is very broken if test ia64 != "$host_cpu"; then _LT_TAGVAR(ld_shlibs, $1)=no cat <<_LT_EOF 1>&2 *** Warning: the GNU linker, at least up to release 2.19, is reported *** to be unable to reliably create shared libraries on AIX. *** Therefore, libtool is disabling shared libraries support. If you *** really care for shared libraries, you may want to install binutils *** 2.20 or above, or modify your PATH so that a non-GNU linker is found. *** You will then need to restart the configuration process. _LT_EOF fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='' ;; m68k) _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes ;; esac ;; beos*) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(allow_undefined_flag, $1)=unsupported # Joseph Beckenbach says some releases of gcc # support --undefined. This deserves some investigation. FIXME _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; cygwin* | mingw* | pw32* | cegcc*) # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, # as there is no search path for DLLs. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-all-symbols' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols' _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'] if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' # If the export-symbols file already is a .def file, use it as # is; otherwise, prepend EXPORTS... _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then cp $export_symbols $output_objdir/$soname.def; else echo EXPORTS > $output_objdir/$soname.def; cat $export_symbols >> $output_objdir/$soname.def; fi~ $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; haiku*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(link_all_deplibs, $1)=yes ;; os2*) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(allow_undefined_flag, $1)=unsupported shrext_cmds=.dll _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ prefix_cmds="$SED"~ if test EXPORTS = "`$SED 1q $export_symbols`"; then prefix_cmds="$prefix_cmds -e 1d"; fi~ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes ;; interix[[3-9]]*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. # Instead, shared libraries are loaded at an image base (0x10000000 by # default) and relocated if they conflict, which is a slow very memory # consuming and fragmenting process. To avoid this, we pick a random, # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link # time. Moving up from 0x10000000 also allows more sbrk(2) space. _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ;; gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) tmp_diet=no if test linux-dietlibc = "$host_os"; then case $cc_basename in diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn) esac fi if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \ && test no = "$tmp_diet" then tmp_addflag=' $pic_flag' tmp_sharedflag='-shared' case $cc_basename,$host_cpu in pgcc*) # Portland Group C compiler _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' tmp_addflag=' $pic_flag' ;; pgf77* | pgf90* | pgf95* | pgfortran*) # Portland Group f77 and f90 compilers _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' tmp_addflag=' $pic_flag -Mnomain' ;; ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 tmp_addflag=' -i_dynamic' ;; efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 tmp_addflag=' -i_dynamic -nofor_main' ;; ifc* | ifort*) # Intel Fortran compiler tmp_addflag=' -nofor_main' ;; lf95*) # Lahey Fortran 8.1 _LT_TAGVAR(whole_archive_flag_spec, $1)= tmp_sharedflag='--shared' ;; nagfor*) # NAGFOR 5.3 tmp_sharedflag='-Wl,-shared' ;; xl[[cC]]* | bgxl[[cC]]* | mpixl[[cC]]*) # IBM XL C 8.0 on PPC (deal with xlf below) tmp_sharedflag='-qmkshrobj' tmp_addflag= ;; nvcc*) # Cuda Compiler Driver 2.2 _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' _LT_TAGVAR(compiler_needs_object, $1)=yes ;; esac case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C 5.9 _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' _LT_TAGVAR(compiler_needs_object, $1)=yes tmp_sharedflag='-G' ;; *Sun\ F*) # Sun Fortran 8.3 tmp_sharedflag='-G' ;; esac _LT_TAGVAR(archive_cmds, $1)='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' if test yes = "$supports_anon_versioning"; then _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib' fi case $cc_basename in tcc*) _LT_TAGVAR(export_dynamic_flag_spec, $1)='-rdynamic' ;; xlf* | bgf* | bgxlf* | mpixlf*) # IBM XL Fortran 10.1 on PPC cannot create shared libs itself _LT_TAGVAR(whole_archive_flag_spec, $1)='--whole-archive$convenience --no-whole-archive' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(archive_cmds, $1)='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib' if test yes = "$supports_anon_versioning"; then _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' fi ;; esac else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' wlarc= else _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' fi ;; solaris*) if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then _LT_TAGVAR(ld_shlibs, $1)=no cat <<_LT_EOF 1>&2 *** Warning: The releases 2.8.* of the GNU linker cannot reliably *** create shared libraries on Solaris systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.9.1 or newer. Another option is to modify *** your PATH or compiler configuration so that the native linker is *** used, and then restart. _LT_EOF elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) case `$LD -v 2>&1` in *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.1[[0-5]].*) _LT_TAGVAR(ld_shlibs, $1)=no cat <<_LT_EOF 1>&2 *** Warning: Releases of the GNU linker prior to 2.16.91.0.3 cannot *** reliably create shared libraries on SCO systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.16.91.0.3 or newer. Another option is to modify *** your PATH or compiler configuration so that the native linker is *** used, and then restart. _LT_EOF ;; *) # For security reasons, it is highly recommended that you always # use absolute paths for naming shared libraries, and exclude the # DT_RUNPATH tag from executables and libraries. But doing so # requires that you compile everything twice, which is a pain. if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; sunos4*) _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' wlarc= _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac if test no = "$_LT_TAGVAR(ld_shlibs, $1)"; then runpath_var= _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= fi else # PORTME fill in a description of your system's linker (not GNU ld) case $host_os in aix3*) _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=yes _LT_TAGVAR(archive_expsym_cmds, $1)='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' # Note: this linker hardcodes the directories in LIBPATH if there # are no directories specified by -L. _LT_TAGVAR(hardcode_minus_L, $1)=yes if test yes = "$GCC" && test -z "$lt_prog_compiler_static"; then # Neither direct hardcoding nor static linking is supported with a # broken collect2. _LT_TAGVAR(hardcode_direct, $1)=unsupported fi ;; aix[[4-9]]*) if test ia64 = "$host_cpu"; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no exp_sym_flag='-Bexport' no_entry_flag= else # If we're using GNU nm, then we don't want the "-C" option. # -C means demangle to GNU nm, but means don't demangle to AIX nm. # Without the "-l" option, or with the "-B" option, AIX nm treats # weak defined symbols like other global defined symbols, whereas # GNU nm marks them as "W". # While the 'weak' keyword is ignored in the Export File, we need # it in the Import File for the 'aix-soname' feature, so we have # to replace the "-B" option with "-P" for AIX nm. if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' else _LT_TAGVAR(export_symbols_cmds, $1)='`func_echo_all $NM | $SED -e '\''s/B\([[^B]]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && ([substr](\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' fi aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # have runtime linking enabled, and use it for executables. # For shared libraries, we enable/disable runtime linking # depending on the kind of the shared library created - # when "with_aix_soname,aix_use_runtimelinking" is: # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables # "aix,yes" lib.so shared, rtl:yes, for executables # lib.a static archive # "both,no" lib.so.V(shr.o) shared, rtl:yes # lib.a(lib.so.V) shared, rtl:no, for executables # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a(lib.so.V) shared, rtl:no # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a static archive case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) for ld_flag in $LDFLAGS; do if (test x-brtl = "x$ld_flag" || test x-Wl,-brtl = "x$ld_flag"); then aix_use_runtimelinking=yes break fi done if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then # With aix-soname=svr4, we create the lib.so.V shared archives only, # so we don't have lib.a shared libs to link our executables. # We have to force runtime linking in this case. aix_use_runtimelinking=yes LDFLAGS="$LDFLAGS -Wl,-brtl" fi ;; esac exp_sym_flag='-bexport' no_entry_flag='-bnoentry' fi # When large executables or shared objects are built, AIX ld can # have problems creating the table of contents. If linking a library # or program results in "error TOC overflow" add -mminimal-toc to # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. _LT_TAGVAR(archive_cmds, $1)='' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(file_list_spec, $1)='$wl-f,' case $with_aix_soname,$aix_use_runtimelinking in aix,*) ;; # traditional, no import file svr4,* | *,yes) # use import file # The Import File defines what to hardcode. _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no ;; esac if test yes = "$GCC"; then case $host_os in aix4.[[012]]|aix4.[[012]].*) # We only want to do this on AIX 4.2 and lower, the check # below for broken collect2 doesn't work under 4.3+ collect2name=`$CC -print-prog-name=collect2` if test -f "$collect2name" && strings "$collect2name" | $GREP resolve_lib_name >/dev/null then # We have reworked collect2 : else # We have old collect2 _LT_TAGVAR(hardcode_direct, $1)=unsupported # It fails to find uninstalled libraries when the uninstalled # path is not listed in the libpath. Setting hardcode_minus_L # to unsupported forces relinking _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)= fi ;; esac shared_flag='-shared' if test yes = "$aix_use_runtimelinking"; then shared_flag="$shared_flag "'$wl-G' fi # Need to ensure runtime linking is disabled for the traditional # shared library, or the linker may eventually find shared libraries # /with/ Import File - we do not want to mix them. shared_flag_aix='-shared' shared_flag_svr4='-shared $wl-G' else # not using gcc if test ia64 = "$host_cpu"; then # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release # chokes on -Wl,-G. The following line is correct: shared_flag='-G' else if test yes = "$aix_use_runtimelinking"; then shared_flag='$wl-G' else shared_flag='$wl-bM:SRE' fi shared_flag_aix='$wl-bM:SRE' shared_flag_svr4='$wl-G' fi fi _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-bexpall' # It seems that -bexpall does not export symbols beginning with # underscore (_), so it is better to generate a list of symbols to export. _LT_TAGVAR(always_export_symbols, $1)=yes if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then # Warning - without using the other runtime loading flags (-brtl), # -berok will link without error, but may produce a broken library. _LT_TAGVAR(allow_undefined_flag, $1)='-berok' # Determine the default libpath from the value encoded in an # empty executable. _LT_SYS_MODULE_PATH_AIX([$1]) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag else if test ia64 = "$host_cpu"; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $libdir:/usr/lib:/lib' _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols" else # Determine the default libpath from the value encoded in an # empty executable. _LT_SYS_MODULE_PATH_AIX([$1]) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" # Warning - without using the other run time loading flags, # -berok will link without error, but may produce a broken library. _LT_TAGVAR(no_undefined_flag, $1)=' $wl-bernotok' _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-berok' if test yes = "$with_gnu_ld"; then # We only use this code for GNU lds that support --whole-archive. _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive' else # Exported symbols can be pulled into shared objects from archives _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' fi _LT_TAGVAR(archive_cmds_need_lc, $1)=yes _LT_TAGVAR(archive_expsym_cmds, $1)='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d' # -brtl affects multiple linker settings, -berok does not and is overridden later compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([[, ]]\\)%-berok\\1%g"`' if test svr4 != "$with_aix_soname"; then # This is similar to how AIX traditionally builds its shared libraries. _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname' fi if test aix != "$with_aix_soname"; then _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp' else # used by -dlpreopen to get the symbols _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$MV $output_objdir/$realname.d/$soname $output_objdir' fi _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$RM -r $output_objdir/$realname.d' fi fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='' ;; m68k) _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes ;; esac ;; bsdi[[45]]*) _LT_TAGVAR(export_dynamic_flag_spec, $1)=-rdynamic ;; cygwin* | mingw* | pw32* | cegcc*) # When not using gcc, we currently assume that we are using # Microsoft Visual C++. # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. case $cc_basename in cl*) # Native MSVC _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=yes _LT_TAGVAR(file_list_spec, $1)='@' # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=.dll # FIXME: Setting linknames here is a bad hack. _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames=' _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then cp "$export_symbols" "$output_objdir/$soname.def"; echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp"; else $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp; fi~ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ linknames=' # The linker will not automatically build a static lib if we build a DLL. # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1,DATA/'\'' | $SED -e '\''/^[[AITW]][[ ]]/s/.*[[ ]]//'\'' | sort | uniq > $export_symbols' # Don't use ranlib _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib' _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~ lt_tool_outputfile="@TOOL_OUTPUT@"~ case $lt_outputfile in *.exe|*.EXE) ;; *) lt_outputfile=$lt_outputfile.exe lt_tool_outputfile=$lt_tool_outputfile.exe ;; esac~ if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; $RM "$lt_outputfile.manifest"; fi' ;; *) # Assume MSVC wrapper _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=.dll # FIXME: Setting linknames here is a bad hack. _LT_TAGVAR(archive_cmds, $1)='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames=' # The linker will automatically build a .lib file if we build a DLL. _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' # FIXME: Should let the user specify the lib program. _LT_TAGVAR(old_archive_cmds, $1)='lib -OUT:$oldlib$oldobjs$old_deplibs' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes ;; esac ;; darwin* | rhapsody*) _LT_DARWIN_LINKER_FEATURES($1) ;; dgux*) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor # support. Future versions do this automatically, but an explicit c++rt0.o # does not break anything, and helps significantly (at the cost of a little # extra space). freebsd2.2*) _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; # Unfortunately, older versions of FreeBSD 2 do not have this feature. freebsd2.*) _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; # FreeBSD 3 and greater uses gcc -shared to do shared libraries. freebsd* | dragonfly*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; hpux9*) if test yes = "$GCC"; then _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' else _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(hardcode_direct, $1)=yes # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' ;; hpux10*) if test yes,no = "$GCC,$with_gnu_ld"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' fi if test no = "$with_gnu_ld"; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. _LT_TAGVAR(hardcode_minus_L, $1)=yes fi ;; hpux11*) if test yes,no = "$GCC,$with_gnu_ld"; then case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' ;; esac else case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) m4_if($1, [], [ # Older versions of the 11.00 compiler do not understand -b yet # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does) _LT_LINKER_OPTION([if $CC understands -b], _LT_TAGVAR(lt_cv_prog_compiler__b, $1), [-b], [_LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'], [_LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'])], [_LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags']) ;; esac fi if test no = "$with_gnu_ld"; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: case $host_cpu in hppa*64*|ia64*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. _LT_TAGVAR(hardcode_minus_L, $1)=yes ;; esac fi ;; irix5* | irix6* | nonstopux*) if test yes = "$GCC"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' # Try to use the -exported_symbol ld option, if it does not # work, assume that -exports_file does not work either and # implicitly export all symbols. # This should be the same for all languages, so no per-tag cache variable. AC_CACHE_CHECK([whether the $host_os linker accepts -exported_symbol], [lt_cv_irix_exported_symbol], [save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS -shared $wl-exported_symbol ${wl}foo $wl-update_registry $wl/dev/null" AC_LINK_IFELSE( [AC_LANG_SOURCE( [AC_LANG_CASE([C], [[int foo (void) { return 0; }]], [C++], [[int foo (void) { return 0; }]], [Fortran 77], [[ subroutine foo end]], [Fortran], [[ subroutine foo end]])])], [lt_cv_irix_exported_symbol=yes], [lt_cv_irix_exported_symbol=no]) LDFLAGS=$save_LDFLAGS]) if test yes = "$lt_cv_irix_exported_symbol"; then _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations $wl-exports_file $wl$export_symbols -o $lib' fi _LT_TAGVAR(link_all_deplibs, $1)=no else _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -exports_file $export_symbols -o $lib' fi _LT_TAGVAR(archive_cmds_need_lc, $1)='no' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(inherit_rpath, $1)=yes _LT_TAGVAR(link_all_deplibs, $1)=yes ;; linux*) case $cc_basename in tcc*) # Fabrice Bellard et al's Tiny C Compiler _LT_TAGVAR(ld_shlibs, $1)=yes _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out else _LT_TAGVAR(archive_cmds, $1)='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; newsos6) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *nto* | *qnx*) ;; openbsd* | bitrig*) if test -f /usr/libexec/ld.so; then _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=yes if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags $wl-retain-symbols-file,$export_symbols' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' else _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' fi else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; os2*) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(allow_undefined_flag, $1)=unsupported shrext_cmds=.dll _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ prefix_cmds="$SED"~ if test EXPORTS = "`$SED 1q $export_symbols`"; then prefix_cmds="$prefix_cmds -e 1d"; fi~ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes ;; osf3*) if test yes = "$GCC"; then _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' else _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' fi _LT_TAGVAR(archive_cmds_need_lc, $1)='no' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: ;; osf4* | osf5*) # as osf3* with the addition of -msym flag if test yes = "$GCC"; then _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $pic_flag $libobjs $deplibs $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' else _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~ $CC -shared$allow_undefined_flag $wl-input $wl$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~$RM $lib.exp' # Both c and cxx compiler support -rpath directly _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' fi _LT_TAGVAR(archive_cmds_need_lc, $1)='no' _LT_TAGVAR(hardcode_libdir_separator, $1)=: ;; solaris*) _LT_TAGVAR(no_undefined_flag, $1)=' -z defs' if test yes = "$GCC"; then wlarc='$wl' _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl-z ${wl}text $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -shared $pic_flag $wl-z ${wl}text $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' else case `$CC -V 2>&1` in *"Compilers 5.0"*) wlarc='' _LT_TAGVAR(archive_cmds, $1)='$LD -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $LD -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' ;; *) wlarc='$wl' _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' ;; esac fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no case $host_os in solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; *) # The compiler driver will combine and reorder linker options, # but understands '-z linker_flag'. GCC discards it without '$wl', # but is careful enough not to reorder. # Supported since Solaris 2.6 (maybe 2.5.1?) if test yes = "$GCC"; then _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract' else _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' fi ;; esac _LT_TAGVAR(link_all_deplibs, $1)=yes ;; sunos4*) if test sequent = "$host_vendor"; then # Use $CC to link under sequent, because it throws in some extra .o # files that make .init and .fini sections work. _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h $soname -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; sysv4) case $host_vendor in sni) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=yes # is this really true??? ;; siemens) ## LD is ld it makes a PLAMLIB ## CC just makes a GrossModule. _LT_TAGVAR(archive_cmds, $1)='$LD -G -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(reload_cmds, $1)='$CC -r -o $output$reload_objs' _LT_TAGVAR(hardcode_direct, $1)=no ;; motorola) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=no #Motorola manual says yes, but my tests say they lie ;; esac runpath_var='LD_RUN_PATH' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; sysv4.3*) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(export_dynamic_flag_spec, $1)='-Bexport' ;; sysv4*MP*) if test -d /usr/nec; then _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no runpath_var=LD_RUN_PATH hardcode_runpath_var=yes _LT_TAGVAR(ld_shlibs, $1)=yes fi ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no runpath_var='LD_RUN_PATH' if test yes = "$GCC"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; sysv5* | sco3.2v5* | sco5v6*) # Note: We CANNOT use -z defs as we might desire, because we do not # link with -lc, and that would cause any symbols used from libc to # always be unresolved, which means just about no library would # ever link correctly. If we're not using GNU ld we use -z text # though, which does catch some bad symbols but isn't as heavy-handed # as -z defs. _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' _LT_TAGVAR(allow_undefined_flag, $1)='$wl-z,nodefs' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R,$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Bexport' runpath_var='LD_RUN_PATH' if test yes = "$GCC"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; uts4*) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) _LT_TAGVAR(ld_shlibs, $1)=no ;; esac if test sni = "$host_vendor"; then case $host in sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Blargedynsym' ;; esac fi fi ]) AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) test no = "$_LT_TAGVAR(ld_shlibs, $1)" && can_build_shared=no _LT_TAGVAR(with_gnu_ld, $1)=$with_gnu_ld _LT_DECL([], [libext], [0], [Old archive suffix (normally "a")])dnl _LT_DECL([], [shrext_cmds], [1], [Shared library suffix (normally ".so")])dnl _LT_DECL([], [extract_expsyms_cmds], [2], [The commands to extract the exported symbol list from a shared archive]) # # Do we need to explicitly link libc? # case "x$_LT_TAGVAR(archive_cmds_need_lc, $1)" in x|xyes) # Assume -lc should be added _LT_TAGVAR(archive_cmds_need_lc, $1)=yes if test yes,yes = "$GCC,$enable_shared"; then case $_LT_TAGVAR(archive_cmds, $1) in *'~'*) # FIXME: we may have to deal with multi-command sequences. ;; '$CC '*) # Test whether the compiler implicitly links with -lc since on some # systems, -lgcc has to come before -lc. If gcc already passes -lc # to ld, don't add -lc before -lgcc. AC_CACHE_CHECK([whether -lc should be explicitly linked in], [lt_cv_]_LT_TAGVAR(archive_cmds_need_lc, $1), [$RM conftest* echo "$lt_simple_compile_test_code" > conftest.$ac_ext if AC_TRY_EVAL(ac_compile) 2>conftest.err; then soname=conftest lib=conftest libobjs=conftest.$ac_objext deplibs= wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) pic_flag=$_LT_TAGVAR(lt_prog_compiler_pic, $1) compiler_flags=-v linker_flags=-v verstring= output_objdir=. libname=conftest lt_save_allow_undefined_flag=$_LT_TAGVAR(allow_undefined_flag, $1) _LT_TAGVAR(allow_undefined_flag, $1)= if AC_TRY_EVAL(_LT_TAGVAR(archive_cmds, $1) 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) then lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=no else lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=yes fi _LT_TAGVAR(allow_undefined_flag, $1)=$lt_save_allow_undefined_flag else cat conftest.err 1>&5 fi $RM conftest* ]) _LT_TAGVAR(archive_cmds_need_lc, $1)=$lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1) ;; esac fi ;; esac _LT_TAGDECL([build_libtool_need_lc], [archive_cmds_need_lc], [0], [Whether or not to add -lc for building shared libraries]) _LT_TAGDECL([allow_libtool_libs_with_static_runtimes], [enable_shared_with_static_runtimes], [0], [Whether or not to disallow shared libs when runtime libs are static]) _LT_TAGDECL([], [export_dynamic_flag_spec], [1], [Compiler flag to allow reflexive dlopens]) _LT_TAGDECL([], [whole_archive_flag_spec], [1], [Compiler flag to generate shared objects directly from archives]) _LT_TAGDECL([], [compiler_needs_object], [1], [Whether the compiler copes with passing no objects directly]) _LT_TAGDECL([], [old_archive_from_new_cmds], [2], [Create an old-style archive from a shared archive]) _LT_TAGDECL([], [old_archive_from_expsyms_cmds], [2], [Create a temporary old-style archive to link instead of a shared archive]) _LT_TAGDECL([], [archive_cmds], [2], [Commands used to build a shared archive]) _LT_TAGDECL([], [archive_expsym_cmds], [2]) _LT_TAGDECL([], [module_cmds], [2], [Commands used to build a loadable module if different from building a shared archive.]) _LT_TAGDECL([], [module_expsym_cmds], [2]) _LT_TAGDECL([], [with_gnu_ld], [1], [Whether we are building with GNU ld or not]) _LT_TAGDECL([], [allow_undefined_flag], [1], [Flag that allows shared libraries with undefined symbols to be built]) _LT_TAGDECL([], [no_undefined_flag], [1], [Flag that enforces no undefined symbols]) _LT_TAGDECL([], [hardcode_libdir_flag_spec], [1], [Flag to hardcode $libdir into a binary during linking. This must work even if $libdir does not exist]) _LT_TAGDECL([], [hardcode_libdir_separator], [1], [Whether we need a single "-rpath" flag with a separated argument]) _LT_TAGDECL([], [hardcode_direct], [0], [Set to "yes" if using DIR/libNAME$shared_ext during linking hardcodes DIR into the resulting binary]) _LT_TAGDECL([], [hardcode_direct_absolute], [0], [Set to "yes" if using DIR/libNAME$shared_ext during linking hardcodes DIR into the resulting binary and the resulting library dependency is "absolute", i.e impossible to change by setting $shlibpath_var if the library is relocated]) _LT_TAGDECL([], [hardcode_minus_L], [0], [Set to "yes" if using the -LDIR flag during linking hardcodes DIR into the resulting binary]) _LT_TAGDECL([], [hardcode_shlibpath_var], [0], [Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR into the resulting binary]) _LT_TAGDECL([], [hardcode_automatic], [0], [Set to "yes" if building a shared library automatically hardcodes DIR into the library and all subsequent libraries and executables linked against it]) _LT_TAGDECL([], [inherit_rpath], [0], [Set to yes if linker adds runtime paths of dependent libraries to runtime path list]) _LT_TAGDECL([], [link_all_deplibs], [0], [Whether libtool must link a program against all its dependency libraries]) _LT_TAGDECL([], [always_export_symbols], [0], [Set to "yes" if exported symbols are required]) _LT_TAGDECL([], [export_symbols_cmds], [2], [The commands to list exported symbols]) _LT_TAGDECL([], [exclude_expsyms], [1], [Symbols that should not be listed in the preloaded symbols]) _LT_TAGDECL([], [include_expsyms], [1], [Symbols that must always be exported]) _LT_TAGDECL([], [prelink_cmds], [2], [Commands necessary for linking programs (against libraries) with templates]) _LT_TAGDECL([], [postlink_cmds], [2], [Commands necessary for finishing linking programs]) _LT_TAGDECL([], [file_list_spec], [1], [Specify filename containing input files]) dnl FIXME: Not yet implemented dnl _LT_TAGDECL([], [thread_safe_flag_spec], [1], dnl [Compiler flag to generate thread safe objects]) ])# _LT_LINKER_SHLIBS # _LT_LANG_C_CONFIG([TAG]) # ------------------------ # Ensure that the configuration variables for a C compiler are suitably # defined. These variables are subsequently used by _LT_CONFIG to write # the compiler configuration to 'libtool'. m4_defun([_LT_LANG_C_CONFIG], [m4_require([_LT_DECL_EGREP])dnl lt_save_CC=$CC AC_LANG_PUSH(C) # Source file extension for C test sources. ac_ext=c # Object file extension for compiled C test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code="int some_variable = 0;" # Code to be used in simple link tests lt_simple_link_test_code='int main(){return(0);}' _LT_TAG_COMPILER # Save the default compiler, since it gets overwritten when the other # tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP. compiler_DEFAULT=$CC # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... if test -n "$compiler"; then _LT_COMPILER_NO_RTTI($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) LT_SYS_DLOPEN_SELF _LT_CMD_STRIPLIB # Report what library types will actually be built AC_MSG_CHECKING([if libtool supports shared libraries]) AC_MSG_RESULT([$can_build_shared]) AC_MSG_CHECKING([whether to build shared libraries]) test no = "$can_build_shared" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test yes = "$enable_shared" && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[[4-9]]*) if test ia64 != "$host_cpu"; then case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in yes,aix,yes) ;; # shared object as lib.so file only yes,svr4,*) ;; # shared object as lib.so archive member only yes,*) enable_static=no ;; # shared object in lib.a archive as well esac fi ;; esac AC_MSG_RESULT([$enable_shared]) AC_MSG_CHECKING([whether to build static libraries]) # Make sure either enable_shared or enable_static is yes. test yes = "$enable_shared" || enable_static=yes AC_MSG_RESULT([$enable_static]) _LT_CONFIG($1) fi AC_LANG_POP CC=$lt_save_CC ])# _LT_LANG_C_CONFIG # _LT_LANG_CXX_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for a C++ compiler are suitably # defined. These variables are subsequently used by _LT_CONFIG to write # the compiler configuration to 'libtool'. m4_defun([_LT_LANG_CXX_CONFIG], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_PATH_MANIFEST_TOOL])dnl if test -n "$CXX" && ( test no != "$CXX" && ( (test g++ = "$CXX" && `g++ -v >/dev/null 2>&1` ) || (test g++ != "$CXX"))); then AC_PROG_CXXCPP else _lt_caught_CXX_error=yes fi AC_LANG_PUSH(C++) _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(compiler_needs_object, $1)=no _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds _LT_TAGVAR(no_undefined_flag, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no # Source file extension for C++ test sources. ac_ext=cpp # Object file extension for compiled C++ test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # No sense in running all these tests if we already determined that # the CXX compiler isn't working. Some variables (like enable_shared) # are currently assumed to apply to all compilers on this platform, # and will be corrupted by setting them based on a non-working compiler. if test yes != "$_lt_caught_CXX_error"; then # Code to be used in simple compile tests lt_simple_compile_test_code="int some_variable = 0;" # Code to be used in simple link tests lt_simple_link_test_code='int main(int, char *[[]]) { return(0); }' # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_CFLAGS=$CFLAGS lt_save_LD=$LD lt_save_GCC=$GCC GCC=$GXX lt_save_with_gnu_ld=$with_gnu_ld lt_save_path_LD=$lt_cv_path_LD if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx else $as_unset lt_cv_prog_gnu_ld fi if test -n "${lt_cv_path_LDCXX+set}"; then lt_cv_path_LD=$lt_cv_path_LDCXX else $as_unset lt_cv_path_LD fi test -z "${LDCXX+set}" || LD=$LDCXX CC=${CXX-"c++"} CFLAGS=$CXXFLAGS compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) if test -n "$compiler"; then # We don't want -fno-exception when compiling C++ code, so set the # no_builtin_flag separately if test yes = "$GXX"; then _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' else _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= fi if test yes = "$GXX"; then # Set up default GNU C++ configuration LT_PATH_LD # Check if GNU C++ uses GNU ld as the underlying linker, since the # archiving commands below assume that GNU ld is being used. if test yes = "$with_gnu_ld"; then _LT_TAGVAR(archive_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' # If archive_cmds runs LD, not CC, wlarc should be empty # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to # investigate it a little bit more. (MM) wlarc='$wl' # ancient GNU ld didn't support --whole-archive et. al. if eval "`$CC -print-prog-name=ld` --help 2>&1" | $GREP 'no-whole-archive' > /dev/null; then _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' else _LT_TAGVAR(whole_archive_flag_spec, $1)= fi else with_gnu_ld=no wlarc= # A generic and very simple default shared library creation # command for GNU C++ for the case where it uses the native # linker, instead of GNU ld. If possible, this setting should # overridden to take advantage of the native linker features on # the platform it is being used on. _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' fi # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"' else GXX=no with_gnu_ld=no wlarc= fi # PORTME: fill in a description of your system's C++ link characteristics AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) _LT_TAGVAR(ld_shlibs, $1)=yes case $host_os in aix3*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; aix[[4-9]]*) if test ia64 = "$host_cpu"; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no exp_sym_flag='-Bexport' no_entry_flag= else aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # have runtime linking enabled, and use it for executables. # For shared libraries, we enable/disable runtime linking # depending on the kind of the shared library created - # when "with_aix_soname,aix_use_runtimelinking" is: # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables # "aix,yes" lib.so shared, rtl:yes, for executables # lib.a static archive # "both,no" lib.so.V(shr.o) shared, rtl:yes # lib.a(lib.so.V) shared, rtl:no, for executables # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a(lib.so.V) shared, rtl:no # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a static archive case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) for ld_flag in $LDFLAGS; do case $ld_flag in *-brtl*) aix_use_runtimelinking=yes break ;; esac done if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then # With aix-soname=svr4, we create the lib.so.V shared archives only, # so we don't have lib.a shared libs to link our executables. # We have to force runtime linking in this case. aix_use_runtimelinking=yes LDFLAGS="$LDFLAGS -Wl,-brtl" fi ;; esac exp_sym_flag='-bexport' no_entry_flag='-bnoentry' fi # When large executables or shared objects are built, AIX ld can # have problems creating the table of contents. If linking a library # or program results in "error TOC overflow" add -mminimal-toc to # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. _LT_TAGVAR(archive_cmds, $1)='' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(file_list_spec, $1)='$wl-f,' case $with_aix_soname,$aix_use_runtimelinking in aix,*) ;; # no import file svr4,* | *,yes) # use import file # The Import File defines what to hardcode. _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no ;; esac if test yes = "$GXX"; then case $host_os in aix4.[[012]]|aix4.[[012]].*) # We only want to do this on AIX 4.2 and lower, the check # below for broken collect2 doesn't work under 4.3+ collect2name=`$CC -print-prog-name=collect2` if test -f "$collect2name" && strings "$collect2name" | $GREP resolve_lib_name >/dev/null then # We have reworked collect2 : else # We have old collect2 _LT_TAGVAR(hardcode_direct, $1)=unsupported # It fails to find uninstalled libraries when the uninstalled # path is not listed in the libpath. Setting hardcode_minus_L # to unsupported forces relinking _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)= fi esac shared_flag='-shared' if test yes = "$aix_use_runtimelinking"; then shared_flag=$shared_flag' $wl-G' fi # Need to ensure runtime linking is disabled for the traditional # shared library, or the linker may eventually find shared libraries # /with/ Import File - we do not want to mix them. shared_flag_aix='-shared' shared_flag_svr4='-shared $wl-G' else # not using gcc if test ia64 = "$host_cpu"; then # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release # chokes on -Wl,-G. The following line is correct: shared_flag='-G' else if test yes = "$aix_use_runtimelinking"; then shared_flag='$wl-G' else shared_flag='$wl-bM:SRE' fi shared_flag_aix='$wl-bM:SRE' shared_flag_svr4='$wl-G' fi fi _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-bexpall' # It seems that -bexpall does not export symbols beginning with # underscore (_), so it is better to generate a list of symbols to # export. _LT_TAGVAR(always_export_symbols, $1)=yes if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then # Warning - without using the other runtime loading flags (-brtl), # -berok will link without error, but may produce a broken library. # The "-G" linker flag allows undefined symbols. _LT_TAGVAR(no_undefined_flag, $1)='-bernotok' # Determine the default libpath from the value encoded in an empty # executable. _LT_SYS_MODULE_PATH_AIX([$1]) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag else if test ia64 = "$host_cpu"; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $libdir:/usr/lib:/lib' _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols" else # Determine the default libpath from the value encoded in an # empty executable. _LT_SYS_MODULE_PATH_AIX([$1]) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" # Warning - without using the other run time loading flags, # -berok will link without error, but may produce a broken library. _LT_TAGVAR(no_undefined_flag, $1)=' $wl-bernotok' _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-berok' if test yes = "$with_gnu_ld"; then # We only use this code for GNU lds that support --whole-archive. _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive' else # Exported symbols can be pulled into shared objects from archives _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' fi _LT_TAGVAR(archive_cmds_need_lc, $1)=yes _LT_TAGVAR(archive_expsym_cmds, $1)='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d' # -brtl affects multiple linker settings, -berok does not and is overridden later compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([[, ]]\\)%-berok\\1%g"`' if test svr4 != "$with_aix_soname"; then # This is similar to how AIX traditionally builds its shared # libraries. Need -bnortl late, we may have -brtl in LDFLAGS. _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname' fi if test aix != "$with_aix_soname"; then _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp' else # used by -dlpreopen to get the symbols _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$MV $output_objdir/$realname.d/$soname $output_objdir' fi _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$RM -r $output_objdir/$realname.d' fi fi ;; beos*) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(allow_undefined_flag, $1)=unsupported # Joseph Beckenbach says some releases of gcc # support --undefined. This deserves some investigation. FIXME _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; chorus*) case $cc_basename in *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; cygwin* | mingw* | pw32* | cegcc*) case $GXX,$cc_basename in ,cl* | no,cl*) # Native MSVC # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=yes _LT_TAGVAR(file_list_spec, $1)='@' # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=.dll # FIXME: Setting linknames here is a bad hack. _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames=' _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then cp "$export_symbols" "$output_objdir/$soname.def"; echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp"; else $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp; fi~ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ linknames=' # The linker will not automatically build a static lib if we build a DLL. # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes # Don't use ranlib _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib' _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~ lt_tool_outputfile="@TOOL_OUTPUT@"~ case $lt_outputfile in *.exe|*.EXE) ;; *) lt_outputfile=$lt_outputfile.exe lt_tool_outputfile=$lt_tool_outputfile.exe ;; esac~ func_to_tool_file "$lt_outputfile"~ if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; $RM "$lt_outputfile.manifest"; fi' ;; *) # g++ # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, # as there is no search path for DLLs. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-all-symbols' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' # If the export-symbols file already is a .def file, use it as # is; otherwise, prepend EXPORTS... _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then cp $export_symbols $output_objdir/$soname.def; else echo EXPORTS > $output_objdir/$soname.def; cat $export_symbols >> $output_objdir/$soname.def; fi~ $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; darwin* | rhapsody*) _LT_DARWIN_LINKER_FEATURES($1) ;; os2*) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(allow_undefined_flag, $1)=unsupported shrext_cmds=.dll _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ prefix_cmds="$SED"~ if test EXPORTS = "`$SED 1q $export_symbols`"; then prefix_cmds="$prefix_cmds -e 1d"; fi~ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes ;; dgux*) case $cc_basename in ec++*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; ghcx*) # Green Hills C++ Compiler # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; freebsd2.*) # C++ shared libraries reported to be fairly broken before # switch to ELF _LT_TAGVAR(ld_shlibs, $1)=no ;; freebsd-elf*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;; freebsd* | dragonfly*) # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF # conventions _LT_TAGVAR(ld_shlibs, $1)=yes ;; haiku*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(link_all_deplibs, $1)=yes ;; hpux9*) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, # but as the default # location of the library. case $cc_basename in CC*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; aCC*) _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -b $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP " \-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test yes = "$GXX"; then _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -nostdlib $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' else # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; hpux10*|hpux11*) if test no = "$with_gnu_ld"; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: case $host_cpu in hppa*64*|ia64*) ;; *) _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' ;; esac fi case $host_cpu in hppa*64*|ia64*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, # but as the default # location of the library. ;; esac case $cc_basename in CC*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; aCC*) case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; esac # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP " \-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test yes = "$GXX"; then if test no = "$with_gnu_ld"; then case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; esac fi else # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; interix[[3-9]]*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. # Instead, shared libraries are loaded at an image base (0x10000000 by # default) and relocated if they conflict, which is a slow very memory # consuming and fragmenting process. To avoid this, we pick a random, # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link # time. Moving up from 0x10000000 also allows more sbrk(2) space. _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ;; irix5* | irix6*) case $cc_basename in CC*) # SGI C++ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' # Archives containing C++ object files must be created using # "CC -ar", where "CC" is the IRIX C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC -ar -WR,-u -o $oldlib $oldobjs' ;; *) if test yes = "$GXX"; then if test no = "$with_gnu_ld"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' else _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` -o $lib' fi fi _LT_TAGVAR(link_all_deplibs, $1)=yes ;; esac _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(inherit_rpath, $1)=yes ;; linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in KCC*) # Kuck and Associates, Inc. (KAI) C++ Compiler # KCC will only create a shared library if the output file # ends with ".so" (or ".sl" for HP-UX), so rename the library # to its proper name (with version) after linking. _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib $wl-retain-symbols-file,$export_symbols; mv \$templib $lib' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' # Archives containing C++ object files must be created using # "CC -Bstatic", where "CC" is the KAI C++ compiler. _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;; icpc* | ecpc* ) # Intel C++ with_gnu_ld=yes # version 8.0 and above of icpc choke on multiply defined symbols # if we add $predep_objects and $postdep_objects, however 7.1 and # earlier do not add the objects themselves. case `$CC -V 2>&1` in *"Version 7."*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; *) # Version 8.0 or newer tmp_idyn= case $host_cpu in ia64*) tmp_idyn=' -i_dynamic';; esac _LT_TAGVAR(archive_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; esac _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive' ;; pgCC* | pgcpp*) # Portland Group C++ compiler case `$CC -V` in *pgCC\ [[1-5]].* | *pgcpp\ [[1-5]].*) _LT_TAGVAR(prelink_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~ compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"' _LT_TAGVAR(old_archive_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~ $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~ $RANLIB $oldlib' _LT_TAGVAR(archive_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; *) # Version 6 and above use weak symbols _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; esac _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl--rpath $wl$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' ;; cxx*) # Compaq C++ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib $wl-retain-symbols-file $wl$export_symbols' runpath_var=LD_RUN_PATH _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "X$list" | $Xsed' ;; xl* | mpixl* | bgxl*) # IBM XL 8.0 on PPC, with GNU ld _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' _LT_TAGVAR(archive_cmds, $1)='$CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' if test yes = "$supports_anon_versioning"; then _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib' fi ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file $wl$export_symbols' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' _LT_TAGVAR(compiler_needs_object, $1)=yes # Not sure whether something based on # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 # would be better. output_verbose_link_cmd='func_echo_all' # Archives containing C++ object files must be created using # "CC -xar", where "CC" is the Sun C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' ;; esac ;; esac ;; lynxos*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; m88k*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; mvs*) case $cc_basename in cxx*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; netbsd*) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' wlarc= _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no fi # Workaround some broken pre-1.5 toolchains output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"' ;; *nto* | *qnx*) _LT_TAGVAR(ld_shlibs, $1)=yes ;; openbsd* | bitrig*) if test -f /usr/libexec/ld.so; then _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`"; then _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file,$export_symbols -o $lib' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' fi output_verbose_link_cmd=func_echo_all else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; osf3* | osf4* | osf5*) case $cc_basename in KCC*) # Kuck and Associates, Inc. (KAI) C++ Compiler # KCC will only create a shared library if the output file # ends with ".so" (or ".sl" for HP-UX), so rename the library # to its proper name (with version) after linking. _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Archives containing C++ object files must be created using # the KAI C++ compiler. case $host in osf3*) _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;; *) _LT_TAGVAR(old_archive_cmds, $1)='$CC -o $oldlib $oldobjs' ;; esac ;; RCC*) # Rational C++ 2.4.1 # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; cxx*) case $host in osf3*) _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $soname `test -n "$verstring" && func_echo_all "$wl-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' ;; *) _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ echo "-hidden">> $lib.exp~ $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname $wl-input $wl$lib.exp `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~ $RM $lib.exp' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' ;; esac _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test yes,no = "$GXX,$with_gnu_ld"; then _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' case $host in osf3*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' ;; esac _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"' else # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; psos*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; sunos4*) case $cc_basename in CC*) # Sun C++ 4.x # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; lcc*) # Lucid # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; solaris*) case $cc_basename in CC* | sunCC*) # Sun C++ 4.2, 5.x and Centerline C++ _LT_TAGVAR(archive_cmds_need_lc,$1)=yes _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G$allow_undefined_flag $wl-M $wl$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no case $host_os in solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; *) # The compiler driver will combine and reorder linker options, # but understands '-z linker_flag'. # Supported since Solaris 2.6 (maybe 2.5.1?) _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' ;; esac _LT_TAGVAR(link_all_deplibs, $1)=yes output_verbose_link_cmd='func_echo_all' # Archives containing C++ object files must be created using # "CC -xar", where "CC" is the Sun C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' ;; gcx*) # Green Hills C++ Compiler _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' # The C++ compiler must be used to create the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC $LDFLAGS -archive -o $oldlib $oldobjs' ;; *) # GNU C++ compiler with Solaris linker if test yes,no = "$GXX,$with_gnu_ld"; then _LT_TAGVAR(no_undefined_flag, $1)=' $wl-z ${wl}defs' if $CC --version | $GREP -v '^2\.7' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -shared $pic_flag -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"' else # g++ 2.7 appears to require '-G' NOT '-shared' on this # platform. _LT_TAGVAR(archive_cmds, $1)='$CC -G -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"' fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $wl$libdir' case $host_os in solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; *) _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract' ;; esac fi ;; esac ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no runpath_var='LD_RUN_PATH' case $cc_basename in CC*) _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; sysv5* | sco3.2v5* | sco5v6*) # Note: We CANNOT use -z defs as we might desire, because we do not # link with -lc, and that would cause any symbols used from libc to # always be unresolved, which means just about no library would # ever link correctly. If we're not using GNU ld we use -z text # though, which does catch some bad symbols but isn't as heavy-handed # as -z defs. _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' _LT_TAGVAR(allow_undefined_flag, $1)='$wl-z,nodefs' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R,$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Bexport' runpath_var='LD_RUN_PATH' case $cc_basename in CC*) _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(old_archive_cmds, $1)='$CC -Tprelink_objects $oldobjs~ '"$_LT_TAGVAR(old_archive_cmds, $1)" _LT_TAGVAR(reload_cmds, $1)='$CC -Tprelink_objects $reload_objs~ '"$_LT_TAGVAR(reload_cmds, $1)" ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; tandem*) case $cc_basename in NCC*) # NonStop-UX NCC 3.20 # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; vxworks*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) test no = "$_LT_TAGVAR(ld_shlibs, $1)" && can_build_shared=no _LT_TAGVAR(GCC, $1)=$GXX _LT_TAGVAR(LD, $1)=$LD ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... _LT_SYS_HIDDEN_LIBDEPS($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi # test -n "$compiler" CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS LDCXX=$LD LD=$lt_save_LD GCC=$lt_save_GCC with_gnu_ld=$lt_save_with_gnu_ld lt_cv_path_LDCXX=$lt_cv_path_LD lt_cv_path_LD=$lt_save_path_LD lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld fi # test yes != "$_lt_caught_CXX_error" AC_LANG_POP ])# _LT_LANG_CXX_CONFIG # _LT_FUNC_STRIPNAME_CNF # ---------------------- # func_stripname_cnf prefix suffix name # strip PREFIX and SUFFIX off of NAME. # PREFIX and SUFFIX must not contain globbing or regex special # characters, hashes, percent signs, but SUFFIX may contain a leading # dot (in which case that matches only a dot). # # This function is identical to the (non-XSI) version of func_stripname, # except this one can be used by m4 code that may be executed by configure, # rather than the libtool script. m4_defun([_LT_FUNC_STRIPNAME_CNF],[dnl AC_REQUIRE([_LT_DECL_SED]) AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH]) func_stripname_cnf () { case @S|@2 in .*) func_stripname_result=`$ECHO "@S|@3" | $SED "s%^@S|@1%%; s%\\\\@S|@2\$%%"`;; *) func_stripname_result=`$ECHO "@S|@3" | $SED "s%^@S|@1%%; s%@S|@2\$%%"`;; esac } # func_stripname_cnf ])# _LT_FUNC_STRIPNAME_CNF # _LT_SYS_HIDDEN_LIBDEPS([TAGNAME]) # --------------------------------- # Figure out "hidden" library dependencies from verbose # compiler output when linking a shared library. # Parse the compiler output and extract the necessary # objects, libraries and library flags. m4_defun([_LT_SYS_HIDDEN_LIBDEPS], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl AC_REQUIRE([_LT_FUNC_STRIPNAME_CNF])dnl # Dependencies to place before and after the object being linked: _LT_TAGVAR(predep_objects, $1)= _LT_TAGVAR(postdep_objects, $1)= _LT_TAGVAR(predeps, $1)= _LT_TAGVAR(postdeps, $1)= _LT_TAGVAR(compiler_lib_search_path, $1)= dnl we can't use the lt_simple_compile_test_code here, dnl because it contains code intended for an executable, dnl not a library. It's possible we should let each dnl tag define a new lt_????_link_test_code variable, dnl but it's only used here... m4_if([$1], [], [cat > conftest.$ac_ext <<_LT_EOF int a; void foo (void) { a = 0; } _LT_EOF ], [$1], [CXX], [cat > conftest.$ac_ext <<_LT_EOF class Foo { public: Foo (void) { a = 0; } private: int a; }; _LT_EOF ], [$1], [F77], [cat > conftest.$ac_ext <<_LT_EOF subroutine foo implicit none integer*4 a a=0 return end _LT_EOF ], [$1], [FC], [cat > conftest.$ac_ext <<_LT_EOF subroutine foo implicit none integer a a=0 return end _LT_EOF ], [$1], [GCJ], [cat > conftest.$ac_ext <<_LT_EOF public class foo { private int a; public void bar (void) { a = 0; } }; _LT_EOF ], [$1], [GO], [cat > conftest.$ac_ext <<_LT_EOF package foo func foo() { } _LT_EOF ]) _lt_libdeps_save_CFLAGS=$CFLAGS case "$CC $CFLAGS " in #( *\ -flto*\ *) CFLAGS="$CFLAGS -fno-lto" ;; *\ -fwhopr*\ *) CFLAGS="$CFLAGS -fno-whopr" ;; *\ -fuse-linker-plugin*\ *) CFLAGS="$CFLAGS -fno-use-linker-plugin" ;; esac dnl Parse the compiler output and extract the necessary dnl objects, libraries and library flags. if AC_TRY_EVAL(ac_compile); then # Parse the compiler output and extract the necessary # objects, libraries and library flags. # Sentinel used to keep track of whether or not we are before # the conftest object file. pre_test_object_deps_done=no for p in `eval "$output_verbose_link_cmd"`; do case $prev$p in -L* | -R* | -l*) # Some compilers place space between "-{L,R}" and the path. # Remove the space. if test x-L = "$p" || test x-R = "$p"; then prev=$p continue fi # Expand the sysroot to ease extracting the directories later. if test -z "$prev"; then case $p in -L*) func_stripname_cnf '-L' '' "$p"; prev=-L; p=$func_stripname_result ;; -R*) func_stripname_cnf '-R' '' "$p"; prev=-R; p=$func_stripname_result ;; -l*) func_stripname_cnf '-l' '' "$p"; prev=-l; p=$func_stripname_result ;; esac fi case $p in =*) func_stripname_cnf '=' '' "$p"; p=$lt_sysroot$func_stripname_result ;; esac if test no = "$pre_test_object_deps_done"; then case $prev in -L | -R) # Internal compiler library paths should come after those # provided the user. The postdeps already come after the # user supplied libs so there is no need to process them. if test -z "$_LT_TAGVAR(compiler_lib_search_path, $1)"; then _LT_TAGVAR(compiler_lib_search_path, $1)=$prev$p else _LT_TAGVAR(compiler_lib_search_path, $1)="${_LT_TAGVAR(compiler_lib_search_path, $1)} $prev$p" fi ;; # The "-l" case would never come before the object being # linked, so don't bother handling this case. esac else if test -z "$_LT_TAGVAR(postdeps, $1)"; then _LT_TAGVAR(postdeps, $1)=$prev$p else _LT_TAGVAR(postdeps, $1)="${_LT_TAGVAR(postdeps, $1)} $prev$p" fi fi prev= ;; *.lto.$objext) ;; # Ignore GCC LTO objects *.$objext) # This assumes that the test object file only shows up # once in the compiler output. if test "$p" = "conftest.$objext"; then pre_test_object_deps_done=yes continue fi if test no = "$pre_test_object_deps_done"; then if test -z "$_LT_TAGVAR(predep_objects, $1)"; then _LT_TAGVAR(predep_objects, $1)=$p else _LT_TAGVAR(predep_objects, $1)="$_LT_TAGVAR(predep_objects, $1) $p" fi else if test -z "$_LT_TAGVAR(postdep_objects, $1)"; then _LT_TAGVAR(postdep_objects, $1)=$p else _LT_TAGVAR(postdep_objects, $1)="$_LT_TAGVAR(postdep_objects, $1) $p" fi fi ;; *) ;; # Ignore the rest. esac done # Clean up. rm -f a.out a.exe else echo "libtool.m4: error: problem compiling $1 test program" fi $RM -f confest.$objext CFLAGS=$_lt_libdeps_save_CFLAGS # PORTME: override above test on systems where it is broken m4_if([$1], [CXX], [case $host_os in interix[[3-9]]*) # Interix 3.5 installs completely hosed .la files for C++, so rather than # hack all around it, let's just trust "g++" to DTRT. _LT_TAGVAR(predep_objects,$1)= _LT_TAGVAR(postdep_objects,$1)= _LT_TAGVAR(postdeps,$1)= ;; esac ]) case " $_LT_TAGVAR(postdeps, $1) " in *" -lc "*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;; esac _LT_TAGVAR(compiler_lib_search_dirs, $1)= if test -n "${_LT_TAGVAR(compiler_lib_search_path, $1)}"; then _LT_TAGVAR(compiler_lib_search_dirs, $1)=`echo " ${_LT_TAGVAR(compiler_lib_search_path, $1)}" | $SED -e 's! -L! !g' -e 's!^ !!'` fi _LT_TAGDECL([], [compiler_lib_search_dirs], [1], [The directories searched by this compiler when creating a shared library]) _LT_TAGDECL([], [predep_objects], [1], [Dependencies to place before and after the objects being linked to create a shared library]) _LT_TAGDECL([], [postdep_objects], [1]) _LT_TAGDECL([], [predeps], [1]) _LT_TAGDECL([], [postdeps], [1]) _LT_TAGDECL([], [compiler_lib_search_path], [1], [The library search path used internally by the compiler when linking a shared library]) ])# _LT_SYS_HIDDEN_LIBDEPS # _LT_LANG_F77_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for a Fortran 77 compiler are # suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to 'libtool'. m4_defun([_LT_LANG_F77_CONFIG], [AC_LANG_PUSH(Fortran 77) if test -z "$F77" || test no = "$F77"; then _lt_disable_F77=yes fi _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds _LT_TAGVAR(no_undefined_flag, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no # Source file extension for f77 test sources. ac_ext=f # Object file extension for compiled f77 test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # No sense in running all these tests if we already determined that # the F77 compiler isn't working. Some variables (like enable_shared) # are currently assumed to apply to all compilers on this platform, # and will be corrupted by setting them based on a non-working compiler. if test yes != "$_lt_disable_F77"; then # Code to be used in simple compile tests lt_simple_compile_test_code="\ subroutine t return end " # Code to be used in simple link tests lt_simple_link_test_code="\ program t end " # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_GCC=$GCC lt_save_CFLAGS=$CFLAGS CC=${F77-"f77"} CFLAGS=$FFLAGS compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) GCC=$G77 if test -n "$compiler"; then AC_MSG_CHECKING([if libtool supports shared libraries]) AC_MSG_RESULT([$can_build_shared]) AC_MSG_CHECKING([whether to build shared libraries]) test no = "$can_build_shared" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test yes = "$enable_shared" && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[[4-9]]*) if test ia64 != "$host_cpu"; then case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in yes,aix,yes) ;; # shared object as lib.so file only yes,svr4,*) ;; # shared object as lib.so archive member only yes,*) enable_static=no ;; # shared object in lib.a archive as well esac fi ;; esac AC_MSG_RESULT([$enable_shared]) AC_MSG_CHECKING([whether to build static libraries]) # Make sure either enable_shared or enable_static is yes. test yes = "$enable_shared" || enable_static=yes AC_MSG_RESULT([$enable_static]) _LT_TAGVAR(GCC, $1)=$G77 _LT_TAGVAR(LD, $1)=$LD ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi # test -n "$compiler" GCC=$lt_save_GCC CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS fi # test yes != "$_lt_disable_F77" AC_LANG_POP ])# _LT_LANG_F77_CONFIG # _LT_LANG_FC_CONFIG([TAG]) # ------------------------- # Ensure that the configuration variables for a Fortran compiler are # suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to 'libtool'. m4_defun([_LT_LANG_FC_CONFIG], [AC_LANG_PUSH(Fortran) if test -z "$FC" || test no = "$FC"; then _lt_disable_FC=yes fi _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds _LT_TAGVAR(no_undefined_flag, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no # Source file extension for fc test sources. ac_ext=${ac_fc_srcext-f} # Object file extension for compiled fc test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # No sense in running all these tests if we already determined that # the FC compiler isn't working. Some variables (like enable_shared) # are currently assumed to apply to all compilers on this platform, # and will be corrupted by setting them based on a non-working compiler. if test yes != "$_lt_disable_FC"; then # Code to be used in simple compile tests lt_simple_compile_test_code="\ subroutine t return end " # Code to be used in simple link tests lt_simple_link_test_code="\ program t end " # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_GCC=$GCC lt_save_CFLAGS=$CFLAGS CC=${FC-"f95"} CFLAGS=$FCFLAGS compiler=$CC GCC=$ac_cv_fc_compiler_gnu _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) if test -n "$compiler"; then AC_MSG_CHECKING([if libtool supports shared libraries]) AC_MSG_RESULT([$can_build_shared]) AC_MSG_CHECKING([whether to build shared libraries]) test no = "$can_build_shared" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test yes = "$enable_shared" && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[[4-9]]*) if test ia64 != "$host_cpu"; then case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in yes,aix,yes) ;; # shared object as lib.so file only yes,svr4,*) ;; # shared object as lib.so archive member only yes,*) enable_static=no ;; # shared object in lib.a archive as well esac fi ;; esac AC_MSG_RESULT([$enable_shared]) AC_MSG_CHECKING([whether to build static libraries]) # Make sure either enable_shared or enable_static is yes. test yes = "$enable_shared" || enable_static=yes AC_MSG_RESULT([$enable_static]) _LT_TAGVAR(GCC, $1)=$ac_cv_fc_compiler_gnu _LT_TAGVAR(LD, $1)=$LD ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... _LT_SYS_HIDDEN_LIBDEPS($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi # test -n "$compiler" GCC=$lt_save_GCC CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS fi # test yes != "$_lt_disable_FC" AC_LANG_POP ])# _LT_LANG_FC_CONFIG # _LT_LANG_GCJ_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for the GNU Java Compiler compiler # are suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to 'libtool'. m4_defun([_LT_LANG_GCJ_CONFIG], [AC_REQUIRE([LT_PROG_GCJ])dnl AC_LANG_SAVE # Source file extension for Java test sources. ac_ext=java # Object file extension for compiled Java test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code="class foo {}" # Code to be used in simple link tests lt_simple_link_test_code='public class conftest { public static void main(String[[]] argv) {}; }' # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_CFLAGS=$CFLAGS lt_save_GCC=$GCC GCC=yes CC=${GCJ-"gcj"} CFLAGS=$GCJFLAGS compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_TAGVAR(LD, $1)=$LD _LT_CC_BASENAME([$compiler]) # GCJ did not exist at the time GCC didn't implicitly link libc in. _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... if test -n "$compiler"; then _LT_COMPILER_NO_RTTI($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi AC_LANG_RESTORE GCC=$lt_save_GCC CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS ])# _LT_LANG_GCJ_CONFIG # _LT_LANG_GO_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for the GNU Go compiler # are suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to 'libtool'. m4_defun([_LT_LANG_GO_CONFIG], [AC_REQUIRE([LT_PROG_GO])dnl AC_LANG_SAVE # Source file extension for Go test sources. ac_ext=go # Object file extension for compiled Go test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code="package main; func main() { }" # Code to be used in simple link tests lt_simple_link_test_code='package main; func main() { }' # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_CFLAGS=$CFLAGS lt_save_GCC=$GCC GCC=yes CC=${GOC-"gccgo"} CFLAGS=$GOFLAGS compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_TAGVAR(LD, $1)=$LD _LT_CC_BASENAME([$compiler]) # Go did not exist at the time GCC didn't implicitly link libc in. _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... if test -n "$compiler"; then _LT_COMPILER_NO_RTTI($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi AC_LANG_RESTORE GCC=$lt_save_GCC CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS ])# _LT_LANG_GO_CONFIG # _LT_LANG_RC_CONFIG([TAG]) # ------------------------- # Ensure that the configuration variables for the Windows resource compiler # are suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to 'libtool'. m4_defun([_LT_LANG_RC_CONFIG], [AC_REQUIRE([LT_PROG_RC])dnl AC_LANG_SAVE # Source file extension for RC test sources. ac_ext=rc # Object file extension for compiled RC test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code='sample MENU { MENUITEM "&Soup", 100, CHECKED }' # Code to be used in simple link tests lt_simple_link_test_code=$lt_simple_compile_test_code # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_CFLAGS=$CFLAGS lt_save_GCC=$GCC GCC= CC=${RC-"windres"} CFLAGS= compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes if test -n "$compiler"; then : _LT_CONFIG($1) fi GCC=$lt_save_GCC AC_LANG_RESTORE CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS ])# _LT_LANG_RC_CONFIG # LT_PROG_GCJ # ----------- AC_DEFUN([LT_PROG_GCJ], [m4_ifdef([AC_PROG_GCJ], [AC_PROG_GCJ], [m4_ifdef([A][M_PROG_GCJ], [A][M_PROG_GCJ], [AC_CHECK_TOOL(GCJ, gcj,) test set = "${GCJFLAGS+set}" || GCJFLAGS="-g -O2" AC_SUBST(GCJFLAGS)])])[]dnl ]) # Old name: AU_ALIAS([LT_AC_PROG_GCJ], [LT_PROG_GCJ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([LT_AC_PROG_GCJ], []) # LT_PROG_GO # ---------- AC_DEFUN([LT_PROG_GO], [AC_CHECK_TOOL(GOC, gccgo,) ]) # LT_PROG_RC # ---------- AC_DEFUN([LT_PROG_RC], [AC_CHECK_TOOL(RC, windres,) ]) # Old name: AU_ALIAS([LT_AC_PROG_RC], [LT_PROG_RC]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([LT_AC_PROG_RC], []) # _LT_DECL_EGREP # -------------- # If we don't have a new enough Autoconf to choose the best grep # available, choose the one first in the user's PATH. m4_defun([_LT_DECL_EGREP], [AC_REQUIRE([AC_PROG_EGREP])dnl AC_REQUIRE([AC_PROG_FGREP])dnl test -z "$GREP" && GREP=grep _LT_DECL([], [GREP], [1], [A grep program that handles long lines]) _LT_DECL([], [EGREP], [1], [An ERE matcher]) _LT_DECL([], [FGREP], [1], [A literal string matcher]) dnl Non-bleeding-edge autoconf doesn't subst GREP, so do it here too AC_SUBST([GREP]) ]) # _LT_DECL_OBJDUMP # -------------- # If we don't have a new enough Autoconf to choose the best objdump # available, choose the one first in the user's PATH. m4_defun([_LT_DECL_OBJDUMP], [AC_CHECK_TOOL(OBJDUMP, objdump, false) test -z "$OBJDUMP" && OBJDUMP=objdump _LT_DECL([], [OBJDUMP], [1], [An object symbol dumper]) AC_SUBST([OBJDUMP]) ]) # _LT_DECL_DLLTOOL # ---------------- # Ensure DLLTOOL variable is set. m4_defun([_LT_DECL_DLLTOOL], [AC_CHECK_TOOL(DLLTOOL, dlltool, false) test -z "$DLLTOOL" && DLLTOOL=dlltool _LT_DECL([], [DLLTOOL], [1], [DLL creation program]) AC_SUBST([DLLTOOL]) ]) # _LT_DECL_SED # ------------ # Check for a fully-functional sed program, that truncates # as few characters as possible. Prefer GNU sed if found. m4_defun([_LT_DECL_SED], [AC_PROG_SED test -z "$SED" && SED=sed Xsed="$SED -e 1s/^X//" _LT_DECL([], [SED], [1], [A sed program that does not truncate output]) _LT_DECL([], [Xsed], ["\$SED -e 1s/^X//"], [Sed that helps us avoid accidentally triggering echo(1) options like -n]) ])# _LT_DECL_SED m4_ifndef([AC_PROG_SED], [ ############################################################ # NOTE: This macro has been submitted for inclusion into # # GNU Autoconf as AC_PROG_SED. When it is available in # # a released version of Autoconf we should remove this # # macro and use it instead. # ############################################################ m4_defun([AC_PROG_SED], [AC_MSG_CHECKING([for a sed that does not truncate output]) AC_CACHE_VAL(lt_cv_path_SED, [# Loop through the user's path and test for sed and gsed. # Then use that list of sed's as ones to test for truncation. as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for lt_ac_prog in sed gsed; do for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$lt_ac_prog$ac_exec_ext"; then lt_ac_sed_list="$lt_ac_sed_list $as_dir/$lt_ac_prog$ac_exec_ext" fi done done done IFS=$as_save_IFS lt_ac_max=0 lt_ac_count=0 # Add /usr/xpg4/bin/sed as it is typically found on Solaris # along with /bin/sed that truncates output. for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do test ! -f "$lt_ac_sed" && continue cat /dev/null > conftest.in lt_ac_count=0 echo $ECHO_N "0123456789$ECHO_C" >conftest.in # Check for GNU sed and select it if it is found. if "$lt_ac_sed" --version 2>&1 < /dev/null | grep 'GNU' > /dev/null; then lt_cv_path_SED=$lt_ac_sed break fi while true; do cat conftest.in conftest.in >conftest.tmp mv conftest.tmp conftest.in cp conftest.in conftest.nl echo >>conftest.nl $lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break cmp -s conftest.out conftest.nl || break # 10000 chars as input seems more than enough test 10 -lt "$lt_ac_count" && break lt_ac_count=`expr $lt_ac_count + 1` if test "$lt_ac_count" -gt "$lt_ac_max"; then lt_ac_max=$lt_ac_count lt_cv_path_SED=$lt_ac_sed fi done done ]) SED=$lt_cv_path_SED AC_SUBST([SED]) AC_MSG_RESULT([$SED]) ])#AC_PROG_SED ])#m4_ifndef # Old name: AU_ALIAS([LT_AC_PROG_SED], [AC_PROG_SED]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([LT_AC_PROG_SED], []) # _LT_CHECK_SHELL_FEATURES # ------------------------ # Find out whether the shell is Bourne or XSI compatible, # or has some other useful features. m4_defun([_LT_CHECK_SHELL_FEATURES], [if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then lt_unset=unset else lt_unset=false fi _LT_DECL([], [lt_unset], [0], [whether the shell understands "unset"])dnl # test EBCDIC or ASCII case `echo X|tr X '\101'` in A) # ASCII based system # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr lt_SP2NL='tr \040 \012' lt_NL2SP='tr \015\012 \040\040' ;; *) # EBCDIC based system lt_SP2NL='tr \100 \n' lt_NL2SP='tr \r\n \100\100' ;; esac _LT_DECL([SP2NL], [lt_SP2NL], [1], [turn spaces into newlines])dnl _LT_DECL([NL2SP], [lt_NL2SP], [1], [turn newlines into spaces])dnl ])# _LT_CHECK_SHELL_FEATURES # _LT_PATH_CONVERSION_FUNCTIONS # ----------------------------- # Determine what file name conversion functions should be used by # func_to_host_file (and, implicitly, by func_to_host_path). These are needed # for certain cross-compile configurations and native mingw. m4_defun([_LT_PATH_CONVERSION_FUNCTIONS], [AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_CANONICAL_BUILD])dnl AC_MSG_CHECKING([how to convert $build file names to $host format]) AC_CACHE_VAL(lt_cv_to_host_file_cmd, [case $host in *-*-mingw* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32 ;; *-*-cygwin* ) lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32 ;; * ) # otherwise, assume *nix lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32 ;; esac ;; *-*-cygwin* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin ;; *-*-cygwin* ) lt_cv_to_host_file_cmd=func_convert_file_noop ;; * ) # otherwise, assume *nix lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin ;; esac ;; * ) # unhandled hosts (and "normal" native builds) lt_cv_to_host_file_cmd=func_convert_file_noop ;; esac ]) to_host_file_cmd=$lt_cv_to_host_file_cmd AC_MSG_RESULT([$lt_cv_to_host_file_cmd]) _LT_DECL([to_host_file_cmd], [lt_cv_to_host_file_cmd], [0], [convert $build file names to $host format])dnl AC_MSG_CHECKING([how to convert $build file names to toolchain format]) AC_CACHE_VAL(lt_cv_to_tool_file_cmd, [#assume ordinary cross tools, or native build. lt_cv_to_tool_file_cmd=func_convert_file_noop case $host in *-*-mingw* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32 ;; esac ;; esac ]) to_tool_file_cmd=$lt_cv_to_tool_file_cmd AC_MSG_RESULT([$lt_cv_to_tool_file_cmd]) _LT_DECL([to_tool_file_cmd], [lt_cv_to_tool_file_cmd], [0], [convert $build files to toolchain format])dnl ])# _LT_PATH_CONVERSION_FUNCTIONS libopenmpt-0.6.1+release.autotools/m4/ltoptions.m40000644000175000017500000003426214175541524017110 00000000000000# Helper functions for option handling. -*- Autoconf -*- # # Copyright (C) 2004-2005, 2007-2009, 2011-2015 Free Software # Foundation, Inc. # Written by Gary V. Vaughan, 2004 # # This file 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. # serial 8 ltoptions.m4 # This is to help aclocal find these macros, as it can't see m4_define. AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])]) # _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME) # ------------------------------------------ m4_define([_LT_MANGLE_OPTION], [[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])]) # _LT_SET_OPTION(MACRO-NAME, OPTION-NAME) # --------------------------------------- # Set option OPTION-NAME for macro MACRO-NAME, and if there is a # matching handler defined, dispatch to it. Other OPTION-NAMEs are # saved as a flag. m4_define([_LT_SET_OPTION], [m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]), _LT_MANGLE_DEFUN([$1], [$2]), [m4_warning([Unknown $1 option '$2'])])[]dnl ]) # _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET]) # ------------------------------------------------------------ # Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. m4_define([_LT_IF_OPTION], [m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])]) # _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET) # ------------------------------------------------------- # Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME # are set. m4_define([_LT_UNLESS_OPTIONS], [m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), [m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option), [m4_define([$0_found])])])[]dnl m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3 ])[]dnl ]) # _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST) # ---------------------------------------- # OPTION-LIST is a space-separated list of Libtool options associated # with MACRO-NAME. If any OPTION has a matching handler declared with # LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about # the unknown option and exit. m4_defun([_LT_SET_OPTIONS], [# Set options m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), [_LT_SET_OPTION([$1], _LT_Option)]) m4_if([$1],[LT_INIT],[ dnl dnl Simply set some default values (i.e off) if boolean options were not dnl specified: _LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no ]) _LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no ]) dnl dnl If no reference was made to various pairs of opposing options, then dnl we run the default mode handler for the pair. For example, if neither dnl 'shared' nor 'disable-shared' was passed, we enable building of shared dnl archives by default: _LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED]) _LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC]) _LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC]) _LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install], [_LT_ENABLE_FAST_INSTALL]) _LT_UNLESS_OPTIONS([LT_INIT], [aix-soname=aix aix-soname=both aix-soname=svr4], [_LT_WITH_AIX_SONAME([aix])]) ]) ])# _LT_SET_OPTIONS ## --------------------------------- ## ## Macros to handle LT_INIT options. ## ## --------------------------------- ## # _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME) # ----------------------------------------- m4_define([_LT_MANGLE_DEFUN], [[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])]) # LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE) # ----------------------------------------------- m4_define([LT_OPTION_DEFINE], [m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl ])# LT_OPTION_DEFINE # dlopen # ------ LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes ]) AU_DEFUN([AC_LIBTOOL_DLOPEN], [_LT_SET_OPTION([LT_INIT], [dlopen]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the 'dlopen' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], []) # win32-dll # --------- # Declare package support for building win32 dll's. LT_OPTION_DEFINE([LT_INIT], [win32-dll], [enable_win32_dll=yes case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-cegcc*) AC_CHECK_TOOL(AS, as, false) AC_CHECK_TOOL(DLLTOOL, dlltool, false) AC_CHECK_TOOL(OBJDUMP, objdump, false) ;; esac test -z "$AS" && AS=as _LT_DECL([], [AS], [1], [Assembler program])dnl test -z "$DLLTOOL" && DLLTOOL=dlltool _LT_DECL([], [DLLTOOL], [1], [DLL creation program])dnl test -z "$OBJDUMP" && OBJDUMP=objdump _LT_DECL([], [OBJDUMP], [1], [Object dumper program])dnl ])# win32-dll AU_DEFUN([AC_LIBTOOL_WIN32_DLL], [AC_REQUIRE([AC_CANONICAL_HOST])dnl _LT_SET_OPTION([LT_INIT], [win32-dll]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the 'win32-dll' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], []) # _LT_ENABLE_SHARED([DEFAULT]) # ---------------------------- # implement the --enable-shared flag, and supports the 'shared' and # 'disable-shared' LT_INIT options. # DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'. m4_define([_LT_ENABLE_SHARED], [m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl AC_ARG_ENABLE([shared], [AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@], [build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])], [p=${PACKAGE-default} case $enableval in yes) enable_shared=yes ;; no) enable_shared=no ;; *) enable_shared=no # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for pkg in $enableval; do IFS=$lt_save_ifs if test "X$pkg" = "X$p"; then enable_shared=yes fi done IFS=$lt_save_ifs ;; esac], [enable_shared=]_LT_ENABLE_SHARED_DEFAULT) _LT_DECL([build_libtool_libs], [enable_shared], [0], [Whether or not to build shared libraries]) ])# _LT_ENABLE_SHARED LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])]) LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])]) # Old names: AC_DEFUN([AC_ENABLE_SHARED], [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared]) ]) AC_DEFUN([AC_DISABLE_SHARED], [_LT_SET_OPTION([LT_INIT], [disable-shared]) ]) AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)]) AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AM_ENABLE_SHARED], []) dnl AC_DEFUN([AM_DISABLE_SHARED], []) # _LT_ENABLE_STATIC([DEFAULT]) # ---------------------------- # implement the --enable-static flag, and support the 'static' and # 'disable-static' LT_INIT options. # DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'. m4_define([_LT_ENABLE_STATIC], [m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl AC_ARG_ENABLE([static], [AS_HELP_STRING([--enable-static@<:@=PKGS@:>@], [build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])], [p=${PACKAGE-default} case $enableval in yes) enable_static=yes ;; no) enable_static=no ;; *) enable_static=no # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for pkg in $enableval; do IFS=$lt_save_ifs if test "X$pkg" = "X$p"; then enable_static=yes fi done IFS=$lt_save_ifs ;; esac], [enable_static=]_LT_ENABLE_STATIC_DEFAULT) _LT_DECL([build_old_libs], [enable_static], [0], [Whether or not to build static libraries]) ])# _LT_ENABLE_STATIC LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])]) LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])]) # Old names: AC_DEFUN([AC_ENABLE_STATIC], [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static]) ]) AC_DEFUN([AC_DISABLE_STATIC], [_LT_SET_OPTION([LT_INIT], [disable-static]) ]) AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)]) AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AM_ENABLE_STATIC], []) dnl AC_DEFUN([AM_DISABLE_STATIC], []) # _LT_ENABLE_FAST_INSTALL([DEFAULT]) # ---------------------------------- # implement the --enable-fast-install flag, and support the 'fast-install' # and 'disable-fast-install' LT_INIT options. # DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'. m4_define([_LT_ENABLE_FAST_INSTALL], [m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl AC_ARG_ENABLE([fast-install], [AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@], [optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])], [p=${PACKAGE-default} case $enableval in yes) enable_fast_install=yes ;; no) enable_fast_install=no ;; *) enable_fast_install=no # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for pkg in $enableval; do IFS=$lt_save_ifs if test "X$pkg" = "X$p"; then enable_fast_install=yes fi done IFS=$lt_save_ifs ;; esac], [enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT) _LT_DECL([fast_install], [enable_fast_install], [0], [Whether or not to optimize for fast installation])dnl ])# _LT_ENABLE_FAST_INSTALL LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])]) LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])]) # Old names: AU_DEFUN([AC_ENABLE_FAST_INSTALL], [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the 'fast-install' option into LT_INIT's first parameter.]) ]) AU_DEFUN([AC_DISABLE_FAST_INSTALL], [_LT_SET_OPTION([LT_INIT], [disable-fast-install]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the 'disable-fast-install' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], []) dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], []) # _LT_WITH_AIX_SONAME([DEFAULT]) # ---------------------------------- # implement the --with-aix-soname flag, and support the `aix-soname=aix' # and `aix-soname=both' and `aix-soname=svr4' LT_INIT options. DEFAULT # is either `aix', `both' or `svr4'. If omitted, it defaults to `aix'. m4_define([_LT_WITH_AIX_SONAME], [m4_define([_LT_WITH_AIX_SONAME_DEFAULT], [m4_if($1, svr4, svr4, m4_if($1, both, both, aix))])dnl shared_archive_member_spec= case $host,$enable_shared in power*-*-aix[[5-9]]*,yes) AC_MSG_CHECKING([which variant of shared library versioning to provide]) AC_ARG_WITH([aix-soname], [AS_HELP_STRING([--with-aix-soname=aix|svr4|both], [shared library versioning (aka "SONAME") variant to provide on AIX, @<:@default=]_LT_WITH_AIX_SONAME_DEFAULT[@:>@.])], [case $withval in aix|svr4|both) ;; *) AC_MSG_ERROR([Unknown argument to --with-aix-soname]) ;; esac lt_cv_with_aix_soname=$with_aix_soname], [AC_CACHE_VAL([lt_cv_with_aix_soname], [lt_cv_with_aix_soname=]_LT_WITH_AIX_SONAME_DEFAULT) with_aix_soname=$lt_cv_with_aix_soname]) AC_MSG_RESULT([$with_aix_soname]) if test aix != "$with_aix_soname"; then # For the AIX way of multilib, we name the shared archive member # based on the bitwidth used, traditionally 'shr.o' or 'shr_64.o', # and 'shr.imp' or 'shr_64.imp', respectively, for the Import File. # Even when GNU compilers ignore OBJECT_MODE but need '-maix64' flag, # the AIX toolchain works better with OBJECT_MODE set (default 32). if test 64 = "${OBJECT_MODE-32}"; then shared_archive_member_spec=shr_64 else shared_archive_member_spec=shr fi fi ;; *) with_aix_soname=aix ;; esac _LT_DECL([], [shared_archive_member_spec], [0], [Shared archive member basename, for filename based shared library versioning on AIX])dnl ])# _LT_WITH_AIX_SONAME LT_OPTION_DEFINE([LT_INIT], [aix-soname=aix], [_LT_WITH_AIX_SONAME([aix])]) LT_OPTION_DEFINE([LT_INIT], [aix-soname=both], [_LT_WITH_AIX_SONAME([both])]) LT_OPTION_DEFINE([LT_INIT], [aix-soname=svr4], [_LT_WITH_AIX_SONAME([svr4])]) # _LT_WITH_PIC([MODE]) # -------------------- # implement the --with-pic flag, and support the 'pic-only' and 'no-pic' # LT_INIT options. # MODE is either 'yes' or 'no'. If omitted, it defaults to 'both'. m4_define([_LT_WITH_PIC], [AC_ARG_WITH([pic], [AS_HELP_STRING([--with-pic@<:@=PKGS@:>@], [try to use only PIC/non-PIC objects @<:@default=use both@:>@])], [lt_p=${PACKAGE-default} case $withval in yes|no) pic_mode=$withval ;; *) pic_mode=default # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for lt_pkg in $withval; do IFS=$lt_save_ifs if test "X$lt_pkg" = "X$lt_p"; then pic_mode=yes fi done IFS=$lt_save_ifs ;; esac], [pic_mode=m4_default([$1], [default])]) _LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl ])# _LT_WITH_PIC LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])]) LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])]) # Old name: AU_DEFUN([AC_LIBTOOL_PICMODE], [_LT_SET_OPTION([LT_INIT], [pic-only]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the 'pic-only' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_PICMODE], []) ## ----------------- ## ## LTDL_INIT Options ## ## ----------------- ## m4_define([_LTDL_MODE], []) LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive], [m4_define([_LTDL_MODE], [nonrecursive])]) LT_OPTION_DEFINE([LTDL_INIT], [recursive], [m4_define([_LTDL_MODE], [recursive])]) LT_OPTION_DEFINE([LTDL_INIT], [subproject], [m4_define([_LTDL_MODE], [subproject])]) m4_define([_LTDL_TYPE], []) LT_OPTION_DEFINE([LTDL_INIT], [installable], [m4_define([_LTDL_TYPE], [installable])]) LT_OPTION_DEFINE([LTDL_INIT], [convenience], [m4_define([_LTDL_TYPE], [convenience])]) libopenmpt-0.6.1+release.autotools/m4/ltsugar.m40000644000175000017500000001044014175541524016526 00000000000000# ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*- # # Copyright (C) 2004-2005, 2007-2008, 2011-2015 Free Software # Foundation, Inc. # Written by Gary V. Vaughan, 2004 # # This file 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. # serial 6 ltsugar.m4 # This is to help aclocal find these macros, as it can't see m4_define. AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])]) # lt_join(SEP, ARG1, [ARG2...]) # ----------------------------- # Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their # associated separator. # Needed until we can rely on m4_join from Autoconf 2.62, since all earlier # versions in m4sugar had bugs. m4_define([lt_join], [m4_if([$#], [1], [], [$#], [2], [[$2]], [m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])]) m4_define([_lt_join], [m4_if([$#$2], [2], [], [m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])]) # lt_car(LIST) # lt_cdr(LIST) # ------------ # Manipulate m4 lists. # These macros are necessary as long as will still need to support # Autoconf-2.59, which quotes differently. m4_define([lt_car], [[$1]]) m4_define([lt_cdr], [m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])], [$#], 1, [], [m4_dquote(m4_shift($@))])]) m4_define([lt_unquote], $1) # lt_append(MACRO-NAME, STRING, [SEPARATOR]) # ------------------------------------------ # Redefine MACRO-NAME to hold its former content plus 'SEPARATOR''STRING'. # Note that neither SEPARATOR nor STRING are expanded; they are appended # to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked). # No SEPARATOR is output if MACRO-NAME was previously undefined (different # than defined and empty). # # This macro is needed until we can rely on Autoconf 2.62, since earlier # versions of m4sugar mistakenly expanded SEPARATOR but not STRING. m4_define([lt_append], [m4_define([$1], m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])]) # lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...]) # ---------------------------------------------------------- # Produce a SEP delimited list of all paired combinations of elements of # PREFIX-LIST with SUFFIX1 through SUFFIXn. Each element of the list # has the form PREFIXmINFIXSUFFIXn. # Needed until we can rely on m4_combine added in Autoconf 2.62. m4_define([lt_combine], [m4_if(m4_eval([$# > 3]), [1], [m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl [[m4_foreach([_Lt_prefix], [$2], [m4_foreach([_Lt_suffix], ]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[, [_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])]) # lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ]) # ----------------------------------------------------------------------- # Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited # by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ. m4_define([lt_if_append_uniq], [m4_ifdef([$1], [m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1], [lt_append([$1], [$2], [$3])$4], [$5])], [lt_append([$1], [$2], [$3])$4])]) # lt_dict_add(DICT, KEY, VALUE) # ----------------------------- m4_define([lt_dict_add], [m4_define([$1($2)], [$3])]) # lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE) # -------------------------------------------- m4_define([lt_dict_add_subkey], [m4_define([$1($2:$3)], [$4])]) # lt_dict_fetch(DICT, KEY, [SUBKEY]) # ---------------------------------- m4_define([lt_dict_fetch], [m4_ifval([$3], m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]), m4_ifdef([$1($2)], [m4_defn([$1($2)])]))]) # lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE]) # ----------------------------------------------------------------- m4_define([lt_if_dict_fetch], [m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4], [$5], [$6])]) # lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...]) # -------------------------------------------------------------- m4_define([lt_dict_filter], [m4_if([$5], [], [], [lt_join(m4_quote(m4_default([$4], [[, ]])), lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]), [lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl ]) libopenmpt-0.6.1+release.autotools/m4/ltversion.m40000644000175000017500000000127314175541524017076 00000000000000# ltversion.m4 -- version numbers -*- Autoconf -*- # # Copyright (C) 2004, 2011-2015 Free Software Foundation, Inc. # Written by Scott James Remnant, 2004 # # This file 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. # @configure_input@ # serial 4179 ltversion.m4 # This file is part of GNU Libtool m4_define([LT_PACKAGE_VERSION], [2.4.6]) m4_define([LT_PACKAGE_REVISION], [2.4.6]) AC_DEFUN([LTVERSION_VERSION], [macro_version='2.4.6' macro_revision='2.4.6' _LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?]) _LT_DECL(, macro_revision, 0) ]) libopenmpt-0.6.1+release.autotools/m4/lt~obsolete.m40000644000175000017500000001377414175541524017434 00000000000000# lt~obsolete.m4 -- aclocal satisfying obsolete definitions. -*-Autoconf-*- # # Copyright (C) 2004-2005, 2007, 2009, 2011-2015 Free Software # Foundation, Inc. # Written by Scott James Remnant, 2004. # # This file 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. # serial 5 lt~obsolete.m4 # These exist entirely to fool aclocal when bootstrapping libtool. # # In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN), # which have later been changed to m4_define as they aren't part of the # exported API, or moved to Autoconf or Automake where they belong. # # The trouble is, aclocal is a bit thick. It'll see the old AC_DEFUN # in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us # using a macro with the same name in our local m4/libtool.m4 it'll # pull the old libtool.m4 in (it doesn't see our shiny new m4_define # and doesn't know about Autoconf macros at all.) # # So we provide this file, which has a silly filename so it's always # included after everything else. This provides aclocal with the # AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything # because those macros already exist, or will be overwritten later. # We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6. # # Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here. # Yes, that means every name once taken will need to remain here until # we give up compatibility with versions before 1.7, at which point # we need to keep only those names which we still refer to. # This is to help aclocal find these macros, as it can't see m4_define. AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])]) m4_ifndef([AC_LIBTOOL_LINKER_OPTION], [AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])]) m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP])]) m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])]) m4_ifndef([_LT_AC_SHELL_INIT], [AC_DEFUN([_LT_AC_SHELL_INIT])]) m4_ifndef([_LT_AC_SYS_LIBPATH_AIX], [AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])]) m4_ifndef([_LT_PROG_LTMAIN], [AC_DEFUN([_LT_PROG_LTMAIN])]) m4_ifndef([_LT_AC_TAGVAR], [AC_DEFUN([_LT_AC_TAGVAR])]) m4_ifndef([AC_LTDL_ENABLE_INSTALL], [AC_DEFUN([AC_LTDL_ENABLE_INSTALL])]) m4_ifndef([AC_LTDL_PREOPEN], [AC_DEFUN([AC_LTDL_PREOPEN])]) m4_ifndef([_LT_AC_SYS_COMPILER], [AC_DEFUN([_LT_AC_SYS_COMPILER])]) m4_ifndef([_LT_AC_LOCK], [AC_DEFUN([_LT_AC_LOCK])]) m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE], [AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])]) m4_ifndef([_LT_AC_TRY_DLOPEN_SELF], [AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])]) m4_ifndef([AC_LIBTOOL_PROG_CC_C_O], [AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])]) m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])]) m4_ifndef([AC_LIBTOOL_OBJDIR], [AC_DEFUN([AC_LIBTOOL_OBJDIR])]) m4_ifndef([AC_LTDL_OBJDIR], [AC_DEFUN([AC_LTDL_OBJDIR])]) m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])]) m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP], [AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])]) m4_ifndef([AC_PATH_MAGIC], [AC_DEFUN([AC_PATH_MAGIC])]) m4_ifndef([AC_PROG_LD_GNU], [AC_DEFUN([AC_PROG_LD_GNU])]) m4_ifndef([AC_PROG_LD_RELOAD_FLAG], [AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])]) m4_ifndef([AC_DEPLIBS_CHECK_METHOD], [AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])]) m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])]) m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])]) m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])]) m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS], [AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])]) m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP], [AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])]) m4_ifndef([LT_AC_PROG_EGREP], [AC_DEFUN([LT_AC_PROG_EGREP])]) m4_ifndef([LT_AC_PROG_SED], [AC_DEFUN([LT_AC_PROG_SED])]) m4_ifndef([_LT_CC_BASENAME], [AC_DEFUN([_LT_CC_BASENAME])]) m4_ifndef([_LT_COMPILER_BOILERPLATE], [AC_DEFUN([_LT_COMPILER_BOILERPLATE])]) m4_ifndef([_LT_LINKER_BOILERPLATE], [AC_DEFUN([_LT_LINKER_BOILERPLATE])]) m4_ifndef([_AC_PROG_LIBTOOL], [AC_DEFUN([_AC_PROG_LIBTOOL])]) m4_ifndef([AC_LIBTOOL_SETUP], [AC_DEFUN([AC_LIBTOOL_SETUP])]) m4_ifndef([_LT_AC_CHECK_DLFCN], [AC_DEFUN([_LT_AC_CHECK_DLFCN])]) m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER], [AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])]) m4_ifndef([_LT_AC_TAGCONFIG], [AC_DEFUN([_LT_AC_TAGCONFIG])]) m4_ifndef([AC_DISABLE_FAST_INSTALL], [AC_DEFUN([AC_DISABLE_FAST_INSTALL])]) m4_ifndef([_LT_AC_LANG_CXX], [AC_DEFUN([_LT_AC_LANG_CXX])]) m4_ifndef([_LT_AC_LANG_F77], [AC_DEFUN([_LT_AC_LANG_F77])]) m4_ifndef([_LT_AC_LANG_GCJ], [AC_DEFUN([_LT_AC_LANG_GCJ])]) m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])]) m4_ifndef([_LT_AC_LANG_C_CONFIG], [AC_DEFUN([_LT_AC_LANG_C_CONFIG])]) m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])]) m4_ifndef([_LT_AC_LANG_CXX_CONFIG], [AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])]) m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])]) m4_ifndef([_LT_AC_LANG_F77_CONFIG], [AC_DEFUN([_LT_AC_LANG_F77_CONFIG])]) m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])]) m4_ifndef([_LT_AC_LANG_GCJ_CONFIG], [AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])]) m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])]) m4_ifndef([_LT_AC_LANG_RC_CONFIG], [AC_DEFUN([_LT_AC_LANG_RC_CONFIG])]) m4_ifndef([AC_LIBTOOL_CONFIG], [AC_DEFUN([AC_LIBTOOL_CONFIG])]) m4_ifndef([_LT_AC_FILE_LTDLL_C], [AC_DEFUN([_LT_AC_FILE_LTDLL_C])]) m4_ifndef([_LT_REQUIRED_DARWIN_CHECKS], [AC_DEFUN([_LT_REQUIRED_DARWIN_CHECKS])]) m4_ifndef([_LT_AC_PROG_CXXCPP], [AC_DEFUN([_LT_AC_PROG_CXXCPP])]) m4_ifndef([_LT_PREPARE_SED_QUOTE_VARS], [AC_DEFUN([_LT_PREPARE_SED_QUOTE_VARS])]) m4_ifndef([_LT_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_PROG_ECHO_BACKSLASH])]) m4_ifndef([_LT_PROG_F77], [AC_DEFUN([_LT_PROG_F77])]) m4_ifndef([_LT_PROG_FC], [AC_DEFUN([_LT_PROG_FC])]) m4_ifndef([_LT_PROG_CXX], [AC_DEFUN([_LT_PROG_CXX])]) libopenmpt-0.6.1+release.autotools/m4/pkg.m40000644000175000017500000002400714175541524015632 00000000000000# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- # serial 12 (pkg-config-0.29.2) dnl Copyright © 2004 Scott James Remnant . dnl Copyright © 2012-2015 Dan Nicholson dnl dnl This program is free software; you can redistribute it and/or modify dnl it under the terms of the GNU General Public License as published by dnl the Free Software Foundation; either version 2 of the License, or dnl (at your option) any later version. dnl dnl This program is distributed in the hope that it will be useful, but dnl WITHOUT ANY WARRANTY; without even the implied warranty of dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU dnl General Public License for more details. dnl dnl You should have received a copy of the GNU General Public License dnl along with this program; if not, write to the Free Software dnl Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA dnl 02111-1307, USA. dnl dnl As a special exception to the GNU General Public License, if you dnl distribute this file as part of a program that contains a dnl configuration script generated by Autoconf, you may include it under dnl the same distribution terms that you use for the rest of that dnl program. dnl PKG_PREREQ(MIN-VERSION) dnl ----------------------- dnl Since: 0.29 dnl dnl Verify that the version of the pkg-config macros are at least dnl MIN-VERSION. Unlike PKG_PROG_PKG_CONFIG, which checks the user's dnl installed version of pkg-config, this checks the developer's version dnl of pkg.m4 when generating configure. dnl dnl To ensure that this macro is defined, also add: dnl m4_ifndef([PKG_PREREQ], dnl [m4_fatal([must install pkg-config 0.29 or later before running autoconf/autogen])]) dnl dnl See the "Since" comment for each macro you use to see what version dnl of the macros you require. m4_defun([PKG_PREREQ], [m4_define([PKG_MACROS_VERSION], [0.29.2]) m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1, [m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])]) ])dnl PKG_PREREQ dnl PKG_PROG_PKG_CONFIG([MIN-VERSION]) dnl ---------------------------------- dnl Since: 0.16 dnl dnl Search for the pkg-config tool and set the PKG_CONFIG variable to dnl first found in the path. Checks that the version of pkg-config found dnl is at least MIN-VERSION. If MIN-VERSION is not specified, 0.9.0 is dnl used since that's the first version where most current features of dnl pkg-config existed. AC_DEFUN([PKG_PROG_PKG_CONFIG], [m4_pattern_forbid([^_?PKG_[A-Z_]+$]) m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$]) m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$]) AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility]) AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path]) AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path]) if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) fi if test -n "$PKG_CONFIG"; then _pkg_min_version=m4_default([$1], [0.9.0]) AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) PKG_CONFIG="" fi fi[]dnl ])dnl PKG_PROG_PKG_CONFIG dnl PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) dnl ------------------------------------------------------------------- dnl Since: 0.18 dnl dnl Check to see whether a particular set of modules exists. Similar to dnl PKG_CHECK_MODULES(), but does not set variables or print errors. dnl dnl Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) dnl only at the first occurence in configure.ac, so if the first place dnl it's called might be skipped (such as if it is within an "if", you dnl have to call PKG_CHECK_EXISTS manually AC_DEFUN([PKG_CHECK_EXISTS], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl if test -n "$PKG_CONFIG" && \ AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then m4_default([$2], [:]) m4_ifvaln([$3], [else $3])dnl fi]) dnl _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) dnl --------------------------------------------- dnl Internal wrapper calling pkg-config via PKG_CONFIG and setting dnl pkg_failed based on the result. m4_define([_PKG_CONFIG], [if test -n "$$1"; then pkg_cv_[]$1="$$1" elif test -n "$PKG_CONFIG"; then PKG_CHECK_EXISTS([$3], [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes ], [pkg_failed=yes]) else pkg_failed=untried fi[]dnl ])dnl _PKG_CONFIG dnl _PKG_SHORT_ERRORS_SUPPORTED dnl --------------------------- dnl Internal check to see if pkg-config supports short errors. AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], [AC_REQUIRE([PKG_PROG_PKG_CONFIG]) if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi[]dnl ])dnl _PKG_SHORT_ERRORS_SUPPORTED dnl PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], dnl [ACTION-IF-NOT-FOUND]) dnl -------------------------------------------------------------- dnl Since: 0.4.0 dnl dnl Note that if there is a possibility the first call to dnl PKG_CHECK_MODULES might not happen, you should be sure to include an dnl explicit call to PKG_PROG_PKG_CONFIG in your configure.ac AC_DEFUN([PKG_CHECK_MODULES], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl pkg_failed=no AC_MSG_CHECKING([for $2]) _PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) _PKG_CONFIG([$1][_LIBS], [libs], [$2]) m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS and $1[]_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details.]) if test $pkg_failed = yes; then AC_MSG_RESULT([no]) _PKG_SHORT_ERRORS_SUPPORTED if test $_pkg_short_errors_supported = yes; then $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1` else $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD m4_default([$4], [AC_MSG_ERROR( [Package requirements ($2) were not met: $$1_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. _PKG_TEXT])[]dnl ]) elif test $pkg_failed = untried; then AC_MSG_RESULT([no]) m4_default([$4], [AC_MSG_FAILURE( [The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. _PKG_TEXT To get pkg-config, see .])[]dnl ]) else $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS $1[]_LIBS=$pkg_cv_[]$1[]_LIBS AC_MSG_RESULT([yes]) $3 fi[]dnl ])dnl PKG_CHECK_MODULES dnl PKG_CHECK_MODULES_STATIC(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], dnl [ACTION-IF-NOT-FOUND]) dnl --------------------------------------------------------------------- dnl Since: 0.29 dnl dnl Checks for existence of MODULES and gathers its build flags with dnl static libraries enabled. Sets VARIABLE-PREFIX_CFLAGS from --cflags dnl and VARIABLE-PREFIX_LIBS from --libs. dnl dnl Note that if there is a possibility the first call to dnl PKG_CHECK_MODULES_STATIC might not happen, you should be sure to dnl include an explicit call to PKG_PROG_PKG_CONFIG in your dnl configure.ac. AC_DEFUN([PKG_CHECK_MODULES_STATIC], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl _save_PKG_CONFIG=$PKG_CONFIG PKG_CONFIG="$PKG_CONFIG --static" PKG_CHECK_MODULES($@) PKG_CONFIG=$_save_PKG_CONFIG[]dnl ])dnl PKG_CHECK_MODULES_STATIC dnl PKG_INSTALLDIR([DIRECTORY]) dnl ------------------------- dnl Since: 0.27 dnl dnl Substitutes the variable pkgconfigdir as the location where a module dnl should install pkg-config .pc files. By default the directory is dnl $libdir/pkgconfig, but the default can be changed by passing dnl DIRECTORY. The user can override through the --with-pkgconfigdir dnl parameter. AC_DEFUN([PKG_INSTALLDIR], [m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])]) m4_pushdef([pkg_description], [pkg-config installation directory @<:@]pkg_default[@:>@]) AC_ARG_WITH([pkgconfigdir], [AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],, [with_pkgconfigdir=]pkg_default) AC_SUBST([pkgconfigdir], [$with_pkgconfigdir]) m4_popdef([pkg_default]) m4_popdef([pkg_description]) ])dnl PKG_INSTALLDIR dnl PKG_NOARCH_INSTALLDIR([DIRECTORY]) dnl -------------------------------- dnl Since: 0.27 dnl dnl Substitutes the variable noarch_pkgconfigdir as the location where a dnl module should install arch-independent pkg-config .pc files. By dnl default the directory is $datadir/pkgconfig, but the default can be dnl changed by passing DIRECTORY. The user can override through the dnl --with-noarch-pkgconfigdir parameter. AC_DEFUN([PKG_NOARCH_INSTALLDIR], [m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])]) m4_pushdef([pkg_description], [pkg-config arch-independent installation directory @<:@]pkg_default[@:>@]) AC_ARG_WITH([noarch-pkgconfigdir], [AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],, [with_noarch_pkgconfigdir=]pkg_default) AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir]) m4_popdef([pkg_default]) m4_popdef([pkg_description]) ])dnl PKG_NOARCH_INSTALLDIR dnl PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE, dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) dnl ------------------------------------------- dnl Since: 0.28 dnl dnl Retrieves the value of the pkg-config variable for the given module. AC_DEFUN([PKG_CHECK_VAR], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl _PKG_CONFIG([$1], [variable="][$3]["], [$2]) AS_VAR_COPY([$1], [pkg_cv_][$1]) AS_VAR_IF([$1], [""], [$5], [$4])dnl ])dnl PKG_CHECK_VAR libopenmpt-0.6.1+release.autotools/m4/emptydir0000644000175000017500000000000014175541453016353 00000000000000libopenmpt-0.6.1+release.autotools/man/0000755000175000017500000000000014175541576015126 500000000000000libopenmpt-0.6.1+release.autotools/man/openmpt123.10000644000175000017500000001006014175541512017023 00000000000000.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.48.1. .TH OPENMPT123 "1" "January 2022" "openmpt123 v0.6.1" "User Commands" .SH NAME openmpt123 - command line module music player based on libopenmpt .SH SYNOPSIS .B openmpt123 [\fI\,options\/\fR] [\fI\,--\/\fR] \fI\,file1 \/\fR[\fI\,file2\/\fR] ... .SH DESCRIPTION openmpt123 plays module music files. .SH OPTIONS .TP \fB\-h\fR, \fB\-\-help\fR Show help .TP \fB\-\-help\-keyboard\fR Show keyboard hotkeys in ui mode .TP \fB\-q\fR, \fB\-\-quiet\fR Suppress non\-error screen output .TP \fB\-v\fR, \fB\-\-verbose\fR Show more screen output .TP \fB\-\-version\fR Show version information and exit .TP \fB\-\-short\-version\fR Show version number and nothing else .TP \fB\-\-long\-version\fR Show long version information and exit .TP \fB\-\-credits\fR Show elaborate contributors list .TP \fB\-\-license\fR Show license .TP \fB\-\-probe\fR Probe each file whether it is a supported file format .TP \fB\-\-info\fR Display information about each file .TP \fB\-\-ui\fR Interactively play each file .TP \fB\-\-batch\fR Play each file .TP \fB\-\-render\fR Render each file to individual PCM data files .TP \fB\-\-terminal\-width\fR n Assume terminal is n characters wide [default: 72] .TP \fB\-\-terminal\-height\fR n Assume terminal is n characters high [default: 23] .TP \fB\-\-[no\-]progress\fR Show playback progress [default: 0] .TP \fB\-\-[no\-]meters\fR Show peak meters [default: 0] .TP \fB\-\-[no\-]channel\-meters\fR Show channel peak meters (EXPERIMENTAL) [default: 0] .TP \fB\-\-[no\-]pattern\fR Show pattern (EXPERIMENTAL) [default: 0] .TP \fB\-\-[no\-]details\fR Show song details [default: 1] .TP \fB\-\-[no\-]message\fR Show song message [default: 0] .TP \fB\-\-update\fR n Set output update interval to n ms [default: \fB\-1]\fR .TP \fB\-\-samplerate\fR n Set samplerate to n Hz [default: 48000] .TP \fB\-\-channels\fR n use n [1,2,4] output channels [default: 2] .TP \fB\-\-[no\-]float\fR Output 32bit floating point instead of 16bit integer [default: 1] .TP \fB\-\-gain\fR n Set output gain to n dB [default: 0] .TP \fB\-\-stereo\fR n Set stereo separation to n % [default: 100] .TP \fB\-\-filter\fR n Set interpolation filter taps to n [1,2,4,8] [default: 8] .TP \fB\-\-ramping\fR n Set volume ramping strength n [0..5] [default: \fB\-1]\fR .TP \fB\-\-tempo\fR f Set tempo factor f [default: 1] .TP \fB\-\-pitch\fR f Set pitch factor f [default: 1] .TP \fB\-\-dither\fR n Dither type to use (if applicable for selected output format): [0=off,1=auto,2=0.5bit,3=1bit] [default: 1] .TP \fB\-\-playlist\fR file Load playlist from file .TP \fB\-\-[no\-]randomize\fR Randomize playlist [default: 0] .TP \fB\-\-[no\-]shuffle\fR Shuffle through playlist [default: 0] .TP \fB\-\-[no\-]restart\fR Restart playlist when finished [default: 0] .TP \fB\-\-subsong\fR n Select subsong n (\fB\-1\fR means play all subsongs consecutively) [default: \fB\-1]\fR .TP \fB\-\-repeat\fR n Repeat song n times (\fB\-1\fR means forever) [default: 0] .TP \fB\-\-seek\fR n Seek to n seconds on start [default: 0] .TP \fB\-\-end\-time\fR n Play until position is n seconds (0 means until the end) [default: 0] .TP \fB\-\-ctl\fR c=v Set libopenmpt ctl c to value v .TP \fB\-\-driver\fR n Set output driver [default: default], .TP \fB\-\-device\fR n Set output device [default: default], use \fB\-\-device\fR help to show available devices .TP \fB\-\-buffer\fR n Set output buffer size to n ms [default: \fB\-1]\fR .TP \fB\-\-period\fR n Set output period size to n ms [default: \fB\-1]\fR .TP \fB\-\-stdout\fR Write raw audio data to stdout [default: 0] .TP \fB\-\-output\-type\fR t Use output format t when writing to a individual PCM files (only applies to \fB\-\-render\fR mode) [default: auto] .TP \fB\-o\fR, \fB\-\-output\fR f Write PCM output to file f instead of streaming to audio device (only applies to \fB\-\-ui\fR and \fB\-\-batch\fR modes) [default: ] .TP \fB\-\-force\fR Force overwriting of output file [default: 0] .TP \fB\-\-\fR Interpret further arguments as filenames .SH COPYRIGHT Copyright \(co 2013\-2022 OpenMPT Project Developers and Contributors libopenmpt-0.6.1+release.autotools/openmpt123/0000755000175000017500000000000014175541576016263 500000000000000libopenmpt-0.6.1+release.autotools/openmpt123/openmpt123_allegro42.hpp0000644000175000017500000001207314016702027022462 00000000000000/* * openmpt123_allegro42.hpp * ------------------------ * Purpose: libopenmpt command line player * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #ifndef OPENMPT123_ALLEGRO42_HPP #define OPENMPT123_ALLEGRO42_HPP #include "openmpt123_config.hpp" #include "openmpt123.hpp" #if defined(MPT_WITH_ALLEGRO42) #if defined(__GNUC__) && !defined(__clang__) && !defined(_MSC_VER) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpedantic" #pragma GCC diagnostic ignored "-Wfloat-conversion" #endif #include #if defined(__GNUC__) && !defined(__clang__) && !defined(_MSC_VER) #pragma GCC diagnostic pop #endif namespace openmpt123 { struct allegro42_exception : public exception { static std::string error_to_string() { try { return allegro_error ? std::string( allegro_error ) : std::string(); } catch ( const std::bad_alloc & ) { return std::string(); } } allegro42_exception() : exception( error_to_string() ) { } }; class allegro42_raii { public: allegro42_raii() { if ( allegro_init() != 0 ) { throw allegro42_exception(); } } ~allegro42_raii() { allegro_exit(); } }; class allegro42_sound_raii { public: allegro42_sound_raii() { if ( install_sound( DIGI_AUTODETECT, MIDI_NONE, NULL ) != 0 ) { throw allegro42_exception(); } if ( digi_card == DIGI_NONE ) { remove_sound(); throw exception( "no audio device found" ); } } ~allegro42_sound_raii() { remove_sound(); } }; class allegro42_stream_raii : public write_buffers_polling_wrapper_int { private: allegro42_raii allegro; allegro42_sound_raii allegro_sound; AUDIOSTREAM * stream; std::size_t bits; std::size_t channels; std::uint32_t period_frames; private: std::uint32_t round_up_power2(std::uint32_t x) { std::uint32_t result = 1; while ( result < x ) { result *= 2; } return result; } public: allegro42_stream_raii( commandlineflags & flags, std::ostream & log ) : write_buffers_polling_wrapper_int(flags) , stream(NULL) , bits(16) , channels(flags.channels) , period_frames(1024) { if ( flags.use_float ) { throw exception( "floating point is unsupported" ); } if ( ( flags.channels != 1 ) && ( flags.channels != 2 ) ) { throw exception( "only mono or stereo supported" ); } if ( flags.buffer == default_high ) { flags.buffer = 1024 * 2 * 1000 / flags.samplerate; } else if ( flags.buffer == default_low ) { flags.buffer = 512 * 2 * 1000 / flags.samplerate; } if ( flags.period == default_high ) { flags.period = 1024 / 2 * 1000 / flags.samplerate; } else if ( flags.period == default_low ) { flags.period = 512 / 2 * 1000 / flags.samplerate; } flags.apply_default_buffer_sizes(); period_frames = round_up_power2( ( flags.buffer * flags.samplerate ) / ( 1000 * 2 ) ); set_queue_size_frames( period_frames ); if ( flags.verbose ) { log << "Allegro-4.2:" << std::endl; log << " allegro samplerate: " << get_mixer_frequency() << std::endl; log << " latency: " << flags.buffer << std::endl; log << " period: " << flags.period << std::endl; log << " frames per buffer: " << period_frames << std::endl; log << " ui redraw: " << flags.ui_redraw_interval << std::endl; } stream = play_audio_stream( period_frames, 16, ( flags.channels > 1 ) ? TRUE : FALSE, flags.samplerate, 255, 128 ); if ( !stream ) { bits = 8; stream = play_audio_stream( period_frames, 8, ( flags.channels > 1 ) ? TRUE : FALSE, flags.samplerate, 255, 128 ); if ( !stream ) { throw allegro42_exception(); } } } ~allegro42_stream_raii() { if ( stream ) { stop_audio_stream( stream ); stream = NULL; } } public: bool forward_queue() override { void * p = get_audio_stream_buffer( stream ); if ( !p ) { return false; } for ( std::size_t frame = 0; frame < period_frames; ++frame ) { for ( std::size_t channel = 0; channel < channels; ++channel ) { std::int16_t sample = pop_queue(); if ( bits == 8 ) { std::uint8_t u8sample = ( static_cast( sample ) + 0x8000u ) >> 8; std::memcpy( reinterpret_cast( p ) + ( ( ( frame * channels ) + channel ) * sizeof( std::uint8_t ) ), &u8sample, sizeof( std::uint8_t ) ); } else { std::uint16_t u16sample = static_cast( sample ) + 0x8000u; std::memcpy( reinterpret_cast( p ) + ( ( ( frame * channels ) + channel ) * sizeof( std::uint16_t ) ), &u16sample, sizeof( std::uint16_t ) ); } } } free_audio_stream_buffer( stream ); return true; } bool unpause() override { voice_start( stream->voice ); return true; } bool pause() override { voice_stop( stream->voice ); return true; } bool sleep( int ms ) override { rest( ms ); return true; } }; static std::string show_allegro42_devices( std::ostream & /* log */ ) { std::ostringstream devices; devices << " allegro42:" << std::endl; devices << " " << "0" << ": Default Device" << std::endl; return devices.str(); } } // namespace openmpt123 #endif // MPT_WITH_ALLEGRO42 #endif // OPENMPT123_ALLEGRO42_HPP libopenmpt-0.6.1+release.autotools/openmpt123/openmpt123_config.hpp0000644000175000017500000000175614052445635022153 00000000000000/* * openmpt123_config.hpp * --------------------- * Purpose: libopenmpt command line player * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #ifndef OPENMPT123_CONFIG_HPP #define OPENMPT123_CONFIG_HPP #define MPT_INLINE_NS mpt_openmpt123 #if defined(_WIN32) #ifndef WIN32 #define WIN32 #endif #endif // _WIN32 #if defined(WIN32) #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #ifndef NOMINMAX #define NOMINMAX #endif #ifdef _UNICODE #ifndef UNICODE #define UNICODE #endif #endif #ifdef UNICODE #ifndef _UNICODE #define _UNICODE #endif #endif #endif // WIN32 #if defined(WIN32) #define MPT_WITH_MMIO #endif // WIN32 #if defined(MPT_BUILD_MSVC) #define MPT_WITH_FLAC #define MPT_WITH_PORTAUDIO #if defined(MPT_BUILD_MSVC_STATIC) #define FLAC__NO_DLL #endif #endif // MPT_BUILD_MSVC #define OPENMPT123_VERSION_STRING OPENMPT_API_VERSION_STRING #endif // OPENMPT123_CONFIG_HPP libopenmpt-0.6.1+release.autotools/openmpt123/openmpt123.cpp0000644000175000017500000024332414164006754020617 00000000000000/* * openmpt123.cpp * -------------- * Purpose: libopenmpt command line player * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ static const char * const license = "Copyright (c) 2004-2022, OpenMPT Project Developers and Contributors" "\n" "Copyright (c) 1997-2003, Olivier Lapicque" "\n" "All rights reserved." "\n" "" "\n" "Redistribution and use in source and binary forms, with or without" "\n" "modification, are permitted provided that the following conditions are met:" "\n" " * Redistributions of source code must retain the above copyright" "\n" " notice, this list of conditions and the following disclaimer." "\n" " * Redistributions in binary form must reproduce the above copyright" "\n" " notice, this list of conditions and the following disclaimer in the" "\n" " documentation and/or other materials provided with the distribution." "\n" " * Neither the name of the OpenMPT project nor the" "\n" " names of its contributors may be used to endorse or promote products" "\n" " derived from this software without specific prior written permission." "\n" "" "\n" "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"" "\n" "AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE" "\n" "IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE" "\n" "DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE" "\n" "FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL" "\n" "DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR" "\n" "SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER" "\n" "CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY," "\n" "OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE" "\n" "OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." "\n" ; #include "openmpt123_config.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(__DJGPP__) #include #include #include #include #include #include #include #include #elif defined(WIN32) #include #include #include #include #if defined(__MINGW32__) && !defined(__MINGW64__) #include #endif #include #include #include #include #include #else #include #include #include #include #include #include #endif #include #include "openmpt123.hpp" #include "openmpt123_flac.hpp" #include "openmpt123_mmio.hpp" #include "openmpt123_sndfile.hpp" #include "openmpt123_raw.hpp" #include "openmpt123_stdout.hpp" #include "openmpt123_allegro42.hpp" #include "openmpt123_portaudio.hpp" #include "openmpt123_pulseaudio.hpp" #include "openmpt123_sdl2.hpp" #include "openmpt123_waveout.hpp" namespace openmpt123 { struct silent_exit_exception : public std::exception { }; struct show_license_exception : public std::exception { }; struct show_credits_exception : public std::exception { }; struct show_man_version_exception : public std::exception { }; struct show_man_help_exception : public std::exception { }; struct show_short_version_number_exception : public std::exception { }; struct show_version_number_exception : public std::exception { }; struct show_long_version_number_exception : public std::exception { }; #if defined( WIN32 ) bool IsConsole( DWORD stdHandle ) { HANDLE hStd = GetStdHandle( stdHandle ); if ( ( hStd != NULL ) && ( hStd != INVALID_HANDLE_VALUE ) ) { DWORD mode = 0; if ( GetConsoleMode( hStd, &mode ) != FALSE ) { return true; } } return false; } #endif bool IsTerminal( int fd ) { #if defined( WIN32 ) if ( !_isatty( fd ) ) { return false; } DWORD stdHandle = 0; if ( fd == 0 ) { stdHandle = STD_INPUT_HANDLE; } else if ( fd == 1 ) { stdHandle = STD_OUTPUT_HANDLE; } else if ( fd == 2 ) { stdHandle = STD_ERROR_HANDLE; } return IsConsole( stdHandle ); #else return isatty( fd ) ? true : false; #endif } #if !defined( WIN32 ) static termios saved_attributes; static void reset_input_mode() { tcsetattr( STDIN_FILENO, TCSANOW, &saved_attributes ); } static void set_input_mode() { termios tattr; if ( !isatty( STDIN_FILENO ) ) { return; } tcgetattr( STDIN_FILENO, &saved_attributes ); atexit( reset_input_mode ); tcgetattr( STDIN_FILENO, &tattr ); tattr.c_lflag &= ~( ICANON | ECHO ); tattr.c_cc[VMIN] = 1; tattr.c_cc[VTIME] = 0; tcsetattr( STDIN_FILENO, TCSAFLUSH, &tattr ); } #endif class file_audio_stream_raii : public file_audio_stream_base { private: std::unique_ptr impl; public: file_audio_stream_raii( const commandlineflags & flags, const std::string & filename, std::ostream & log ) : impl(nullptr) { if ( !flags.force_overwrite ) { std::ifstream testfile( filename, std::ios::binary ); if ( testfile ) { throw exception( "file already exists" ); } } if ( false ) { // nothing } else if ( flags.output_extension == "raw" ) { impl = std::make_unique( filename, flags, log ); #ifdef MPT_WITH_MMIO } else if ( flags.output_extension == "wav" ) { impl = std::make_unique( filename, flags, log ); #endif #ifdef MPT_WITH_FLAC } else if ( flags.output_extension == "flac" ) { impl = std::make_unique( filename, flags, log ); #endif #ifdef MPT_WITH_SNDFILE } else { impl = std::make_unique( filename, flags, log ); #endif } if ( !impl ) { throw exception( "file format handler '" + flags.output_extension + "' not found" ); } } virtual ~file_audio_stream_raii() { return; } void write_metadata( std::map metadata ) override { impl->write_metadata( metadata ); } void write_updated_metadata( std::map metadata ) override { impl->write_updated_metadata( metadata ); } void write( const std::vector buffers, std::size_t frames ) override { impl->write( buffers, frames ); } void write( const std::vector buffers, std::size_t frames ) override { impl->write( buffers, frames ); } }; static std::string ctls_to_string( const std::map & ctls ) { std::string result; for ( const auto & ctl : ctls ) { if ( !result.empty() ) { result += "; "; } result += ctl.first + "=" + ctl.second; } return result; } static double tempo_flag_to_double( std::int32_t tempo ) { return std::pow( 2.0, tempo / 24.0 ); } static double pitch_flag_to_double( std::int32_t pitch ) { return std::pow( 2.0, pitch / 24.0 ); } static double my_round( double val ) { if ( val >= 0.0 ) { return std::floor( val + 0.5 ); } else { return std::ceil( val - 0.5 ); } } static std::int32_t double_to_tempo_flag( double factor ) { return static_cast( my_round( std::log( factor ) / std::log( 2.0 ) * 24.0 ) ); } static std::int32_t double_to_pitch_flag( double factor ) { return static_cast( my_round( std::log( factor ) / std::log( 2.0 ) * 24.0 ) ); } static std::ostream & operator << ( std::ostream & s, const commandlineflags & flags ) { s << "Quiet: " << flags.quiet << std::endl; s << "Verbose: " << flags.verbose << std::endl; s << "Mode : " << mode_to_string( flags.mode ) << std::endl; s << "Show progress: " << flags.show_progress << std::endl; s << "Show peak meters: " << flags.show_meters << std::endl; s << "Show channel peak meters: " << flags.show_channel_meters << std::endl; s << "Show details: " << flags.show_details << std::endl; s << "Show message: " << flags.show_message << std::endl; s << "Update: " << flags.ui_redraw_interval << "ms" << std::endl; s << "Device: " << flags.device << std::endl; s << "Buffer: " << flags.buffer << "ms" << std::endl; s << "Period: " << flags.period << "ms" << std::endl; s << "Samplerate: " << flags.samplerate << std::endl; s << "Channels: " << flags.channels << std::endl; s << "Float: " << flags.use_float << std::endl; s << "Gain: " << flags.gain / 100.0 << std::endl; s << "Stereo separation: " << flags.separation << std::endl; s << "Interpolation filter taps: " << flags.filtertaps << std::endl; s << "Volume ramping strength: " << flags.ramping << std::endl; s << "Tempo: " << tempo_flag_to_double( flags.tempo ) << std::endl; s << "Pitch: " << pitch_flag_to_double( flags.pitch ) << std::endl; s << "Output dithering: " << flags.dither << std::endl; s << "Repeat count: " << flags.repeatcount << std::endl; s << "Seek target: " << flags.seek_target << std::endl; s << "End time: " << flags.end_time << std::endl; s << "Standard output: " << flags.use_stdout << std::endl; s << "Output filename: " << flags.output_filename << std::endl; s << "Force overwrite output file: " << flags.force_overwrite << std::endl; s << "Ctls: " << ctls_to_string( flags.ctls ) << std::endl; s << std::endl; s << "Files: " << std::endl; for ( const auto & filename : flags.filenames ) { s << " " << filename << std::endl; } s << std::endl; return s; } static std::string replace( std::string str, const std::string & oldstr, const std::string & newstr ) { std::size_t pos = 0; while ( ( pos = str.find( oldstr, pos ) ) != std::string::npos ) { str.replace( pos, oldstr.length(), newstr ); pos += newstr.length(); } return str; } static bool begins_with( const std::string & str, const std::string & match ) { return ( str.find( match ) == 0 ); } static bool ends_with( const std::string & str, const std::string & match ) { return ( str.rfind( match ) == ( str.length() - match.length() ) ); } static std::string trim_left(std::string str, const std::string &whitespace = std::string()) { std::string::size_type pos = str.find_first_not_of(whitespace); if(pos != std::string::npos) { str.erase(str.begin(), str.begin() + pos); } else if(pos == std::string::npos && str.length() > 0 && str.find_last_of(whitespace) == str.length() - 1) { return std::string(); } return str; } static std::string trim_right(std::string str, const std::string &whitespace = std::string()) { std::string::size_type pos = str.find_last_not_of(whitespace); if(pos != std::string::npos) { str.erase(str.begin() + pos + 1, str.end()); } else if(pos == std::string::npos && str.length() > 0 && str.find_first_of(whitespace) == 0) { return std::string(); } return str; } static std::string trim(std::string str, const std::string &whitespace = std::string()) { return trim_right(trim_left(str, whitespace), whitespace); } static std::string trim_eol( const std::string & str ) { return trim( str, "\r\n" ); } static std::string default_path_separator() { #if defined(WIN32) return "\\"; #else return "/"; #endif } static std::string path_separators() { #if defined(WIN32) return "\\/"; #else return "/"; #endif } static bool is_path_separator( char c ) { #if defined(WIN32) return ( c == '\\' ) || ( c == '/' ); #else return c == '/'; #endif } static std::string get_basepath( std::string filename ) { std::string::size_type pos = filename.find_last_of( path_separators() ); if ( pos == std::string::npos ) { return std::string(); } return filename.substr( 0, pos ) + default_path_separator(); } static bool is_absolute( std::string filename ) { #if defined(WIN32) if ( begins_with( filename, "\\\\?\\UNC\\" ) ) { return true; } if ( begins_with( filename, "\\\\?\\" ) ) { return true; } if ( begins_with( filename, "\\\\" ) ) { return true; // UNC } if ( begins_with( filename, "//" ) ) { return true; // UNC } return ( filename.length() ) >= 3 && ( filename[1] == ':' ) && is_path_separator( filename[2] ); #else return ( filename.length() >= 1 ) && is_path_separator( filename[0] ); #endif } static std::string get_filename( const std::string & filepath ) { if ( filepath.find_last_of( path_separators() ) == std::string::npos ) { return filepath; } return filepath.substr( filepath.find_last_of( path_separators() ) + 1 ); } static std::string prepend_lines( std::string str, const std::string & prefix ) { if ( str.empty() ) { return str; } if ( str.substr( str.length() - 1, 1 ) == std::string("\n") ) { str = str.substr( 0, str.length() - 1 ); } return replace( str, std::string("\n"), std::string("\n") + prefix ); } static std::string bytes_to_string( std::uint64_t bytes ) { static const char * const suffixes[] = { "B", "kB", "MB", "GB", "TB", "PB" }; int offset = 0; while ( bytes > 9999 ) { bytes /= 1000; offset += 1; if ( offset == 5 ) { break; } } std::ostringstream result; result << bytes << suffixes[offset]; return result.str(); } static std::string seconds_to_string( double time ) { std::int64_t time_ms = static_cast( time * 1000 ); std::int64_t milliseconds = time_ms % 1000; std::int64_t seconds = ( time_ms / 1000 ) % 60; std::int64_t minutes = ( time_ms / ( 1000 * 60 ) ) % 60; std::int64_t hours = ( time_ms / ( 1000 * 60 * 60 ) ); std::ostringstream str; if ( hours > 0 ) { str << hours << ":"; } str << std::setfill('0') << std::setw(2) << minutes; str << ":"; str << std::setfill('0') << std::setw(2) << seconds; str << "."; str << std::setfill('0') << std::setw(3) << milliseconds; return str.str(); } static void show_info( std::ostream & log, bool verbose ) { log << "openmpt123" << " v" << OPENMPT123_VERSION_STRING << ", libopenmpt " << openmpt::string::get( "library_version" ) << " (" << "OpenMPT " << openmpt::string::get( "core_version" ) << ")" << std::endl; log << "Copyright (c) 2013-2022 OpenMPT Project Developers and Contributors " << std::endl; if ( !verbose ) { log << std::endl; return; } log << " libopenmpt source..: " << openmpt::string::get( "source_url" ) << std::endl; log << " libopenmpt date....: " << openmpt::string::get( "source_date" ) << std::endl; log << " libopenmpt srcinfo.: "; { std::vector fields; if ( openmpt::string::get( "source_is_package" ) == "1" ) { fields.push_back( "package" ); } if ( openmpt::string::get( "source_is_release" ) == "1" ) { fields.push_back( "release" ); } if ( ( !openmpt::string::get( "source_revision" ).empty() ) && ( openmpt::string::get( "source_revision" ) != "0" ) ) { std::string field = "rev" + openmpt::string::get( "source_revision" ); if ( openmpt::string::get( "source_has_mixed_revisions" ) == "1" ) { field += "+mixed"; } if ( openmpt::string::get( "source_is_modified" ) == "1" ) { field += "+modified"; } fields.push_back( field ); } bool first = true; for ( const auto & field : fields ) { if ( first ) { first = false; } else { log << ", "; } log << field; } } log << std::endl; log << " libopenmpt compiler: " << openmpt::string::get( "build_compiler" ) << std::endl; log << " libopenmpt features: " << openmpt::string::get( "library_features" ) << std::endl; #ifdef MPT_WITH_SDL2 log << " libSDL2 "; SDL_version sdlver; std::memset( &sdlver, 0, sizeof( SDL_version ) ); SDL_GetVersion( &sdlver ); log << static_cast( sdlver.major ) << "." << static_cast( sdlver.minor ) << "." << static_cast( sdlver.patch ); const char * revision = SDL_GetRevision(); if ( revision ) { log << " (" << revision << ")"; } log << ", "; std::memset( &sdlver, 0, sizeof( SDL_version ) ); SDL_VERSION( &sdlver ); log << "API: " << static_cast( sdlver.major ) << "." << static_cast( sdlver.minor ) << "." << static_cast( sdlver.patch ) << ""; log << " " << std::endl; #endif #ifdef MPT_WITH_PULSEAUDIO log << " " << "libpulse, libpulse-simple" << " (headers " << pa_get_headers_version() << ", API " << PA_API_VERSION << ", PROTOCOL " << PA_PROTOCOL_VERSION << ", library " << ( pa_get_library_version() ? pa_get_library_version() : "unknown" ) << ") " << std::endl; #endif #ifdef MPT_WITH_PORTAUDIO log << " " << Pa_GetVersionText() << " (" << Pa_GetVersion() << ") " << std::endl; #endif #ifdef MPT_WITH_FLAC log << " FLAC " << FLAC__VERSION_STRING << ", " << FLAC__VENDOR_STRING << ", API " << FLAC_API_VERSION_CURRENT << "." << FLAC_API_VERSION_REVISION << "." << FLAC_API_VERSION_AGE << " " << std::endl; #endif #ifdef MPT_WITH_SNDFILE char sndfile_info[128]; std::memset( sndfile_info, 0, sizeof( sndfile_info ) ); sf_command( 0, SFC_GET_LIB_VERSION, sndfile_info, sizeof( sndfile_info ) ); sndfile_info[127] = '\0'; log << " libsndfile " << sndfile_info << " " << std::endl; #endif log << std::endl; } static void show_man_version( textout & log ) { log << "openmpt123" << " v" << OPENMPT123_VERSION_STRING << std::endl; log << std::endl; log << "Copyright (c) 2013-2022 OpenMPT Project Developers and Contributors " << std::endl; } static void show_short_version( textout & log ) { log << OPENMPT123_VERSION_STRING << " / " << openmpt::string::get( "library_version" ) << " / " << openmpt::string::get( "core_version" ) << std::endl; log.writeout(); } static void show_version( textout & log ) { show_info( log, false ); log.writeout(); } static void show_long_version( textout & log ) { show_info( log, true ); log.writeout(); } static void show_credits( textout & log ) { show_info( log, false ); log << openmpt::string::get( "contact" ) << std::endl; log << std::endl; log << openmpt::string::get( "credits" ) << std::endl; log.writeout(); } static void show_license( textout & log ) { show_info( log, false ); log << license << std::endl; log.writeout(); } static std::string get_driver_string( const std::string & driver ) { if ( driver.empty() ) { return "default"; } return driver; } static std::string get_device_string( const std::string & device ) { if ( device.empty() ) { return "default"; } return device; } static void show_help( textout & log, bool with_info = true, bool longhelp = false, bool man_version = false, const std::string & message = std::string() ) { if ( with_info ) { show_info( log, false ); } { log << "Usage: openmpt123 [options] [--] file1 [file2] ..." << std::endl; log << std::endl; if ( man_version ) { log << "openmpt123 plays module music files." << std::endl; log << std::endl; } if ( man_version ) { log << "Options:" << std::endl; } log << " -h, --help Show help" << std::endl; log << " --help-keyboard Show keyboard hotkeys in ui mode" << std::endl; log << " -q, --quiet Suppress non-error screen output" << std::endl; log << " -v, --verbose Show more screen output" << std::endl; log << " --version Show version information and exit" << std::endl; log << " --short-version Show version number and nothing else" << std::endl; log << " --long-version Show long version information and exit" << std::endl; log << " --credits Show elaborate contributors list" << std::endl; log << " --license Show license" << std::endl; log << std::endl; log << " --probe Probe each file whether it is a supported file format" << std::endl; log << " --info Display information about each file" << std::endl; log << " --ui Interactively play each file" << std::endl; log << " --batch Play each file" << std::endl; log << " --render Render each file to individual PCM data files" << std::endl; if ( !longhelp ) { log << std::endl; log.writeout(); return; } log << std::endl; log << " --terminal-width n Assume terminal is n characters wide [default: " << commandlineflags().terminal_width << "]" << std::endl; log << " --terminal-height n Assume terminal is n characters high [default: " << commandlineflags().terminal_height << "]" << std::endl; log << std::endl; log << " --[no-]progress Show playback progress [default: " << commandlineflags().show_progress << "]" << std::endl; log << " --[no-]meters Show peak meters [default: " << commandlineflags().show_meters << "]" << std::endl; log << " --[no-]channel-meters Show channel peak meters (EXPERIMENTAL) [default: " << commandlineflags().show_channel_meters << "]" << std::endl; log << " --[no-]pattern Show pattern (EXPERIMENTAL) [default: " << commandlineflags().show_pattern << "]" << std::endl; log << std::endl; log << " --[no-]details Show song details [default: " << commandlineflags().show_details << "]" << std::endl; log << " --[no-]message Show song message [default: " << commandlineflags().show_message << "]" << std::endl; log << std::endl; log << " --update n Set output update interval to n ms [default: " << commandlineflags().ui_redraw_interval << "]" << std::endl; log << std::endl; log << " --samplerate n Set samplerate to n Hz [default: " << commandlineflags().samplerate << "]" << std::endl; log << " --channels n use n [1,2,4] output channels [default: " << commandlineflags().channels << "]" << std::endl; log << " --[no-]float Output 32bit floating point instead of 16bit integer [default: " << commandlineflags().use_float << "]" << std::endl; log << std::endl; log << " --gain n Set output gain to n dB [default: " << commandlineflags().gain / 100.0 << "]" << std::endl; log << " --stereo n Set stereo separation to n % [default: " << commandlineflags().separation << "]" << std::endl; log << " --filter n Set interpolation filter taps to n [1,2,4,8] [default: " << commandlineflags().filtertaps << "]" << std::endl; log << " --ramping n Set volume ramping strength n [0..5] [default: " << commandlineflags().ramping << "]" << std::endl; log << " --tempo f Set tempo factor f [default: " << tempo_flag_to_double( commandlineflags().tempo ) << "]" << std::endl; log << " --pitch f Set pitch factor f [default: " << pitch_flag_to_double( commandlineflags().pitch ) << "]" << std::endl; log << " --dither n Dither type to use (if applicable for selected output format): [0=off,1=auto,2=0.5bit,3=1bit] [default: " << commandlineflags().dither << "]" << std::endl; log << std::endl; log << " --playlist file Load playlist from file" << std::endl; log << " --[no-]randomize Randomize playlist [default: " << commandlineflags().randomize << "]" << std::endl; log << " --[no-]shuffle Shuffle through playlist [default: " << commandlineflags().shuffle << "]" << std::endl; log << " --[no-]restart Restart playlist when finished [default: " << commandlineflags().restart << "]" << std::endl; log << std::endl; log << " --subsong n Select subsong n (-1 means play all subsongs consecutively) [default: " << commandlineflags().subsong << "]" << std::endl; log << " --repeat n Repeat song n times (-1 means forever) [default: " << commandlineflags().repeatcount << "]" << std::endl; log << " --seek n Seek to n seconds on start [default: " << commandlineflags().seek_target << "]" << std::endl; log << " --end-time n Play until position is n seconds (0 means until the end) [default: " << commandlineflags().end_time << "]" << std::endl; log << std::endl; log << " --ctl c=v Set libopenmpt ctl c to value v" << std::endl; log << std::endl; log << " --driver n Set output driver [default: " << get_driver_string( commandlineflags().driver ) << "]," << std::endl; log << " --device n Set output device [default: " << get_device_string( commandlineflags().device ) << "]," << std::endl; log << " use --device help to show available devices" << std::endl; log << " --buffer n Set output buffer size to n ms [default: " << commandlineflags().buffer << "]" << std::endl; log << " --period n Set output period size to n ms [default: " << commandlineflags().period << "]" << std::endl; log << " --stdout Write raw audio data to stdout [default: " << commandlineflags().use_stdout << "]" << std::endl; log << " --output-type t Use output format t when writing to a individual PCM files (only applies to --render mode) [default: " << commandlineflags().output_extension << "]" << std::endl; log << " -o, --output f Write PCM output to file f instead of streaming to audio device (only applies to --ui and --batch modes) [default: " << commandlineflags().output_filename << "]" << std::endl; log << " --force Force overwriting of output file [default: " << commandlineflags().force_overwrite << "]" << std::endl; log << std::endl; log << " -- Interpret further arguments as filenames" << std::endl; log << std::endl; if ( !man_version ) { log << " Supported file formats: " << std::endl; log << " "; std::vector extensions = openmpt::get_supported_extensions(); bool first = true; for ( const auto & extension : extensions ) { if ( first ) { first = false; } else { log << ", "; } log << extension; } log << std::endl; } } log << std::endl; if ( message.size() > 0 ) { log << message; log << std::endl; } log.writeout(); } static void show_help_keyboard( textout & log ) { show_info( log, false ); log << "Keyboard hotkeys (use 'openmpt123 --ui'):" << std::endl; log << std::endl; log << " [q] quit" << std::endl; log << " [ ] pause / unpause" << std::endl; log << " [N] skip 10 files backward" << std::endl; log << " [n] prev file" << std::endl; log << " [m] next file" << std::endl; log << " [M] skip 10 files forward" << std::endl; log << " [h] seek 10 seconds backward" << std::endl; log << " [j] seek 1 seconds backward" << std::endl; log << " [k] seek 1 seconds forward" << std::endl; log << " [l] seek 10 seconds forward" << std::endl; log << " [u]|[i] +/- tempo" << std::endl; log << " [o]|[p] +/- pitch" << std::endl; log << " [3]|[4] +/- gain" << std::endl; log << " [5]|[6] +/- stereo separation" << std::endl; log << " [7]|[8] +/- filter taps" << std::endl; log << " [9]|[0] +/- volume ramping" << std::endl; log << std::endl; log.writeout(); } template < typename Tmod > static void apply_mod_settings( commandlineflags & flags, Tmod & mod ) { flags.separation = std::max( flags.separation, std::int32_t( 0 ) ); flags.filtertaps = std::max( flags.filtertaps, std::int32_t( 1 ) ); flags.filtertaps = std::min( flags.filtertaps, std::int32_t( 8 ) ); flags.ramping = std::max( flags.ramping, std::int32_t( -1 ) ); flags.ramping = std::min( flags.ramping, std::int32_t( 10 ) ); flags.tempo = std::max( flags.tempo, std::int32_t( -48 ) ); flags.tempo = std::min( flags.tempo, std::int32_t( 48 ) ); flags.pitch = std::max( flags.pitch, std::int32_t( -48 ) ); flags.pitch = std::min( flags.pitch, std::int32_t( 48 ) ); mod.set_render_param( openmpt::module::RENDER_MASTERGAIN_MILLIBEL, flags.gain ); mod.set_render_param( openmpt::module::RENDER_STEREOSEPARATION_PERCENT, flags.separation ); mod.set_render_param( openmpt::module::RENDER_INTERPOLATIONFILTER_LENGTH, flags.filtertaps ); mod.set_render_param( openmpt::module::RENDER_VOLUMERAMPING_STRENGTH, flags.ramping ); try { mod.ctl_set_floatingpoint( "play.tempo_factor", tempo_flag_to_double( flags.tempo ) ); } catch ( const openmpt::exception & ) { // ignore } try { mod.ctl_set_floatingpoint( "play.pitch_factor", pitch_flag_to_double( flags.pitch ) ); } catch ( const openmpt::exception & ) { // ignore } mod.ctl_set_integer( "dither", flags.dither ); } struct prev_file { int count; prev_file( int c ) : count(c) { } }; struct next_file { int count; next_file( int c ) : count(c) { } }; template < typename Tmod > static bool handle_keypress( int c, commandlineflags & flags, Tmod & mod, write_buffers_interface & audio_stream ) { switch ( c ) { case 'q': throw silent_exit_exception(); break; case 'N': throw prev_file(10); break; case 'n': throw prev_file(1); break; case ' ': if ( !flags.paused ) { flags.paused = audio_stream.pause(); } else { flags.paused = false; audio_stream.unpause(); } break; case 'h': mod.set_position_seconds( mod.get_position_seconds() - 10.0 ); break; case 'j': mod.set_position_seconds( mod.get_position_seconds() - 1.0 ); break; case 'k': mod.set_position_seconds( mod.get_position_seconds() + 1.0 ); break; case 'l': mod.set_position_seconds( mod.get_position_seconds() + 10.0 ); break; case 'H': mod.set_position_order_row( mod.get_current_order() - 1, 0 ); break; case 'J': mod.set_position_order_row( mod.get_current_order(), mod.get_current_row() - 1 ); break; case 'K': mod.set_position_order_row( mod.get_current_order(), mod.get_current_row() + 1 ); break; case 'L': mod.set_position_order_row( mod.get_current_order() + 1, 0 ); break; case 'm': throw next_file(1); break; case 'M': throw next_file(10); break; case 'u': flags.tempo -= 1; apply_mod_settings( flags, mod ); break; case 'i': flags.tempo += 1; apply_mod_settings( flags, mod ); break; case 'o': flags.pitch -= 1; apply_mod_settings( flags, mod ); break; case 'p': flags.pitch += 1; apply_mod_settings( flags, mod ); break; case '3': flags.gain -=100; apply_mod_settings( flags, mod ); break; case '4': flags.gain +=100; apply_mod_settings( flags, mod ); break; case '5': flags.separation -= 5; apply_mod_settings( flags, mod ); break; case '6': flags.separation += 5; apply_mod_settings( flags, mod ); break; case '7': flags.filtertaps /= 2; apply_mod_settings( flags, mod ); break; case '8': flags.filtertaps *= 2; apply_mod_settings( flags, mod ); break; case '9': flags.ramping -= 1; apply_mod_settings( flags, mod ); break; case '0': flags.ramping += 1; apply_mod_settings( flags, mod ); break; } return true; } struct meter_channel { float peak; float clip; float hold; float hold_age; meter_channel() : peak(0.0f) , clip(0.0f) , hold(0.0f) , hold_age(0.0f) { return; } }; struct meter_type { meter_channel channels[4]; }; static const float falloff_rate = 20.0f / 1.7f; static void update_meter( meter_type & meter, const commandlineflags & flags, std::size_t count, const std::int16_t * const * buffers ) { float falloff_factor = std::pow( 10.0f, -falloff_rate / flags.samplerate / 20.0f ); for ( int channel = 0; channel < flags.channels; ++channel ) { meter.channels[channel].peak = 0.0f; for ( std::size_t frame = 0; frame < count; ++frame ) { if ( meter.channels[channel].clip != 0.0f ) { meter.channels[channel].clip -= ( 1.0f / 2.0f ) * 1.0f / static_cast( flags.samplerate ); if ( meter.channels[channel].clip <= 0.0f ) { meter.channels[channel].clip = 0.0f; } } float val = std::fabs( buffers[channel][frame] / 32768.0f ); if ( val >= 1.0f ) { meter.channels[channel].clip = 1.0f; } if ( val > meter.channels[channel].peak ) { meter.channels[channel].peak = val; } meter.channels[channel].hold *= falloff_factor; if ( val > meter.channels[channel].hold ) { meter.channels[channel].hold = val; meter.channels[channel].hold_age = 0.0f; } else { meter.channels[channel].hold_age += 1.0f / static_cast( flags.samplerate ); } } } } static void update_meter( meter_type & meter, const commandlineflags & flags, std::size_t count, const float * const * buffers ) { float falloff_factor = std::pow( 10.0f, -falloff_rate / flags.samplerate / 20.0f ); for ( int channel = 0; channel < flags.channels; ++channel ) { if ( !count ) { meter = meter_type(); } meter.channels[channel].peak = 0.0f; for ( std::size_t frame = 0; frame < count; ++frame ) { if ( meter.channels[channel].clip != 0.0f ) { meter.channels[channel].clip -= ( 1.0f / 2.0f ) * 1.0f / static_cast( flags.samplerate ); if ( meter.channels[channel].clip <= 0.0f ) { meter.channels[channel].clip = 0.0f; } } float val = std::fabs( buffers[channel][frame] ); if ( val >= 1.0f ) { meter.channels[channel].clip = 1.0f; } if ( val > meter.channels[channel].peak ) { meter.channels[channel].peak = val; } meter.channels[channel].hold *= falloff_factor; if ( val > meter.channels[channel].hold ) { meter.channels[channel].hold = val; meter.channels[channel].hold_age = 0.0f; } else { meter.channels[channel].hold_age += 1.0f / static_cast( flags.samplerate ); } } } } static const char * const channel_tags[4][4] = { { " C", " ", " ", " " }, { " L", " R", " ", " " }, { "FL", "FR", "RC", " " }, { "FL", "FR", "RL", "RR" }, }; static std::string channel_to_string( int channels, int channel, const meter_channel & meter, bool tiny = false ) { int val = std::numeric_limits::min(); int hold_pos = std::numeric_limits::min(); if ( meter.peak > 0.0f ) { float db = 20.0f * std::log10( meter.peak ); val = static_cast( db + 48.0f ); } if ( meter.hold > 0.0f ) { float db_hold = 20.0f * std::log10( meter.hold ); hold_pos = static_cast( db_hold + 48.0f ); } if ( val < 0 ) { val = 0; } int headroom = val; if ( val > 48 ) { val = 48; } headroom -= val; if ( headroom < 0 ) { headroom = 0; } if ( headroom > 12 ) { headroom = 12; } headroom -= 1; // clip indicator if ( headroom < 0 ) { headroom = 0; } if ( tiny ) { if ( meter.clip != 0.0f || meter.peak >= 1.0f ) { return "#"; } else if ( meter.peak > std::pow( 10.0f, -6.0f / 20.0f ) ) { return "O"; } else if ( meter.peak > std::pow( 10.0f, -12.0f / 20.0f ) ) { return "o"; } else if ( meter.peak > std::pow( 10.0f, -18.0f / 20.0f ) ) { return "."; } else { return " "; } } else { std::ostringstream res1; std::ostringstream res2; res1 << " " << channel_tags[channels-1][channel] << " : " ; res2 << std::string(val,'>') << std::string(std::size_t{48}-val,' ') << ( ( meter.clip != 0.0f ) ? "#" : ":" ) << std::string(headroom,'>') << std::string(std::size_t{12}-headroom,' ') ; std::string tmp = res2.str(); if ( 0 <= hold_pos && hold_pos <= 60 ) { if ( hold_pos == 48 ) { tmp[hold_pos] = '#'; } else { tmp[hold_pos] = ':'; } } return res1.str() + tmp; } } static char peak_to_char( float peak ) { if ( peak >= 1.0f ) { return '#'; } else if ( peak >= 0.5f ) { return 'O'; } else if ( peak >= 0.25f ) { return 'o'; } else if ( peak >= 0.125f ) { return '.'; } else { return ' '; } } static std::string peak_to_string_left( float peak, int width ) { std::string result; float thresh = 1.0f; while ( width-- ) { if ( peak >= thresh ) { if ( thresh == 1.0f ) { result.push_back( '#' ); } else { result.push_back( '<' ); } } else { result.push_back( ' ' ); } thresh *= 0.5f; } return result; } static std::string peak_to_string_right( float peak, int width ) { std::string result; float thresh = 1.0f; while ( width-- ) { if ( peak >= thresh ) { if ( thresh == 1.0f ) { result.push_back( '#' ); } else { result.push_back( '>' ); } } else { result.push_back( ' ' ); } thresh *= 0.5f; } std::reverse( result.begin(), result.end() ); return result; } static void draw_meters( std::ostream & log, const meter_type & meter, const commandlineflags & flags ) { for ( int channel = 0; channel < flags.channels; ++channel ) { log << channel_to_string( flags.channels, channel, meter.channels[channel] ) << std::endl; } } static void draw_meters_tiny( std::ostream & log, const meter_type & meter, const commandlineflags & flags ) { for ( int channel = 0; channel < flags.channels; ++channel ) { log << channel_to_string( flags.channels, channel, meter.channels[channel], true ); } } static void draw_channel_meters_tiny( std::ostream & log, float peak ) { log << peak_to_char( peak ); } static void draw_channel_meters_tiny( std::ostream & log, float peak_left, float peak_right ) { log << peak_to_char( peak_left ) << peak_to_char( peak_right ); } static void draw_channel_meters( std::ostream & log, float peak_left, float peak_right, int width ) { if ( width >= 8 + 1 + 8 ) { width = 8 + 1 + 8; } log << peak_to_string_left( peak_left, width / 2 ) << ( width % 2 == 1 ? ":" : "" ) << peak_to_string_right( peak_right, width / 2 ); } template < typename Tsample, typename Tmod > void render_loop( commandlineflags & flags, Tmod & mod, double & duration, textout & log, write_buffers_interface & audio_stream ) { log.writeout(); std::size_t bufsize; if ( flags.mode == Mode::UI ) { bufsize = std::min( flags.ui_redraw_interval, flags.period ) * flags.samplerate / 1000; } else if ( flags.mode == Mode::Batch ) { bufsize = flags.period * flags.samplerate / 1000; } else { bufsize = 1024; } std::int64_t last_redraw_frame = std::int64_t{0} - flags.ui_redraw_interval; std::int64_t rendered_frames = 0; std::vector left( bufsize ); std::vector right( bufsize ); std::vector rear_left( bufsize ); std::vector rear_right( bufsize ); std::vector buffers( 4 ) ; buffers[0] = left.data(); buffers[1] = right.data(); buffers[2] = rear_left.data(); buffers[3] = rear_right.data(); buffers.resize( flags.channels ); meter_type meter; const bool multiline = flags.show_ui; int lines = 0; int pattern_lines = 0; if ( multiline ) { lines += 1; if ( flags.show_ui ) { lines += 1; } if ( flags.show_meters ) { for ( int channel = 0; channel < flags.channels; ++channel ) { lines += 1; } } if ( flags.show_channel_meters ) { lines += 1; } if ( flags.show_details ) { lines += 1; if ( flags.show_progress ) { lines += 1; } } if ( flags.show_progress ) { lines += 1; } if ( flags.show_pattern ) { pattern_lines = flags.terminal_height - lines - 1; lines = flags.terminal_height - 1; } } else if ( flags.show_ui || flags.show_details || flags.show_progress ) { log << std::endl; } for ( int line = 0; line < lines; ++line ) { log << std::endl; } log.writeout(); double cpu_smooth = 0.0; while ( true ) { if ( flags.mode == Mode::UI ) { #if defined( __DJGPP__ ) while ( kbhit() ) { int c = getch(); if ( !handle_keypress( c, flags, mod, audio_stream ) ) { return; } } #elif defined( WIN32 ) && defined( UNICODE ) while ( _kbhit() ) { wint_t c = _getwch(); if ( !handle_keypress( c, flags, mod, audio_stream ) ) { return; } } #elif defined( WIN32 ) while ( _kbhit() ) { int c = _getch(); if ( !handle_keypress( c, flags, mod, audio_stream ) ) { return; } } #else while ( true ) { pollfd pollfds; pollfds.fd = STDIN_FILENO; pollfds.events = POLLIN; poll(&pollfds, 1, 0); if ( !( pollfds.revents & POLLIN ) ) { break; } char c = 0; if ( read( STDIN_FILENO, &c, 1 ) != 1 ) { break; } if ( !handle_keypress( c, flags, mod, audio_stream ) ) { return; } } #endif if ( flags.paused ) { audio_stream.sleep( flags.ui_redraw_interval ); continue; } } std::clock_t cpu_beg = 0; std::clock_t cpu_end = 0; if ( flags.show_details ) { cpu_beg = std::clock(); } std::size_t count = 0; switch ( flags.channels ) { case 1: count = mod.read( flags.samplerate, bufsize, left.data() ); break; case 2: count = mod.read( flags.samplerate, bufsize, left.data(), right.data() ); break; case 4: count = mod.read( flags.samplerate, bufsize, left.data(), right.data(), rear_left.data(), rear_right.data() ); break; } char cpu_str[64] = ""; if ( flags.show_details ) { cpu_end = std::clock(); if ( count > 0 ) { double cpu = 1.0; cpu *= ( static_cast( cpu_end ) - static_cast( cpu_beg ) ) / static_cast( CLOCKS_PER_SEC ); cpu /= ( static_cast( count ) ) / static_cast( flags.samplerate ); double mix = ( static_cast( count ) ) / static_cast( flags.samplerate ); cpu_smooth = ( 1.0 - mix ) * cpu_smooth + mix * cpu; sprintf( cpu_str, "%.2f%%", cpu_smooth * 100.0 ); } } if ( flags.show_meters ) { update_meter( meter, flags, count, buffers.data() ); } if ( count > 0 ) { audio_stream.write( buffers, count ); } if ( count > 0 ) { rendered_frames += count; if ( rendered_frames >= last_redraw_frame + ( flags.ui_redraw_interval * flags.samplerate / 1000 ) ) { last_redraw_frame = rendered_frames; } else { continue; } } if ( multiline ) { log.cursor_up( lines ); log << std::endl; if ( flags.show_meters ) { draw_meters( log, meter, flags ); } if ( flags.show_channel_meters ) { int width = ( flags.terminal_width - 3 ) / mod.get_num_channels(); if ( width > 11 ) { width = 11; } log << " "; for ( std::int32_t channel = 0; channel < mod.get_num_channels(); ++channel ) { if ( width >= 3 ) { log << ":"; } if ( width == 1 ) { draw_channel_meters_tiny( log, ( mod.get_current_channel_vu_left( channel ) + mod.get_current_channel_vu_right( channel ) ) * (1.0f/std::sqrt(2.0f)) ); } else if ( width <= 4 ) { draw_channel_meters_tiny( log, mod.get_current_channel_vu_left( channel ), mod.get_current_channel_vu_right( channel ) ); } else { draw_channel_meters( log, mod.get_current_channel_vu_left( channel ), mod.get_current_channel_vu_right( channel ), width - 1 ); } } if ( width >= 3 ) { log << ":"; } log << std::endl; } if ( flags.show_pattern ) { int width = ( flags.terminal_width - 3 ) / mod.get_num_channels(); if ( width > 13 + 1 ) { width = 13 + 1; } for ( std::int32_t line = 0; line < pattern_lines; ++line ) { std::int32_t row = mod.get_current_row() - ( pattern_lines / 2 ) + line; if ( row == mod.get_current_row() ) { log << ">"; } else { log << " "; } if ( row < 0 || row >= mod.get_pattern_num_rows( mod.get_current_pattern() ) ) { for ( std::int32_t channel = 0; channel < mod.get_num_channels(); ++channel ) { if ( width >= 3 ) { log << ":"; } log << std::string( width >= 3 ? width - 1 : width, ' ' ); } } else { for ( std::int32_t channel = 0; channel < mod.get_num_channels(); ++channel ) { if ( width >= 3 ) { if ( row == mod.get_current_row() ) { log << "+"; } else { log << ":"; } } log << mod.format_pattern_row_channel( mod.get_current_pattern(), row, channel, width >= 3 ? width - 1 : width ); } } if ( width >= 3 ) { log << ":"; } log << std::endl; } } if ( flags.show_ui ) { log << "Settings...: "; log << "Gain: " << flags.gain * 0.01f << " dB" << " "; log << "Stereo: " << flags.separation << " %" << " "; log << "Filter: " << flags.filtertaps << " taps" << " "; log << "Ramping: " << flags.ramping << " "; log << std::endl; } if ( flags.show_details ) { log << "Mixer......: "; log << "CPU:" << std::setw(6) << std::setfill(':') << cpu_str; log << " "; log << "Chn:" << std::setw(3) << std::setfill(':') << mod.get_current_playing_channels(); log << " "; log << std::endl; if ( flags.show_progress ) { log << "Player.....: "; log << "Ord:" << std::setw(3) << std::setfill(':') << mod.get_current_order() << "/" << std::setw(3) << std::setfill(':') << mod.get_num_orders(); log << " "; log << "Pat:" << std::setw(3) << std::setfill(':') << mod.get_current_pattern(); log << " "; log << "Row:" << std::setw(3) << std::setfill(':') << mod.get_current_row(); log << " "; log << "Spd:" << std::setw(2) << std::setfill(':') << mod.get_current_speed(); log << " "; log << "Tmp:" << std::setw(3) << std::setfill(':') << mod.get_current_tempo(); log << " "; log << std::endl; } } if ( flags.show_progress ) { log << "Position...: " << seconds_to_string( mod.get_position_seconds() ) << " / " << seconds_to_string( duration ) << " " << std::endl; } } else if ( flags.show_channel_meters ) { if ( flags.show_ui || flags.show_details || flags.show_progress ) { int width = ( flags.terminal_width - 3 ) / mod.get_num_channels(); log << " "; for ( std::int32_t channel = 0; channel < mod.get_num_channels(); ++channel ) { if ( width >= 3 ) { log << ":"; } if ( width == 1 ) { draw_channel_meters_tiny( log, ( mod.get_current_channel_vu_left( channel ) + mod.get_current_channel_vu_right( channel ) ) * (1.0f/std::sqrt(2.0f)) ); } else if ( width <= 4 ) { draw_channel_meters_tiny( log, mod.get_current_channel_vu_left( channel ), mod.get_current_channel_vu_right( channel ) ); } else { draw_channel_meters( log, mod.get_current_channel_vu_left( channel ), mod.get_current_channel_vu_right( channel ), width - 1 ); } } if ( width >= 3 ) { log << ":"; } } log << " " << "\r"; } else { if ( flags.show_ui ) { log << " "; log << std::setw(3) << std::setfill(':') << flags.gain * 0.01f << "dB"; log << "|"; log << std::setw(3) << std::setfill(':') << flags.separation << "%"; log << "|"; log << std::setw(2) << std::setfill(':') << flags.filtertaps << "taps"; log << "|"; log << std::setw(3) << std::setfill(':') << flags.ramping; } if ( flags.show_meters ) { log << " "; draw_meters_tiny( log, meter, flags ); } if ( flags.show_details && flags.show_ui ) { log << " "; log << "CPU:" << std::setw(6) << std::setfill(':') << cpu_str; log << "|"; log << "Chn:" << std::setw(3) << std::setfill(':') << mod.get_current_playing_channels(); } if ( flags.show_details && !flags.show_ui ) { if ( flags.show_progress ) { log << " "; log << "Ord:" << std::setw(3) << std::setfill(':') << mod.get_current_order() << "/" << std::setw(3) << std::setfill(':') << mod.get_num_orders(); log << "|"; log << "Pat:" << std::setw(3) << std::setfill(':') << mod.get_current_pattern(); log << "|"; log << "Row:" << std::setw(3) << std::setfill(':') << mod.get_current_row(); log << " "; log << "Spd:" << std::setw(2) << std::setfill(':') << mod.get_current_speed(); log << "|"; log << "Tmp:" << std::setw(3) << std::setfill(':') << mod.get_current_tempo(); } } if ( flags.show_progress ) { log << " "; log << seconds_to_string( mod.get_position_seconds() ); log << "/"; log << seconds_to_string( duration ); } if ( flags.show_ui || flags.show_details || flags.show_progress ) { log << " " << "\r"; } } log.writeout(); if ( count == 0 ) { break; } if ( flags.end_time > 0 && mod.get_position_seconds() >= flags.end_time ) { break; } } log.writeout(); } template < typename Tmod > std::map get_metadata( const Tmod & mod ) { std::map result; const std::vector metadata_keys = mod.get_metadata_keys(); for ( const auto & key : metadata_keys ) { result[ key ] = mod.get_metadata( key ); } return result; } class set_field : private std::ostringstream { private: std::vector & fields; public: set_field( std::vector & fields, const std::string & name ) : fields(fields) { fields.push_back( name ); } std::ostream & ostream() { return *this; } ~set_field() { fields.back().val = str(); } }; static void show_fields( textout & log, const std::vector & fields ) { const std::size_t fw = 11; for ( const auto & field :fields ) { std::string key = field.key; std::string val = field.val; if ( key.length() < fw ) { key += std::string( fw - key.length(), '.' ); } if ( key.length() > fw ) { key = key.substr( 0, fw ); } key += ": "; val = prepend_lines( val, std::string( fw, ' ' ) + ": " ); log << key << val << std::endl; } } static void probe_mod_file( commandlineflags & flags, const std::string & filename, std::uint64_t filesize, std::istream & data_stream, textout & log ) { log.writeout(); std::vector fields; if ( flags.filenames.size() > 1 ) { set_field( fields, "Playlist" ).ostream() << flags.playlist_index + 1 << "/" << flags.filenames.size(); set_field( fields, "Prev/Next" ).ostream() << "'" << ( flags.playlist_index > 0 ? get_filename( flags.filenames[ flags.playlist_index - 1 ] ) : std::string() ) << "'" << " / " << "['" << get_filename( filename ) << "']" << " / " << "'" << ( flags.playlist_index + 1 < flags.filenames.size() ? get_filename( flags.filenames[ flags.playlist_index + 1 ] ) : std::string() ) << "'" ; } if ( flags.verbose ) { set_field( fields, "Path" ).ostream() << filename; } if ( flags.show_details ) { set_field( fields, "Filename" ).ostream() << get_filename( filename ); set_field( fields, "Size" ).ostream() << bytes_to_string( filesize ); } int probe_result = openmpt::probe_file_header( openmpt::probe_file_header_flags_default2, data_stream ); std::string probe_result_string; switch ( probe_result ) { case openmpt::probe_file_header_result_success: probe_result_string = "Success"; break; case openmpt::probe_file_header_result_failure: probe_result_string = "Failure"; break; case openmpt::probe_file_header_result_wantmoredata: probe_result_string = "Insufficient Data"; break; default: probe_result_string = "Internal Error"; break; } set_field( fields, "Probe" ).ostream() << probe_result_string; show_fields( log, fields ); log.writeout(); } template < typename Tmod > void render_mod_file( commandlineflags & flags, const std::string & filename, std::uint64_t filesize, Tmod & mod, textout & log, write_buffers_interface & audio_stream ) { log.writeout(); if ( flags.mode != Mode::Probe && flags.mode != Mode::Info ) { mod.set_repeat_count( flags.repeatcount ); apply_mod_settings( flags, mod ); } double duration = mod.get_duration_seconds(); std::vector fields; if ( flags.filenames.size() > 1 ) { set_field( fields, "Playlist" ).ostream() << flags.playlist_index + 1 << "/" << flags.filenames.size(); set_field( fields, "Prev/Next" ).ostream() << "'" << ( flags.playlist_index > 0 ? get_filename( flags.filenames[ flags.playlist_index - 1 ] ) : std::string() ) << "'" << " / " << "['" << get_filename( filename ) << "']" << " / " << "'" << ( flags.playlist_index + 1 < flags.filenames.size() ? get_filename( flags.filenames[ flags.playlist_index + 1 ] ) : std::string() ) << "'" ; } if ( flags.verbose ) { set_field( fields, "Path" ).ostream() << filename; } if ( flags.show_details ) { set_field( fields, "Filename" ).ostream() << get_filename( filename ); set_field( fields, "Size" ).ostream() << bytes_to_string( filesize ); if ( !mod.get_metadata( "warnings" ).empty() ) { set_field( fields, "Warnings" ).ostream() << mod.get_metadata( "warnings" ); } if ( !mod.get_metadata( "container" ).empty() ) { set_field( fields, "Container" ).ostream() << mod.get_metadata( "container" ) << " (" << mod.get_metadata( "container_long" ) << ")"; } set_field( fields, "Type" ).ostream() << mod.get_metadata( "type" ) << " (" << mod.get_metadata( "type_long" ) << ")"; if ( !mod.get_metadata( "originaltype" ).empty() ) { set_field( fields, "Orig. Type" ).ostream() << mod.get_metadata( "originaltype" ) << " (" << mod.get_metadata( "originaltype_long" ) << ")"; } if ( ( mod.get_num_subsongs() > 1 ) && ( flags.subsong != -1 ) ) { set_field( fields, "Subsong" ).ostream() << flags.subsong; } set_field( fields, "Tracker" ).ostream() << mod.get_metadata( "tracker" ); if ( !mod.get_metadata( "date" ).empty() ) { set_field( fields, "Date" ).ostream() << mod.get_metadata( "date" ); } if ( !mod.get_metadata( "artist" ).empty() ) { set_field( fields, "Artist" ).ostream() << mod.get_metadata( "artist" ); } } if ( true ) { set_field( fields, "Title" ).ostream() << mod.get_metadata( "title" ); set_field( fields, "Duration" ).ostream() << seconds_to_string( duration ); } if ( flags.show_details ) { set_field( fields, "Subsongs" ).ostream() << mod.get_num_subsongs(); set_field( fields, "Channels" ).ostream() << mod.get_num_channels(); set_field( fields, "Orders" ).ostream() << mod.get_num_orders(); set_field( fields, "Patterns" ).ostream() << mod.get_num_patterns(); set_field( fields, "Instruments" ).ostream() << mod.get_num_instruments(); set_field( fields, "Samples" ).ostream() << mod.get_num_samples(); } if ( flags.show_message ) { set_field( fields, "Message" ).ostream() << mod.get_metadata( "message" ); } show_fields( log, fields ); log.writeout(); if ( flags.filenames.size() == 1 || flags.mode == Mode::Render ) { audio_stream.write_metadata( get_metadata( mod ) ); } else { audio_stream.write_updated_metadata( get_metadata( mod ) ); } if ( flags.mode == Mode::Probe || flags.mode == Mode::Info ) { return; } if ( flags.seek_target > 0.0 ) { mod.set_position_seconds( flags.seek_target ); } try { if ( flags.use_float ) { render_loop( flags, mod, duration, log, audio_stream ); } else { render_loop( flags, mod, duration, log, audio_stream ); } if ( flags.show_progress ) { log << std::endl; } } catch ( ... ) { if ( flags.show_progress ) { log << std::endl; } throw; } log.writeout(); } static void probe_file( commandlineflags & flags, const std::string & filename, textout & log ) { log.writeout(); std::ostringstream silentlog; try { #if defined(WIN32) && defined(UNICODE) && !defined(_MSC_VER) std::istringstream file_stream; #else std::ifstream file_stream; #endif std::uint64_t filesize = 0; bool use_stdin = ( filename == "-" ); if ( !use_stdin ) { #if defined(WIN32) && defined(UNICODE) && !defined(_MSC_VER) // Only MSVC has std::ifstream::ifstream(std::wstring). // Fake it for other compilers using _wfopen(). std::string data; FILE * f = _wfopen( mpt::transcode( mpt::common_encoding::utf8, filename ).c_str(), L"rb" ); if ( f ) { while ( !feof( f ) ) { static const std::size_t BUFFER_SIZE = 4096; char buffer[BUFFER_SIZE]; size_t data_read = fread( buffer, 1, BUFFER_SIZE, f ); std::copy( buffer, buffer + data_read, std::back_inserter( data ) ); } fclose( f ); f = NULL; } file_stream.str( data ); filesize = data.length(); #elif defined(_MSC_VER) && defined(UNICODE) file_stream.open( mpt::transcode( mpt::common_encoding::utf8, filename ), std::ios::binary ); file_stream.seekg( 0, std::ios::end ); filesize = file_stream.tellg(); file_stream.seekg( 0, std::ios::beg ); #else file_stream.open( filename, std::ios::binary ); file_stream.seekg( 0, std::ios::end ); filesize = file_stream.tellg(); file_stream.seekg( 0, std::ios::beg ); #endif } std::istream & data_stream = use_stdin ? std::cin : file_stream; if ( data_stream.fail() ) { throw exception( "file open error" ); } probe_mod_file( flags, filename, filesize, data_stream, log ); } catch ( silent_exit_exception & ) { throw; } catch ( std::exception & e ) { if ( !silentlog.str().empty() ) { log << "errors probing '" << filename << "': " << silentlog.str() << std::endl; } else { log << "errors probing '" << filename << "'" << std::endl; } log << "error probing '" << filename << "': " << e.what() << std::endl; } catch ( ... ) { if ( !silentlog.str().empty() ) { log << "errors probing '" << filename << "': " << silentlog.str() << std::endl; } else { log << "errors probing '" << filename << "'" << std::endl; } log << "unknown error probing '" << filename << "'" << std::endl; } log << std::endl; log.writeout(); } static void render_file( commandlineflags & flags, const std::string & filename, textout & log, write_buffers_interface & audio_stream ) { log.writeout(); std::ostringstream silentlog; try { #if defined(WIN32) && defined(UNICODE) && !defined(_MSC_VER) std::istringstream file_stream; #else std::ifstream file_stream; #endif std::uint64_t filesize = 0; bool use_stdin = ( filename == "-" ); if ( !use_stdin ) { #if defined(WIN32) && defined(UNICODE) && !defined(_MSC_VER) // Only MSVC has std::ifstream::ifstream(std::wstring). // Fake it for other compilers using _wfopen(). std::string data; FILE * f = _wfopen( mpt::transcode( mpt::common_encoding::utf8, filename ).c_str(), L"rb" ); if ( f ) { while ( !feof( f ) ) { static const std::size_t BUFFER_SIZE = 4096; char buffer[BUFFER_SIZE]; size_t data_read = fread( buffer, 1, BUFFER_SIZE, f ); std::copy( buffer, buffer + data_read, std::back_inserter( data ) ); } fclose( f ); f = NULL; } file_stream.str( data ); filesize = data.length(); #elif defined(_MSC_VER) && defined(UNICODE) file_stream.open( mpt::transcode( mpt::common_encoding::utf8, filename ), std::ios::binary ); file_stream.seekg( 0, std::ios::end ); filesize = file_stream.tellg(); file_stream.seekg( 0, std::ios::beg ); #else file_stream.open( filename, std::ios::binary ); file_stream.seekg( 0, std::ios::end ); filesize = file_stream.tellg(); file_stream.seekg( 0, std::ios::beg ); #endif } std::istream & data_stream = use_stdin ? std::cin : file_stream; if ( data_stream.fail() ) { throw exception( "file open error" ); } { openmpt::module mod( data_stream, silentlog, flags.ctls ); mod.select_subsong( flags.subsong ); silentlog.str( std::string() ); // clear, loader messages get stored to get_metadata( "warnings" ) by libopenmpt internally render_mod_file( flags, filename, filesize, mod, log, audio_stream ); } } catch ( prev_file & ) { throw; } catch ( next_file & ) { throw; } catch ( silent_exit_exception & ) { throw; } catch ( std::exception & e ) { if ( !silentlog.str().empty() ) { log << "errors loading '" << filename << "': " << silentlog.str() << std::endl; } else { log << "errors loading '" << filename << "'" << std::endl; } log << "error playing '" << filename << "': " << e.what() << std::endl; } catch ( ... ) { if ( !silentlog.str().empty() ) { log << "errors loading '" << filename << "': " << silentlog.str() << std::endl; } else { log << "errors loading '" << filename << "'" << std::endl; } log << "unknown error playing '" << filename << "'" << std::endl; } log << std::endl; log.writeout(); } static std::string get_random_filename( std::set & filenames, std::default_random_engine & prng ) { std::size_t index = std::uniform_int_distribution( 0, filenames.size() - 1 )( prng ); std::set::iterator it = filenames.begin(); std::advance( it, index ); return *it; } static void render_files( commandlineflags & flags, textout & log, write_buffers_interface & audio_stream, std::default_random_engine & prng ) { if ( flags.randomize ) { std::shuffle( flags.filenames.begin(), flags.filenames.end(), prng ); } try { while ( true ) { if ( flags.shuffle ) { // TODO: improve prev/next logic std::set shuffle_set; shuffle_set.insert( flags.filenames.begin(), flags.filenames.end() ); while ( true ) { if ( shuffle_set.empty() ) { break; } std::string filename = get_random_filename( shuffle_set, prng ); try { flags.playlist_index = std::find( flags.filenames.begin(), flags.filenames.end(), filename ) - flags.filenames.begin(); render_file( flags, filename, log, audio_stream ); shuffle_set.erase( filename ); continue; } catch ( prev_file & ) { shuffle_set.erase( filename ); continue; } catch ( next_file & ) { shuffle_set.erase( filename ); continue; } catch ( ... ) { throw; } } } else { std::vector::iterator filename = flags.filenames.begin(); while ( true ) { if ( filename == flags.filenames.end() ) { break; } try { flags.playlist_index = filename - flags.filenames.begin(); render_file( flags, *filename, log, audio_stream ); filename++; continue; } catch ( prev_file & e ) { while ( filename != flags.filenames.begin() && e.count ) { e.count--; --filename; } continue; } catch ( next_file & e ) { while ( filename != flags.filenames.end() && e.count ) { e.count--; ++filename; } continue; } catch ( ... ) { throw; } } } if ( !flags.restart ) { break; } } } catch ( ... ) { throw; } } static bool parse_playlist( commandlineflags & flags, std::string filename, std::ostream & log ) { log.flush(); bool is_playlist = false; bool m3u8 = false; if ( ends_with( filename, ".m3u") || ends_with( filename, ".m3U") || ends_with( filename, ".M3u") || ends_with( filename, ".M3U") ) { is_playlist = true; } if ( ends_with( filename, ".m3u8") || ends_with( filename, ".m3U8") || ends_with( filename, ".M3u8") || ends_with( filename, ".M3U8") ) { is_playlist = true; m3u8 = true; } if ( ends_with( filename, ".pls") || ends_with( filename, ".plS") || ends_with( filename, ".pLs") || ends_with( filename, ".pLS") || ends_with( filename, ".Pls") || ends_with( filename, ".PlS") || ends_with( filename, ".PLs") || ends_with( filename, ".PLS") ) { is_playlist = true; } std::string basepath = get_basepath( filename ); try { #if defined(WIN32) && defined(UNICODE) && !defined(_MSC_VER) std::istringstream file_stream; #else std::ifstream file_stream; #endif #if defined(WIN32) && defined(UNICODE) && !defined(_MSC_VER) // Only MSVC has std::ifstream::ifstream(std::wstring). // Fake it for other compilers using _wfopen(). std::string data; FILE * f = _wfopen( mpt::transcode( mpt::common_encoding::utf8, filename ).c_str(), L"rb" ); if ( f ) { while ( !feof( f ) ) { static const std::size_t BUFFER_SIZE = 4096; char buffer[BUFFER_SIZE]; size_t data_read = fread( buffer, 1, BUFFER_SIZE, f ); std::copy( buffer, buffer + data_read, std::back_inserter( data ) ); } fclose( f ); f = NULL; } file_stream.str( data ); #elif defined(_MSC_VER) && defined(UNICODE) file_stream.open( mpt::transcode( mpt::common_encoding::utf8, filename ), std::ios::binary ); #else file_stream.open( filename, std::ios::binary ); #endif std::string line; bool first = true; bool extm3u = false; bool pls = false; while ( std::getline( file_stream, line ) ) { std::string newfile; line = trim_eol( line ); if ( first ) { first = false; if ( line == "#EXTM3U" ) { extm3u = true; continue; } else if ( line == "[playlist]" ) { pls = true; } } if ( line.empty() ) { continue; } if ( pls ) { if ( begins_with( line, "File" ) ) { if ( line.find( "=" ) != std::string::npos ) { flags.filenames.push_back( line.substr( line.find( "=" ) + 1 ) ); } } else if ( begins_with( line, "Title" ) ) { continue; } else if ( begins_with( line, "Length" ) ) { continue; } else if ( begins_with( line, "NumberOfEntries" ) ) { continue; } else if ( begins_with( line, "Version" ) ) { continue; } else { continue; } } else if ( extm3u ) { if ( begins_with( line, "#EXTINF" ) ) { continue; } else if ( begins_with( line, "#" ) ) { continue; } if ( m3u8 ) { newfile = line; } else { #if defined(WIN32) newfile = mpt::transcode( mpt::common_encoding::utf8, mpt::logical_encoding::locale, line ); #else newfile = line; #endif } } else { if ( m3u8 ) { newfile = line; } else { #if defined(WIN32) newfile = mpt::transcode( mpt::common_encoding::utf8, mpt::logical_encoding::locale, line ); #else newfile = line; #endif } } if ( !newfile.empty() ) { if ( !is_absolute( newfile ) ) { newfile = basepath + newfile; } flags.filenames.push_back( newfile ); } } } catch ( std::exception & e ) { log << "error loading '" << filename << "': " << e.what() << std::endl; } catch ( ... ) { log << "unknown error loading '" << filename << "'" << std::endl; } log.flush(); return is_playlist; } static commandlineflags parse_openmpt123( const std::vector & args, std::ostream & log ) { log.flush(); if ( args.size() <= 1 ) { throw args_error_exception(); } commandlineflags flags; bool files_only = false; // cppcheck false-positive // cppcheck-suppress StlMissingComparison for ( auto i = args.begin(); i != args.end(); ++i ) { if ( i == args.begin() ) { // skip program name continue; } std::string arg = *i; std::string nextarg = ( i+1 != args.end() ) ? *(i+1) : ""; if ( files_only ) { flags.filenames.push_back( arg ); } else if ( arg.substr( 0, 1 ) != "-" ) { flags.filenames.push_back( arg ); } else { if ( arg == "--" ) { files_only = true; } else if ( arg == "-h" || arg == "--help" ) { throw show_help_exception(); } else if ( arg == "--help-keyboard" ) { throw show_help_keyboard_exception(); } else if ( arg == "-q" || arg == "--quiet" ) { flags.quiet = true; } else if ( arg == "-v" || arg == "--verbose" ) { flags.verbose = true; } else if ( arg == "--man-version" ) { throw show_man_version_exception(); } else if ( arg == "--man-help" ) { throw show_man_help_exception(); } else if ( arg == "--version" ) { throw show_version_number_exception(); } else if ( arg == "--short-version" ) { throw show_short_version_number_exception(); } else if ( arg == "--long-version" ) { throw show_long_version_number_exception(); } else if ( arg == "--credits" ) { throw show_credits_exception(); } else if ( arg == "--license" ) { throw show_license_exception(); } else if ( arg == "--probe" ) { flags.mode = Mode::Probe; } else if ( arg == "--info" ) { flags.mode = Mode::Info; } else if ( arg == "--ui" ) { flags.mode = Mode::UI; } else if ( arg == "--batch" ) { flags.mode = Mode::Batch; } else if ( arg == "--render" ) { flags.mode = Mode::Render; } else if ( arg == "--terminal-width" && nextarg != "" ) { std::istringstream istr( nextarg ); istr >> flags.terminal_width; ++i; } else if ( arg == "--terminal-height" && nextarg != "" ) { std::istringstream istr( nextarg ); istr >> flags.terminal_height; ++i; } else if ( arg == "--progress" ) { flags.show_progress = true; } else if ( arg == "--no-progress" ) { flags.show_progress = false; } else if ( arg == "--meters" ) { flags.show_meters = true; } else if ( arg == "--no-meters" ) { flags.show_meters = false; } else if ( arg == "--channel-meters" ) { flags.show_channel_meters = true; } else if ( arg == "--no-channel-meters" ) { flags.show_channel_meters = false; } else if ( arg == "--pattern" ) { flags.show_pattern = true; } else if ( arg == "--no-pattern" ) { flags.show_pattern = false; } else if ( arg == "--details" ) { flags.show_details = true; } else if ( arg == "--no-details" ) { flags.show_details = false; } else if ( arg == "--message" ) { flags.show_message = true; } else if ( arg == "--no-message" ) { flags.show_message = false; } else if ( arg == "--driver" && nextarg != "" ) { if ( false ) { // nothing } else if ( nextarg == "help" ) { std::ostringstream drivers; drivers << " Available drivers:" << std::endl; drivers << " " << "default" << std::endl; #if defined( MPT_WITH_PULSEAUDIO ) drivers << " " << "pulseaudio" << std::endl; #endif #if defined( MPT_WITH_SDL2 ) drivers << " " << "sdl2" << std::endl; #endif #if defined( MPT_WITH_PORTAUDIO ) drivers << " " << "portaudio" << std::endl; #endif #if defined( WIN32 ) drivers << " " << "waveout" << std::endl; #endif #if defined( MPT_WITH_ALLEGRO42 ) drivers << " " << "allegro42" << std::endl; #endif throw show_help_exception( drivers.str() ); } else if ( nextarg == "default" ) { flags.driver = ""; } else { flags.driver = nextarg; } ++i; } else if ( arg == "--device" && nextarg != "" ) { if ( false ) { // nothing } else if ( nextarg == "help" ) { std::ostringstream devices; devices << " Available devices:" << std::endl; devices << " " << "default" << ": " << "default" << std::endl; #if defined( MPT_WITH_PULSEAUDIO ) devices << show_pulseaudio_devices( log ); #endif #if defined( MPT_WITH_SDL2 ) devices << show_sdl2_devices( log ); #endif #if defined( MPT_WITH_PORTAUDIO ) devices << show_portaudio_devices( log ); #endif #if defined( WIN32 ) devices << show_waveout_devices( log ); #endif #if defined( MPT_WITH_ALLEGRO42 ) devices << show_allegro42_devices( log ); #endif throw show_help_exception( devices.str() ); } else if ( nextarg == "default" ) { flags.device = ""; } else { flags.device = nextarg; } ++i; } else if ( arg == "--buffer" && nextarg != "" ) { std::istringstream istr( nextarg ); istr >> flags.buffer; ++i; } else if ( arg == "--period" && nextarg != "" ) { std::istringstream istr( nextarg ); istr >> flags.period; ++i; } else if ( arg == "--update" && nextarg != "" ) { std::istringstream istr( nextarg ); istr >> flags.ui_redraw_interval; ++i; } else if ( arg == "--stdout" ) { flags.use_stdout = true; } else if ( ( arg == "-o" || arg == "--output" ) && nextarg != "" ) { flags.output_filename = nextarg; ++i; } else if ( arg == "--force" ) { flags.force_overwrite = true; } else if ( arg == "--output-type" && nextarg != "" ) { flags.output_extension = nextarg; ++i; } else if ( arg == "--samplerate" && nextarg != "" ) { std::istringstream istr( nextarg ); istr >> flags.samplerate; ++i; } else if ( arg == "--channels" && nextarg != "" ) { std::istringstream istr( nextarg ); istr >> flags.channels; ++i; } else if ( arg == "--float" ) { flags.use_float = true; } else if ( arg == "--no-float" ) { flags.use_float = false; } else if ( arg == "--gain" && nextarg != "" ) { std::istringstream istr( nextarg ); double gain = 0.0; istr >> gain; flags.gain = static_cast( gain * 100.0 ); ++i; } else if ( arg == "--stereo" && nextarg != "" ) { std::istringstream istr( nextarg ); istr >> flags.separation; ++i; } else if ( arg == "--filter" && nextarg != "" ) { std::istringstream istr( nextarg ); istr >> flags.filtertaps; ++i; } else if ( arg == "--ramping" && nextarg != "" ) { std::istringstream istr( nextarg ); istr >> flags.ramping; ++i; } else if ( arg == "--tempo" && nextarg != "" ) { std::istringstream istr( nextarg ); double tmp = 1.0; istr >> tmp; flags.tempo = double_to_tempo_flag( tmp ); ++i; } else if ( arg == "--pitch" && nextarg != "" ) { std::istringstream istr( nextarg ); double tmp = 1.0; istr >> tmp; flags.pitch = double_to_pitch_flag( tmp ); ++i; } else if ( arg == "--dither" && nextarg != "" ) { std::istringstream istr( nextarg ); istr >> flags.dither; ++i; } else if ( arg == "--playlist" && nextarg != "" ) { parse_playlist( flags, nextarg, log ); ++i; } else if ( arg == "--randomize" ) { flags.randomize = true; } else if ( arg == "--no-randomize" ) { flags.randomize = false; } else if ( arg == "--shuffle" ) { flags.shuffle = true; } else if ( arg == "--no-shuffle" ) { flags.shuffle = false; } else if ( arg == "--restart" ) { flags.restart = true; } else if ( arg == "--no-restart" ) { flags.restart = false; } else if ( arg == "--subsong" && nextarg != "" ) { std::istringstream istr( nextarg ); istr >> flags.subsong; ++i; } else if ( arg == "--repeat" && nextarg != "" ) { std::istringstream istr( nextarg ); istr >> flags.repeatcount; ++i; } else if ( arg == "--ctl" && nextarg != "" ) { std::istringstream istr( nextarg ); std::string ctl_c_v; istr >> ctl_c_v; if ( ctl_c_v.find( "=" ) == std::string::npos ) { throw args_error_exception(); } std::string ctl = ctl_c_v.substr( 0, ctl_c_v.find( "=" ) ); std::string val = ctl_c_v.substr( ctl_c_v.find( "=" ) + std::string("=").length(), std::string::npos ); if ( ctl.empty() ) { throw args_error_exception(); } flags.ctls[ ctl ] = val; ++i; } else if ( arg == "--seek" && nextarg != "" ) { std::istringstream istr( nextarg ); istr >> flags.seek_target; ++i; } else if ( arg == "--end-time" && nextarg != "" ) { std::istringstream istr( nextarg ); istr >> flags.end_time; ++i; } else if ( arg.size() > 0 && arg.substr( 0, 1 ) == "-" ) { throw args_error_exception(); } } } return flags; } #if defined(WIN32) class FD_utf8_raii { private: FILE * file; int old_mode; public: FD_utf8_raii( FILE * file, bool set_utf8 ) : file(file) , old_mode(-1) { if ( set_utf8 ) { fflush( file ); #if defined(UNICODE) old_mode = _setmode( _fileno( file ), _O_U8TEXT ); #else old_mode = _setmode( _fileno( file ), _O_TEXT ); #endif if ( old_mode == -1 ) { throw exception( "failed to set TEXT mode on file descriptor" ); } } } ~FD_utf8_raii() { if ( old_mode != -1 ) { fflush( file ); old_mode = _setmode( _fileno( file ), old_mode ); } } }; class FD_binary_raii { private: FILE * file; int old_mode; public: FD_binary_raii( FILE * file, bool set_binary ) : file(file) , old_mode(-1) { if ( set_binary ) { fflush( file ); old_mode = _setmode( _fileno( file ), _O_BINARY ); if ( old_mode == -1 ) { throw exception( "failed to set binary mode on file descriptor" ); } } } ~FD_binary_raii() { if ( old_mode != -1 ) { fflush( file ); old_mode = _setmode( _fileno( file ), old_mode ); } } }; #endif #if defined(WIN32) && defined(UNICODE) static int wmain( int wargc, wchar_t * wargv [] ) { #else static int main( int argc, char * argv [] ) { #endif std::vector args; #if defined(WIN32) && defined(UNICODE) for ( int arg = 0; arg < wargc; ++arg ) { args.push_back( mpt::transcode( mpt::common_encoding::utf8, wargv[arg] ) ); } #else args = std::vector( argv, argv + argc ); #endif #if defined(WIN32) FD_utf8_raii stdin_utf8_guard( stdin, true ); FD_utf8_raii stdout_utf8_guard( stdout, true ); FD_utf8_raii stderr_utf8_guard( stderr, true ); #endif textout_dummy dummy_log; #if defined(WIN32) #if defined(UNICODE) textout_ostream_console std_out( std::wcout, STD_OUTPUT_HANDLE ); textout_ostream_console std_err( std::wclog, STD_ERROR_HANDLE ); #else textout_ostream_console std_out( std::cout, STD_OUTPUT_HANDLE ); textout_ostream_console std_err( std::clog, STD_ERROR_HANDLE ); #endif #else textout_ostream std_out( std::cout ); textout_ostream std_err( std::clog ); #endif commandlineflags flags; try { flags = parse_openmpt123( args, std::cerr ); flags.check_and_sanitize(); } catch ( args_error_exception & ) { show_help( std_out ); return 1; } catch ( show_man_help_exception & ) { show_help( std_out, false, true, true ); return 0; } catch ( show_man_version_exception & ) { show_man_version( std_out ); return 0; } catch ( show_help_exception & e ) { show_help( std_out, true, e.longhelp, false, e.message ); if ( flags.verbose ) { show_credits( std_out ); } return 0; } catch ( show_help_keyboard_exception & ) { show_help_keyboard( std_out ); return 0; } catch ( show_long_version_number_exception & ) { show_long_version( std_out ); return 0; } catch ( show_version_number_exception & ) { show_version( std_out ); return 0; } catch ( show_short_version_number_exception & ) { show_short_version( std_out ); return 0; } catch ( show_credits_exception & ) { show_credits( std_out ); return 0; } catch ( show_license_exception & ) { show_license( std_out ); return 0; } catch ( silent_exit_exception & ) { return 0; } catch ( std::exception & e ) { std_err << "error: " << e.what() << std::endl; std_err.writeout(); return 1; } catch ( ... ) { std_err << "unknown error" << std::endl; std_err.writeout(); return 1; } try { bool stdin_can_ui = true; for ( const auto & filename : flags.filenames ) { if ( filename == "-" ) { stdin_can_ui = false; break; } } bool stdout_can_ui = true; if ( flags.use_stdout ) { stdout_can_ui = false; } // set stdin binary #if defined(WIN32) FD_binary_raii stdin_guard( stdin, !stdin_can_ui ); #endif // set stdout binary #if defined(WIN32) FD_binary_raii stdout_guard( stdout, !stdout_can_ui ); #endif // setup terminal #if !defined(WIN32) if ( stdin_can_ui ) { if ( flags.mode == Mode::UI ) { set_input_mode(); } } #endif textout & log = flags.quiet ? static_cast( dummy_log ) : static_cast( stdout_can_ui ? std_out : std_err ); show_info( log, flags.verbose ); if ( !flags.warnings.empty() ) { log << flags.warnings << std::endl; } if ( flags.verbose ) { log << flags; } log.writeout(); std::default_random_engine prng; try { std::random_device rd; std::seed_seq seq{ rd(), static_cast( std::time( NULL ) ) }; prng = std::default_random_engine{ seq }; } catch ( const std::exception & ) { std::seed_seq seq{ static_cast( std::time( NULL ) ) }; prng = std::default_random_engine{ seq }; } std::srand( std::uniform_int_distribution()( prng ) ); switch ( flags.mode ) { case Mode::Probe: { for ( const auto & filename : flags.filenames ) { probe_file( flags, filename, log ); flags.playlist_index++; } } break; case Mode::Info: { void_audio_stream dummy; render_files( flags, log, dummy, prng ); } break; case Mode::UI: case Mode::Batch: { if ( flags.use_stdout ) { flags.apply_default_buffer_sizes(); stdout_stream_raii stdout_audio_stream; render_files( flags, log, stdout_audio_stream, prng ); } else if ( !flags.output_filename.empty() ) { flags.apply_default_buffer_sizes(); file_audio_stream_raii file_audio_stream( flags, flags.output_filename, log ); render_files( flags, log, file_audio_stream, prng ); #if defined( MPT_WITH_PULSEAUDIO ) } else if ( flags.driver == "pulseaudio" || flags.driver.empty() ) { pulseaudio_stream_raii pulseaudio_stream( flags, log ); render_files( flags, log, pulseaudio_stream, prng ); #endif #if defined( MPT_WITH_SDL2 ) } else if ( flags.driver == "sdl2" || flags.driver.empty() ) { sdl2_stream_raii sdl2_stream( flags, log ); render_files( flags, log, sdl2_stream, prng ); #endif #if defined( MPT_WITH_PORTAUDIO ) } else if ( flags.driver == "portaudio" || flags.driver.empty() ) { portaudio_stream_raii portaudio_stream( flags, log ); render_files( flags, log, portaudio_stream, prng ); #endif #if defined( WIN32 ) } else if ( flags.driver == "waveout" || flags.driver.empty() ) { waveout_stream_raii waveout_stream( flags ); render_files( flags, log, waveout_stream, prng ); #endif #if defined( MPT_WITH_ALLEGRO42 ) } else if ( flags.driver == "allegro42" || flags.driver.empty() ) { allegro42_stream_raii allegro42_stream( flags, log ); render_files( flags, log, allegro42_stream, prng ); #endif } else { if ( flags.driver.empty() ) { throw exception( "openmpt123 is compiled without any audio driver" ); } else { throw exception( "audio driver '" + flags.driver + "' not found" ); } } } break; case Mode::Render: { for ( const auto & filename : flags.filenames ) { flags.apply_default_buffer_sizes(); file_audio_stream_raii file_audio_stream( flags, filename + std::string(".") + flags.output_extension, log ); render_file( flags, filename, log, file_audio_stream ); flags.playlist_index++; } } break; case Mode::None: break; } } catch ( args_error_exception & ) { show_help( std_out ); return 1; #ifdef MPT_WITH_ALLEGRO42 } catch ( allegro42_exception & e ) { std_err << "Allegro-4.2 error: " << e.what() << std::endl; std_err.writeout(); return 1; #endif #ifdef MPT_WITH_PULSEAUDIO } catch ( pulseaudio_exception & e ) { std_err << "PulseAudio error: " << e.what() << std::endl; std_err.writeout(); return 1; #endif #ifdef MPT_WITH_PORTAUDIO } catch ( portaudio_exception & e ) { std_err << "PortAudio error: " << e.what() << std::endl; std_err.writeout(); return 1; #endif #ifdef MPT_WITH_SDL2 } catch ( sdl2_exception & e ) { std_err << "SDL2 error: " << e.what() << std::endl; std_err.writeout(); return 1; #endif } catch ( silent_exit_exception & ) { return 0; } catch ( std::exception & e ) { std_err << "error: " << e.what() << std::endl; std_err.writeout(); return 1; } catch ( ... ) { std_err << "unknown error" << std::endl; std_err.writeout(); return 1; } return 0; } } // namespace openmpt123 #if defined(WIN32) && defined(UNICODE) #if defined(__GNUC__) // mingw64 does only default to special C linkage for "main", but not for "wmain". extern "C" int wmain( int wargc, wchar_t * wargv [] ); extern "C" #endif int wmain( int wargc, wchar_t * wargv [] ) { return openmpt123::wmain( wargc, wargv ); } #else int main( int argc, char * argv [] ) { return openmpt123::main( argc, argv ); } #endif libopenmpt-0.6.1+release.autotools/openmpt123/openmpt123_flac.hpp0000644000175000017500000001266313452606270021607 00000000000000/* * openmpt123_flac.hpp * ------------------- * Purpose: libopenmpt command line player * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #ifndef OPENMPT123_FLAC_HPP #define OPENMPT123_FLAC_HPP #include "openmpt123_config.hpp" #include "openmpt123.hpp" #if defined(MPT_WITH_FLAC) #if defined(_MSC_VER) && defined(__clang__) && defined(__c2__) #include #if __STDC__ typedef _off_t off_t; #endif #endif #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wreserved-id-macro" #endif #include #include #include #if defined(__clang__) #pragma clang diagnostic pop #endif namespace openmpt123 { class flac_stream_raii : public file_audio_stream_base { private: commandlineflags flags; std::string filename; bool called_init; std::vector< std::pair< std::string, std::string > > tags; FLAC__StreamMetadata * flac_metadata[1]; FLAC__StreamEncoder * encoder; std::vector interleaved_buffer; void add_vorbiscomment_field( FLAC__StreamMetadata * vorbiscomment, const std::string & field, const std::string & value ) { if ( !value.empty() ) { FLAC__StreamMetadata_VorbisComment_Entry entry; FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair( &entry, field.c_str(), value.c_str() ); FLAC__metadata_object_vorbiscomment_append_comment( vorbiscomment, entry, false ); } } public: flac_stream_raii( const std::string & filename_, const commandlineflags & flags_, std::ostream & /*log*/ ) : flags(flags_), filename(filename_), called_init(false), encoder(0) { flac_metadata[0] = 0; encoder = FLAC__stream_encoder_new(); if ( !encoder ) { throw exception( "error creating flac encoder" ); } FLAC__stream_encoder_set_channels( encoder, flags.channels ); FLAC__stream_encoder_set_bits_per_sample( encoder, flags.use_float ? 24 : 16 ); FLAC__stream_encoder_set_sample_rate( encoder, flags.samplerate ); FLAC__stream_encoder_set_compression_level( encoder, 8 ); } ~flac_stream_raii() { if ( encoder ) { FLAC__stream_encoder_finish( encoder ); FLAC__stream_encoder_delete( encoder ); encoder = 0; } if ( flac_metadata[0] ) { FLAC__metadata_object_delete( flac_metadata[0] ); flac_metadata[0] = 0; } } void write_metadata( std::map metadata ) override { if ( called_init ) { return; } tags.clear(); tags.push_back( std::make_pair( "TITLE", metadata[ "title" ] ) ); tags.push_back( std::make_pair( "ARTIST", metadata[ "artist" ] ) ); tags.push_back( std::make_pair( "DATE", metadata[ "date" ] ) ); tags.push_back( std::make_pair( "COMMENT", metadata[ "message" ] ) ); if ( !metadata[ "type" ].empty() && !metadata[ "tracker" ].empty() ) { tags.push_back( std::make_pair( "SOURCEMEDIA", std::string() + "'" + metadata[ "type" ] + "' tracked music file, made with '" + metadata[ "tracker" ] + "', rendered with '" + get_encoder_tag() + "'" ) ); } else if ( !metadata[ "type_long" ].empty() ) { tags.push_back( std::make_pair( "SOURCEMEDIA", std::string() + "'" + metadata[ "type" ] + "' tracked music file, rendered with '" + get_encoder_tag() + "'" ) ); } else if ( !metadata[ "tracker" ].empty() ) { tags.push_back( std::make_pair( "SOURCEMEDIA", std::string() + "tracked music file, made with '" + metadata[ "tracker" ] + "', rendered with '" + get_encoder_tag() + "'" ) ); } else { tags.push_back( std::make_pair( "SOURCEMEDIA", std::string() + "tracked music file, rendered with '" + get_encoder_tag() + "'" ) ); } tags.push_back( std::make_pair( "ENCODER", get_encoder_tag() ) ); flac_metadata[0] = FLAC__metadata_object_new( FLAC__METADATA_TYPE_VORBIS_COMMENT ); for ( std::vector< std::pair< std::string, std::string > >::iterator tag = tags.begin(); tag != tags.end(); ++tag ) { add_vorbiscomment_field( flac_metadata[0], tag->first, tag->second ); } FLAC__stream_encoder_set_metadata( encoder, flac_metadata, 1 ); } void write( const std::vector buffers, std::size_t frames ) override { if ( !called_init ) { FLAC__stream_encoder_init_file( encoder, filename.c_str(), NULL, 0 ); called_init = true; } interleaved_buffer.clear(); for ( std::size_t frame = 0; frame < frames; frame++ ) { for ( std::size_t channel = 0; channel < buffers.size(); channel++ ) { float in = buffers[channel][frame]; if ( in <= -1.0f ) { in = -1.0f; } else if ( in >= 1.0f ) { in = 1.0f; } FLAC__int32 out = mpt_lround( in * (1<<23) ); out = std::max( 0 - (1<<23), out ); out = std::min( out, 0 + (1<<23) - 1 ); interleaved_buffer.push_back( out ); } } FLAC__stream_encoder_process_interleaved( encoder, interleaved_buffer.data(), static_cast( frames ) ); } void write( const std::vector buffers, std::size_t frames ) override { if ( !called_init ) { FLAC__stream_encoder_init_file( encoder, filename.c_str(), NULL, 0 ); called_init = true; } interleaved_buffer.clear(); for ( std::size_t frame = 0; frame < frames; frame++ ) { for ( std::size_t channel = 0; channel < buffers.size(); channel++ ) { interleaved_buffer.push_back( buffers[channel][frame] ); } } FLAC__stream_encoder_process_interleaved( encoder, interleaved_buffer.data(), static_cast( frames ) ); } }; } // namespace openmpt123 #endif // MPT_WITH_FLAC #endif // OPENMPT123_FLAC_HPP libopenmpt-0.6.1+release.autotools/openmpt123/openmpt123.hpp0000644000175000017500000004323714107230757020624 00000000000000/* * openmpt123.hpp * -------------- * Purpose: libopenmpt command line player * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #ifndef OPENMPT123_HPP #define OPENMPT123_HPP #include "openmpt123_config.hpp" #include "mpt/base/compiletime_warning.hpp" #include "mpt/base/floatingpoint.hpp" #include "mpt/base/preprocessor.hpp" #include "mpt/string_transcode/transcode.hpp" #include namespace openmpt123 { struct exception : public openmpt::exception { exception( const std::string & text ) : openmpt::exception(text) { } }; struct show_help_exception { std::string message; bool longhelp; show_help_exception( const std::string & msg = "", bool longhelp_ = true ) : message(msg), longhelp(longhelp_) { } }; struct args_error_exception { args_error_exception() { } }; struct show_help_keyboard_exception { }; #if defined(WIN32) bool IsConsole( DWORD stdHandle ); #endif bool IsTerminal( int fd ); struct field { std::string key; std::string val; field( const std::string & key ) : key(key) { return; } }; class textout : public std::ostringstream { public: textout() { return; } virtual ~textout() { return; } protected: std::string pop() { std::string text = str(); str(std::string()); return text; } public: virtual void writeout() = 0; virtual void cursor_up( std::size_t lines ) { static_cast( lines ); } }; class textout_dummy : public textout { public: textout_dummy() { return; } virtual ~textout_dummy() { return; } public: void writeout() override { static_cast( pop() ); } }; class textout_ostream : public textout { private: std::ostream & s; #if defined(__DJGPP__) mpt::common_encoding codepage; #endif public: textout_ostream( std::ostream & s_ ) : s(s_) #if defined(__DJGPP__) , codepage(mpt::common_encoding::cp437) #endif { #if defined(__DJGPP__) codepage = mpt::djgpp_get_locale_encoding(); #endif return; } virtual ~textout_ostream() { writeout_impl(); } private: void writeout_impl() { std::string text = pop(); if ( text.length() > 0 ) { #if defined(__DJGPP__) s << mpt::transcode( codepage, mpt::common_encoding::utf8, text ); #elif defined(__EMSCRIPTEN__) s << text; #else s << mpt::transcode( mpt::logical_encoding::locale, mpt::common_encoding::utf8, text ); #endif s.flush(); } } public: void writeout() override { writeout_impl(); } void cursor_up( std::size_t lines ) override { s.flush(); for ( std::size_t line = 0; line < lines; ++line ) { *this << "\x1b[1A"; } } }; #if defined(WIN32) class textout_ostream_console : public textout { private: #if defined(UNICODE) std::wostream & s; #else std::ostream & s; #endif HANDLE handle; bool console; public: #if defined(UNICODE) textout_ostream_console( std::wostream & s_, DWORD stdHandle_ ) #else textout_ostream_console( std::ostream & s_, DWORD stdHandle_ ) #endif : s(s_) , handle(GetStdHandle( stdHandle_ )) , console(IsConsole( stdHandle_ )) { return; } virtual ~textout_ostream_console() { writeout_impl(); } private: void writeout_impl() { std::string text = pop(); if ( text.length() > 0 ) { if ( console ) { #if defined(UNICODE) std::wstring wtext = mpt::transcode( mpt::common_encoding::utf8, text ); WriteConsole( handle, wtext.data(), static_cast( wtext.size() ), NULL, NULL ); #else std::string ltext = mpt::transcode( mpt::logical_encoding::locale, mpt::common_encoding::utf8, text ); WriteConsole( handle, ltext.data(), static_cast( ltext.size() ), NULL, NULL ); #endif } else { #if defined(UNICODE) s << mpt::transcode( mpt::common_encoding::utf8, text ); #else s << mpt::transcode( mpt::logical_encoding::locale, mpt::common_encoding::utf8, text ); #endif s.flush(); } } } public: void writeout() override { writeout_impl(); } void cursor_up( std::size_t lines ) override { if ( console ) { s.flush(); CONSOLE_SCREEN_BUFFER_INFO csbi; ZeroMemory( &csbi, sizeof( CONSOLE_SCREEN_BUFFER_INFO ) ); COORD coord_cursor = COORD(); if ( GetConsoleScreenBufferInfo( handle, &csbi ) != FALSE ) { coord_cursor = csbi.dwCursorPosition; coord_cursor.X = 1; coord_cursor.Y -= static_cast( lines ); SetConsoleCursorPosition( handle, coord_cursor ); } } } }; #endif // WIN32 static inline float mpt_round( float val ) { if ( val >= 0.0f ) { return std::floor( val + 0.5f ); } else { return std::ceil( val - 0.5f ); } } static inline long mpt_lround( float val ) { return static_cast< long >( mpt_round( val ) ); } static inline std::string append_software_tag( std::string software ) { std::string openmpt123 = std::string() + "openmpt123 " + OPENMPT123_VERSION_STRING + " (libopenmpt " + openmpt::string::get( "library_version" ) + ", OpenMPT " + openmpt::string::get( "core_version" ) + ")"; if ( software.empty() ) { software = openmpt123; } else { software += " (via " + openmpt123 + ")"; } return software; } static inline std::string get_encoder_tag() { return std::string() + "openmpt123 " + OPENMPT123_VERSION_STRING + " (libopenmpt " + openmpt::string::get( "library_version" ) + ", OpenMPT " + openmpt::string::get( "core_version" ) + ")"; } static inline std::string get_extension( std::string filename ) { if ( filename.find_last_of( "." ) != std::string::npos ) { return filename.substr( filename.find_last_of( "." ) + 1 ); } return ""; } enum class Mode { None, Probe, Info, UI, Batch, Render }; static inline std::string mode_to_string( Mode mode ) { switch ( mode ) { case Mode::None: return "none"; break; case Mode::Probe: return "probe"; break; case Mode::Info: return "info"; break; case Mode::UI: return "ui"; break; case Mode::Batch: return "batch"; break; case Mode::Render: return "render"; break; } return ""; } static const std::int32_t default_low = -2; static const std::int32_t default_high = -1; struct commandlineflags { Mode mode; bool canUI; std::int32_t ui_redraw_interval; bool canProgress; std::string driver; std::string device; std::int32_t buffer; std::int32_t period; std::int32_t samplerate; std::int32_t channels; std::int32_t gain; std::int32_t separation; std::int32_t filtertaps; std::int32_t ramping; // ramping strength : -1:default 0:off 1 2 3 4 5 // roughly milliseconds std::int32_t tempo; std::int32_t pitch; std::int32_t dither; std::int32_t repeatcount; std::int32_t subsong; std::map ctls; double seek_target; double end_time; bool quiet; bool verbose; int terminal_width; int terminal_height; bool show_details; bool show_message; bool show_ui; bool show_progress; bool show_meters; bool show_channel_meters; bool show_pattern; bool use_float; bool use_stdout; bool randomize; bool shuffle; bool restart; std::size_t playlist_index; std::vector filenames; std::string output_filename; std::string output_extension; bool force_overwrite; bool paused; std::string warnings; void apply_default_buffer_sizes() { if ( ui_redraw_interval == default_high ) { ui_redraw_interval = 50; } else if ( ui_redraw_interval == default_low ) { ui_redraw_interval = 10; } if ( buffer == default_high ) { buffer = 250; } else if ( buffer == default_low ) { buffer = 50; } if ( period == default_high ) { period = 50; } else if ( period == default_low ) { period = 10; } } commandlineflags() { mode = Mode::UI; ui_redraw_interval = default_high; driver = ""; device = ""; buffer = default_high; period = default_high; #if defined(__DJGPP__) samplerate = 44100; channels = 2; use_float = false; #else samplerate = 48000; channels = 2; use_float = mpt::float_traits::is_hard && mpt::float_traits::is_ieee754_binary; #endif gain = 0; separation = 100; filtertaps = 8; ramping = -1; tempo = 0; pitch = 0; dither = 1; repeatcount = 0; subsong = -1; seek_target = 0.0; end_time = 0.0; quiet = false; verbose = false; #if defined(__DJGPP__) terminal_width = 80; terminal_height = 25; #else terminal_width = 72; terminal_height = 23; #endif #if defined(WIN32) terminal_width = 72; terminal_height = 23; HANDLE hStdOutput = GetStdHandle( STD_OUTPUT_HANDLE ); if ( ( hStdOutput != NULL ) && ( hStdOutput != INVALID_HANDLE_VALUE ) ) { CONSOLE_SCREEN_BUFFER_INFO csbi; ZeroMemory( &csbi, sizeof( CONSOLE_SCREEN_BUFFER_INFO ) ); if ( GetConsoleScreenBufferInfo( hStdOutput, &csbi ) != FALSE ) { terminal_width = std::min( static_cast( 1 + csbi.srWindow.Right - csbi.srWindow.Left ), static_cast( csbi.dwSize.X ) ); terminal_height = std::min( static_cast( 1 + csbi.srWindow.Bottom - csbi.srWindow.Top ), static_cast( csbi.dwSize.Y ) ); } } #else // WIN32 if ( isatty( STDERR_FILENO ) ) { if ( std::getenv( "COLUMNS" ) ) { std::istringstream istr( std::getenv( "COLUMNS" ) ); int tmp = 0; istr >> tmp; if ( tmp > 0 ) { terminal_width = tmp; } } if ( std::getenv( "ROWS" ) ) { std::istringstream istr( std::getenv( "ROWS" ) ); int tmp = 0; istr >> tmp; if ( tmp > 0 ) { terminal_height = tmp; } } #if defined(TIOCGWINSZ) struct winsize ts; if ( ioctl( STDERR_FILENO, TIOCGWINSZ, &ts ) >= 0 ) { terminal_width = ts.ws_col; terminal_height = ts.ws_row; } #elif defined(TIOCGSIZE) struct ttysize ts; if ( ioctl( STDERR_FILENO, TIOCGSIZE, &ts ) >= 0 ) { terminal_width = ts.ts_cols; terminal_height = ts.ts_rows; } #endif } #endif show_details = true; show_message = false; #if defined(WIN32) canUI = IsTerminal( 0 ) ? true : false; canProgress = IsTerminal( 2 ) ? true : false; #else // !WIN32 canUI = isatty( STDIN_FILENO ) ? true : false; canProgress = isatty( STDERR_FILENO ) ? true : false; #endif // WIN32 show_ui = canUI; show_progress = canProgress; show_meters = canUI && canProgress; show_channel_meters = false; show_pattern = false; use_stdout = false; randomize = false; shuffle = false; restart = false; playlist_index = 0; output_extension = "auto"; force_overwrite = false; paused = false; } void check_and_sanitize() { if ( filenames.size() == 0 ) { throw args_error_exception(); } if ( use_stdout && ( device != commandlineflags().device || !output_filename.empty() ) ) { throw args_error_exception(); } if ( !output_filename.empty() && ( device != commandlineflags().device || use_stdout ) ) { throw args_error_exception(); } for ( const auto & filename : filenames ) { if ( filename == "-" ) { canUI = false; } } show_ui = canUI; if ( mode == Mode::None ) { if ( canUI ) { mode = Mode::UI; } else { mode = Mode::Batch; } } if ( mode == Mode::UI && !canUI ) { throw args_error_exception(); } if ( show_progress && !canProgress ) { throw args_error_exception(); } switch ( mode ) { case Mode::None: throw args_error_exception(); break; case Mode::Probe: show_ui = false; show_progress = false; show_meters = false; show_channel_meters = false; show_pattern = false; break; case Mode::Info: show_ui = false; show_progress = false; show_meters = false; show_channel_meters = false; show_pattern = false; break; case Mode::UI: break; case Mode::Batch: show_meters = false; show_channel_meters = false; show_pattern = false; break; case Mode::Render: show_meters = false; show_channel_meters = false; show_pattern = false; show_ui = false; break; } if ( quiet ) { verbose = false; show_ui = false; show_details = false; show_progress = false; show_channel_meters = false; } if ( verbose ) { show_details = true; } if ( channels != 1 && channels != 2 && channels != 4 ) { channels = commandlineflags().channels; } if ( samplerate < 0 ) { samplerate = commandlineflags().samplerate; } if ( output_extension == "auto" ) { output_extension = ""; } if ( mode != Mode::Render && !output_extension.empty() ) { throw args_error_exception(); } if ( mode == Mode::Render && !output_filename.empty() ) { throw args_error_exception(); } if ( mode != Mode::Render && !output_filename.empty() ) { output_extension = get_extension( output_filename ); } if ( output_extension.empty() ) { output_extension = "wav"; } } }; template < typename Tsample > Tsample convert_sample_to( float val ); template < > float convert_sample_to( float val ) { return val; } template < > std::int16_t convert_sample_to( float val ) { std::int32_t tmp = static_cast( val * 32768.0f ); tmp = std::min( tmp, std::int32_t( 32767 ) ); tmp = std::max( tmp, std::int32_t( -32768 ) ); return static_cast( tmp ); } class write_buffers_interface { protected: virtual ~write_buffers_interface() { return; } public: virtual void write_metadata( std::map metadata ) { (void)metadata; return; } virtual void write_updated_metadata( std::map metadata ) { (void)metadata; return; } virtual void write( const std::vector buffers, std::size_t frames ) = 0; virtual void write( const std::vector buffers, std::size_t frames ) = 0; virtual bool pause() { return false; } virtual bool unpause() { return false; } virtual bool sleep( int /*ms*/ ) { return false; } virtual bool is_dummy() const { return false; } }; class write_buffers_polling_wrapper : public write_buffers_interface { protected: std::size_t channels; std::size_t sampleQueueMaxFrames; std::deque sampleQueue; protected: virtual ~write_buffers_polling_wrapper() { return; } protected: write_buffers_polling_wrapper( const commandlineflags & flags ) : channels(flags.channels) , sampleQueueMaxFrames(0) { return; } void set_queue_size_frames( std::size_t frames ) { sampleQueueMaxFrames = frames; } template < typename Tsample > Tsample pop_queue() { float val = 0.0f; if ( !sampleQueue.empty() ) { val = sampleQueue.front(); sampleQueue.pop_front(); } return convert_sample_to( val ); } public: void write( const std::vector buffers, std::size_t frames ) override { for ( std::size_t frame = 0; frame < frames; ++frame ) { for ( std::size_t channel = 0; channel < channels; ++channel ) { sampleQueue.push_back( buffers[channel][frame] ); } while ( sampleQueue.size() >= sampleQueueMaxFrames * channels ) { while ( !forward_queue() ) { sleep( 1 ); } } } } void write( const std::vector buffers, std::size_t frames ) override { for ( std::size_t frame = 0; frame < frames; ++frame ) { for ( std::size_t channel = 0; channel < channels; ++channel ) { sampleQueue.push_back( buffers[channel][frame] * (1.0f/32768.0f) ); } while ( sampleQueue.size() >= sampleQueueMaxFrames * channels ) { while ( !forward_queue() ) { sleep( 1 ); } } } } virtual bool forward_queue() = 0; bool sleep( int ms ) override = 0; }; class write_buffers_polling_wrapper_int : public write_buffers_interface { protected: std::size_t channels; std::size_t sampleQueueMaxFrames; std::deque sampleQueue; protected: virtual ~write_buffers_polling_wrapper_int() { return; } protected: write_buffers_polling_wrapper_int( const commandlineflags & flags ) : channels(flags.channels) , sampleQueueMaxFrames(0) { return; } void set_queue_size_frames( std::size_t frames ) { sampleQueueMaxFrames = frames; } std::int16_t pop_queue() { std::int16_t val = 0; if ( !sampleQueue.empty() ) { val = sampleQueue.front(); sampleQueue.pop_front(); } return val; } public: void write( const std::vector buffers, std::size_t frames ) override { for ( std::size_t frame = 0; frame < frames; ++frame ) { for ( std::size_t channel = 0; channel < channels; ++channel ) { sampleQueue.push_back( convert_sample_to( buffers[channel][frame] ) ); } while ( sampleQueue.size() >= sampleQueueMaxFrames * channels ) { while ( !forward_queue() ) { sleep( 1 ); } } } } void write( const std::vector buffers, std::size_t frames ) override { for ( std::size_t frame = 0; frame < frames; ++frame ) { for ( std::size_t channel = 0; channel < channels; ++channel ) { sampleQueue.push_back( buffers[channel][frame] ); } while ( sampleQueue.size() >= sampleQueueMaxFrames * channels ) { while ( !forward_queue() ) { sleep( 1 ); } } } } virtual bool forward_queue() = 0; bool sleep( int ms ) override = 0; }; class void_audio_stream : public write_buffers_interface { public: virtual ~void_audio_stream() { return; } public: void write( const std::vector buffers, std::size_t frames ) override { (void)buffers; (void)frames; } void write( const std::vector buffers, std::size_t frames ) override { (void)buffers; (void)frames; } bool is_dummy() const override { return true; } }; class file_audio_stream_base : public write_buffers_interface { protected: file_audio_stream_base() { return; } public: void write_metadata( std::map metadata ) override { (void)metadata; return; } void write_updated_metadata( std::map metadata ) override { (void)metadata; return; } void write( const std::vector buffers, std::size_t frames ) override = 0; void write( const std::vector buffers, std::size_t frames ) override = 0; virtual ~file_audio_stream_base() { return; } }; } // namespace openmpt123 #endif // OPENMPT123_HPP libopenmpt-0.6.1+release.autotools/openmpt123/openmpt123_mmio.hpp0000644000175000017500000001025614107230757021640 00000000000000/* * openmpt123_mmio.hpp * ------------------- * Purpose: libopenmpt command line player * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #ifndef OPENMPT123_MMIO_HPP #define OPENMPT123_MMIO_HPP #include "openmpt123_config.hpp" #include "openmpt123.hpp" #if defined(MPT_WITH_MMIO) namespace openmpt123 { class mmio_stream_raii : public file_audio_stream_base { private: std::ostream & log; commandlineflags flags; WAVEFORMATEX waveformatex; HMMIO mmio; MMCKINFO WAVE_chunk; MMCKINFO fmt__chunk; MMCKINFO data_chunk; MMIOINFO data_info; private: void CHECKED( HRESULT err ) { if ( err != 0 ) { throw exception( "error writing wave file" ); } } void UNCHECKED( HRESULT err ) { if ( err != 0 ) { log << "error writing wave file" << std::endl; } } public: mmio_stream_raii( const std::string & filename, const commandlineflags & flags_, std::ostream & log_ ) : log(log_), flags(flags_), mmio(NULL) { ZeroMemory( &waveformatex, sizeof( WAVEFORMATEX ) ); waveformatex.cbSize = 0; waveformatex.wFormatTag = flags.use_float ? WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM; waveformatex.nChannels = static_cast( flags.channels ); waveformatex.nSamplesPerSec = flags.samplerate; waveformatex.wBitsPerSample = flags.use_float ? 32 : 16; waveformatex.nBlockAlign = static_cast( flags.channels * ( waveformatex.wBitsPerSample / 8 ) ); waveformatex.nAvgBytesPerSec = waveformatex.nSamplesPerSec * waveformatex.nBlockAlign; #if defined(WIN32) && defined(UNICODE) wchar_t * tmp = _wcsdup( mpt::transcode( mpt::common_encoding::utf8, filename ).c_str() ); mmio = mmioOpen( tmp, NULL, MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_CREATE ); free( tmp ); tmp = 0; #else char * tmp = strdup( filename.c_str() ); mmio = mmioOpen( tmp, NULL, MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_CREATE ); free( tmp ); tmp = 0; #endif ZeroMemory( &WAVE_chunk, sizeof( MMCKINFO ) ); WAVE_chunk.fccType = mmioFOURCC('W', 'A', 'V', 'E'); CHECKED(mmioCreateChunk( mmio, &WAVE_chunk, MMIO_CREATERIFF )); ZeroMemory( &fmt__chunk, sizeof( MMCKINFO ) ); fmt__chunk.ckid = mmioFOURCC('f', 'm', 't', ' '); fmt__chunk.cksize = sizeof( WAVEFORMATEX ); CHECKED(mmioCreateChunk( mmio, &fmt__chunk, 0 )); mmioWrite( mmio, (const char*)&waveformatex, sizeof( WAVEFORMATEX ) ); CHECKED(mmioAscend( mmio, &fmt__chunk, 0 )); ZeroMemory( &data_chunk, sizeof( MMCKINFO ) ); data_chunk.ckid = mmioFOURCC('d', 'a', 't', 'a'); data_chunk.cksize = 0; CHECKED(mmioCreateChunk( mmio, &data_chunk, 0 )); ZeroMemory( &data_info, sizeof( MMIOINFO ) ); CHECKED(mmioGetInfo( mmio, &data_info, 0 )); } void write( const std::vector buffers, std::size_t frames ) override { for ( std::size_t frame = 0; frame < frames; frame++ ) { for ( std::size_t channel = 0; channel < buffers.size(); channel++ ) { if ( data_info.pchEndWrite - data_info.pchNext < static_cast( sizeof( float ) ) ) { data_info.dwFlags |= MMIO_DIRTY; CHECKED(mmioAdvance( mmio, &data_info, MMIO_WRITE )); } std::memcpy( data_info.pchNext, &( buffers[channel][frame] ), sizeof( float ) ); data_info.pchNext += sizeof( float ); } } } void write( const std::vector buffers, std::size_t frames ) override { for ( std::size_t frame = 0; frame < frames; frame++ ) { for ( std::size_t channel = 0; channel < buffers.size(); channel++ ) { if ( data_info.pchEndWrite - data_info.pchNext < static_cast( sizeof( std::int16_t ) ) ) { data_info.dwFlags |= MMIO_DIRTY; CHECKED(mmioAdvance( mmio, &data_info, MMIO_WRITE )); } std::memcpy( data_info.pchNext, &( buffers[channel][frame] ), sizeof( std::int16_t ) ); data_info.pchNext += sizeof( std::int16_t ); } } } ~mmio_stream_raii() { data_info.dwFlags |= MMIO_DIRTY; UNCHECKED(mmioSetInfo( mmio, &data_info, 0 )); UNCHECKED(mmioAscend( mmio, &data_chunk, 0 )); UNCHECKED(mmioAscend( mmio, &WAVE_chunk, 0 )); UNCHECKED(mmioClose( mmio, 0 )); mmio = NULL; } }; } // namespace openmpt123 #endif // MPT_WITH_MMIO #endif // OPENMPT123_MMIO_HPP libopenmpt-0.6.1+release.autotools/openmpt123/openmpt123_portaudio.hpp0000644000175000017500000002350013652054136022700 00000000000000/* * openmpt123_portaudio.hpp * ------------------------ * Purpose: libopenmpt command line player * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #ifndef OPENMPT123_PORTAUDIO_HPP #define OPENMPT123_PORTAUDIO_HPP #include "openmpt123_config.hpp" #include "openmpt123.hpp" #if defined(MPT_WITH_PORTAUDIO) #include namespace openmpt123 { struct portaudio_exception : public exception { portaudio_exception( PaError code ) : exception( Pa_GetErrorText( code ) ) { } }; typedef void (*PaUtilLogCallback ) (const char *log); #ifdef _MSC_VER extern "C" void PaUtil_SetDebugPrintFunction(PaUtilLogCallback cb); #endif class portaudio_raii { private: std::ostream & log; bool log_set; bool portaudio_initialized; static std::ostream * portaudio_log_stream; private: static void portaudio_log_function( const char * log ) { if ( portaudio_log_stream ) { *portaudio_log_stream << "PortAudio: " << log; } } protected: void check_portaudio_error( PaError e ) { if ( e > 0 ) { return; } if ( e == paNoError ) { return; } if ( e == paOutputUnderflowed ) { log << "PortAudio warning: " << Pa_GetErrorText( e ) << std::endl; return; } throw portaudio_exception( e ); } public: portaudio_raii( bool verbose, std::ostream & log ) : log(log), log_set(false), portaudio_initialized(false) { if ( verbose ) { portaudio_log_stream = &log; } else { portaudio_log_stream = 0; } #ifdef _MSC_VER PaUtil_SetDebugPrintFunction( portaudio_log_function ); log_set = true; #endif check_portaudio_error( Pa_Initialize() ); portaudio_initialized = true; if ( verbose ) { *portaudio_log_stream << std::endl; } } ~portaudio_raii() { if ( portaudio_initialized ) { check_portaudio_error( Pa_Terminate() ); portaudio_initialized = false; } if ( log_set ) { #ifdef _MSC_VER PaUtil_SetDebugPrintFunction( NULL ); log_set = false; #endif } portaudio_log_stream = 0; } }; std::ostream * portaudio_raii::portaudio_log_stream = 0; class portaudio_stream_blocking_raii : public portaudio_raii, public write_buffers_interface { private: PaStream * stream; bool interleaved; std::size_t channels; std::vector sampleBufFloat; std::vector sampleBufInt; public: portaudio_stream_blocking_raii( commandlineflags & flags, std::ostream & log ) : portaudio_raii(flags.verbose, log) , stream(NULL) , interleaved(false) , channels(flags.channels) { PaStreamParameters streamparameters; std::memset( &streamparameters, 0, sizeof(PaStreamParameters) ); std::istringstream device_string( flags.device ); int device = -1; device_string >> device; streamparameters.device = ( device == -1 ) ? Pa_GetDefaultOutputDevice() : device; streamparameters.channelCount = flags.channels; streamparameters.sampleFormat = ( flags.use_float ? paFloat32 : paInt16 ) | paNonInterleaved; if ( flags.buffer == default_high ) { streamparameters.suggestedLatency = Pa_GetDeviceInfo( streamparameters.device )->defaultHighOutputLatency; flags.buffer = static_cast( Pa_GetDeviceInfo( streamparameters.device )->defaultHighOutputLatency * 1000.0 ); } else if ( flags.buffer == default_low ) { streamparameters.suggestedLatency = Pa_GetDeviceInfo( streamparameters.device )->defaultLowOutputLatency; flags.buffer = static_cast( Pa_GetDeviceInfo( streamparameters.device )->defaultLowOutputLatency * 1000.0 ); } else { streamparameters.suggestedLatency = flags.buffer * 0.001; } unsigned long framesperbuffer = 0; if ( flags.mode != Mode::UI ) { framesperbuffer = paFramesPerBufferUnspecified; flags.period = 50; flags.period = std::min( flags.period, flags.buffer / 3 ); } else if ( flags.period == default_high ) { framesperbuffer = paFramesPerBufferUnspecified; flags.period = 50; flags.period = std::min( flags.period, flags.buffer / 3 ); } else if ( flags.period == default_low ) { framesperbuffer = paFramesPerBufferUnspecified; flags.period = 10; flags.period = std::min( flags.period, flags.buffer / 3 ); } else { framesperbuffer = flags.period * flags.samplerate / 1000; } if ( flags.period <= 0 ) { flags.period = 1; } flags.apply_default_buffer_sizes(); if ( flags.verbose ) { log << "PortAudio:" << std::endl; log << " device: " << streamparameters.device << " [ " << Pa_GetHostApiInfo( Pa_GetDeviceInfo( streamparameters.device )->hostApi )->name << " / " << Pa_GetDeviceInfo( streamparameters.device )->name << " ] " << std::endl; log << " low latency: " << Pa_GetDeviceInfo( streamparameters.device )->defaultLowOutputLatency << std::endl; log << " high latency: " << Pa_GetDeviceInfo( streamparameters.device )->defaultHighOutputLatency << std::endl; log << " suggested latency: " << streamparameters.suggestedLatency << std::endl; log << " frames per buffer: " << framesperbuffer << std::endl; log << " ui redraw: " << flags.period << std::endl; } PaError e = PaError(); e = Pa_OpenStream( &stream, NULL, &streamparameters, flags.samplerate, framesperbuffer, ( flags.dither > 0 ) ? paNoFlag : paDitherOff, NULL, NULL ); if ( e != paNoError ) { // Non-interleaved failed, try interleaved next. // This might help broken portaudio on MacOS X. streamparameters.sampleFormat &= ~paNonInterleaved; e = Pa_OpenStream( &stream, NULL, &streamparameters, flags.samplerate, framesperbuffer, ( flags.dither > 0 ) ? paNoFlag : paDitherOff, NULL, NULL ); if ( e == paNoError ) { interleaved = true; } check_portaudio_error( e ); } check_portaudio_error( Pa_StartStream( stream ) ); if ( flags.verbose ) { log << " channels: " << streamparameters.channelCount << std::endl; log << " sampleformat: " << ( ( ( streamparameters.sampleFormat & ~paNonInterleaved ) == paFloat32 ) ? "paFloat32" : "paInt16" ) << std::endl; log << " latency: " << Pa_GetStreamInfo( stream )->outputLatency << std::endl; log << " samplerate: " << Pa_GetStreamInfo( stream )->sampleRate << std::endl; log << std::endl; } } ~portaudio_stream_blocking_raii() { if ( stream ) { PaError stopped = Pa_IsStreamStopped( stream ); check_portaudio_error( stopped ); if ( !stopped ) { check_portaudio_error( Pa_StopStream( stream ) ); } check_portaudio_error( Pa_CloseStream( stream ) ); stream = NULL; } } private: template void write_frames( const Tsample * buffer, std::size_t frames ) { while ( frames > 0 ) { unsigned long chunk_frames = static_cast( std::min( static_cast( frames ), static_cast( std::numeric_limits::max() ) ) ); check_portaudio_error( Pa_WriteStream( stream, buffer, chunk_frames ) ); buffer += chunk_frames * channels; frames -= chunk_frames; } } template void write_frames( std::vector buffers, std::size_t frames ) { while ( frames > 0 ) { unsigned long chunk_frames = static_cast( std::min( static_cast( frames ), static_cast( std::numeric_limits::max() ) ) ); check_portaudio_error( Pa_WriteStream( stream, buffers.data(), chunk_frames ) ); for ( std::size_t channel = 0; channel < channels; ++channel ) { buffers[channel] += chunk_frames; } frames -= chunk_frames; } } public: void write( const std::vector buffers, std::size_t frames ) override { if ( interleaved ) { sampleBufFloat.clear(); for ( std::size_t frame = 0; frame < frames; ++frame ) { for ( std::size_t channel = 0; channel < channels; ++channel ) { sampleBufFloat.push_back( buffers[channel][frame] ); } } write_frames( sampleBufFloat.data(), frames ); } else { write_frames( buffers, frames ); } } void write( const std::vector buffers, std::size_t frames ) override { if ( interleaved ) { sampleBufInt.clear(); for ( std::size_t frame = 0; frame < frames; ++frame ) { for ( std::size_t channel = 0; channel < channels; ++channel ) { sampleBufInt.push_back( buffers[channel][frame] ); } } write_frames( sampleBufInt.data(), frames ); } else { write_frames( buffers, frames ); } } bool unpause() override { check_portaudio_error( Pa_StartStream( stream ) ); return true; } bool pause() override { check_portaudio_error( Pa_StopStream( stream ) ); return true; } bool sleep( int ms ) override { Pa_Sleep( ms ); return true; } }; #define portaudio_stream_raii portaudio_stream_blocking_raii static std::string show_portaudio_devices( std::ostream & log ) { std::ostringstream devices; devices << " portaudio:" << std::endl; portaudio_raii portaudio( false, log ); for ( PaDeviceIndex i = 0; i < Pa_GetDeviceCount(); ++i ) { if ( Pa_GetDeviceInfo( i ) && Pa_GetDeviceInfo( i )->maxOutputChannels > 0 ) { devices << " " << i << ": "; if ( Pa_GetHostApiInfo( Pa_GetDeviceInfo( i )->hostApi ) && Pa_GetHostApiInfo( Pa_GetDeviceInfo( i )->hostApi )->name ) { devices << Pa_GetHostApiInfo( Pa_GetDeviceInfo( i )->hostApi )->name; } else { devices << "Host API " << Pa_GetDeviceInfo( i )->hostApi; } if ( Pa_GetHostApiInfo( Pa_GetDeviceInfo( i )->hostApi ) ) { if ( i == Pa_GetHostApiInfo( Pa_GetDeviceInfo( i )->hostApi )->defaultOutputDevice ) { devices << " (default)"; } } devices << " - "; if ( Pa_GetDeviceInfo( i )->name ) { devices << Pa_GetDeviceInfo( i )->name; } else { devices << "Device " << i; } devices << " ("; devices << "high latency: " << Pa_GetDeviceInfo( i )->defaultHighOutputLatency; devices << ", "; devices << "low latency: " << Pa_GetDeviceInfo( i )->defaultLowOutputLatency; devices << ")"; devices << std::endl; } } return devices.str(); } } // namespace openmpt123 #endif // MPT_WITH_PORTAUDIO #endif // OPENMPT123_PORTAUDIO_HPP libopenmpt-0.6.1+release.autotools/openmpt123/openmpt123_pulseaudio.hpp0000644000175000017500000001146313317406650023051 00000000000000/* * openmpt123_pulseaudio.hpp * ------------------------- * Purpose: libopenmpt command line player * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #ifndef OPENMPT123_PULSEAUDIO_HPP #define OPENMPT123_PULSEAUDIO_HPP #include "openmpt123_config.hpp" #include "openmpt123.hpp" #if defined(MPT_WITH_PULSEAUDIO) #include #include namespace openmpt123 { struct pulseaudio_exception : public exception { static std::string error_to_string( int error ) { try { if ( error == 0 ) { return std::string(); } std::ostringstream e; const char * str = pa_strerror( error ); if ( !str ) { e << "error=" << error; return e.str(); } if ( std::strlen(str) == 0 ) { e << "error=" << error; return e.str(); } e << str << " (error=" << error << ")"; return e.str(); } catch ( const std::bad_alloc & ) { return std::string(); } } pulseaudio_exception( int error ) : exception( error_to_string( error ) ) { } }; class pulseaudio_stream_raii : public write_buffers_interface { private: pa_simple * stream; std::size_t channels; std::size_t sampleSize; std::vector sampleBufFloat; std::vector sampleBufInt; public: pulseaudio_stream_raii( commandlineflags & flags, std::ostream & /* log */ ) : stream(NULL) , channels(flags.channels) , sampleSize(flags.use_float ? sizeof( float ) : sizeof( std::int16_t )) { int error = 0; pa_sample_spec ss; std::memset( &ss, 0, sizeof( pa_sample_spec ) ); ss.format = ( flags.use_float ? PA_SAMPLE_FLOAT32 : PA_SAMPLE_S16NE ); ss.rate = flags.samplerate; ss.channels = flags.channels; pa_buffer_attr ba; std::memset( &ba, 0, sizeof( pa_buffer_attr ) ); bool use_ba = false; if ( flags.buffer != default_high && flags.buffer != default_low ) { use_ba = true; ba.maxlength = channels * sampleSize * ( flags.buffer * flags.samplerate / 1000 ); } else { ba.maxlength = static_cast(-1); } if ( flags.period != default_high && flags.period != default_low ) { use_ba = true; ba.minreq = channels * sampleSize * ( flags.period * flags.samplerate / 1000 ); if ( ba.maxlength != static_cast(-1) ) { ba.tlength = ba.maxlength - ba.minreq; ba.prebuf = ba.tlength; } else { ba.tlength = static_cast(-1); ba.prebuf = static_cast(-1); } } else { ba.minreq = static_cast(-1); ba.tlength = static_cast(-1); ba.prebuf = static_cast(-1); } ba.fragsize = 0; flags.apply_default_buffer_sizes(); sampleBufFloat.resize( channels * ( flags.ui_redraw_interval * flags.samplerate / 1000 ) ); sampleBufInt.resize( channels * ( flags.ui_redraw_interval * flags.samplerate / 1000 ) ); stream = pa_simple_new( NULL, "openmpt123", PA_STREAM_PLAYBACK, NULL, "openmpt123", &ss, NULL, ( use_ba ? &ba : NULL ), &error ); if ( !stream ) { throw pulseaudio_exception( error ); } } ~pulseaudio_stream_raii() { int error = 0; if ( stream ) { error = 0; if ( pa_simple_drain( stream, &error ) < 0 ) { // throw pulseaudio_exception( error ); } pa_simple_free( stream ); stream = NULL; } } private: template void write_frames( const Tsample * buffer, std::size_t frames ) { int error = 0; if ( pa_simple_write( stream, buffer, frames * channels * sampleSize, &error ) < 0 ) { throw pulseaudio_exception( error ); } } public: void write( const std::vector buffers, std::size_t frames ) override { sampleBufFloat.clear(); for ( std::size_t frame = 0; frame < frames; ++frame ) { for ( std::size_t channel = 0; channel < channels; ++channel ) { sampleBufFloat.push_back( buffers[channel][frame] ); } } write_frames( sampleBufFloat.data(), frames ); } void write( const std::vector buffers, std::size_t frames ) override { sampleBufInt.clear(); for ( std::size_t frame = 0; frame < frames; ++frame ) { for ( std::size_t channel = 0; channel < channels; ++channel ) { sampleBufInt.push_back( buffers[channel][frame] ); } } write_frames( sampleBufInt.data(), frames ); } bool unpause() override { return true; } bool pause() override { int error = 0; error = 0; if ( pa_simple_drain( stream, &error ) < 0 ) { throw pulseaudio_exception( error ); } return true; } bool sleep( int ms ) override { pa_msleep( ms ); return true; } }; static std::string show_pulseaudio_devices( std::ostream & /* log */ ) { std::ostringstream devices; devices << " pulseaudio:" << std::endl; devices << " " << "0" << ": Default Device" << std::endl; return devices.str(); } } // namespace openmpt123 #endif // MPT_WITH_PULSEAUDIO #endif // OPENMPT123_PULSEAUDIO_HPP libopenmpt-0.6.1+release.autotools/openmpt123/openmpt123_raw.hpp0000644000175000017500000000356113202011064021450 00000000000000/* * openmpt123_raw.hpp * ------------------ * Purpose: libopenmpt command line player * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #ifndef OPENMPT123_RAW_HPP #define OPENMPT123_RAW_HPP #include "openmpt123_config.hpp" #include "openmpt123.hpp" #include namespace openmpt123 { class raw_stream_raii : public file_audio_stream_base { private: commandlineflags flags; std::ofstream file; std::vector interleaved_float_buffer; std::vector interleaved_int_buffer; public: raw_stream_raii( const std::string & filename, const commandlineflags & flags_, std::ostream & /*log*/ ) : flags(flags_), file(filename.c_str(), std::ios::binary) { return; } ~raw_stream_raii() { return; } void write_metadata( std::map /* metadata */ ) override { return; } void write( const std::vector buffers, std::size_t frames ) override { interleaved_float_buffer.clear(); for ( std::size_t frame = 0; frame < frames; frame++ ) { for ( std::size_t channel = 0; channel < buffers.size(); channel++ ) { interleaved_float_buffer.push_back( buffers[channel][frame] ); } } file.write( reinterpret_cast( interleaved_float_buffer.data() ), frames * buffers.size() * sizeof( float ) ); } void write( const std::vector buffers, std::size_t frames ) override { interleaved_int_buffer.clear(); for ( std::size_t frame = 0; frame < frames; frame++ ) { for ( std::size_t channel = 0; channel < buffers.size(); channel++ ) { interleaved_int_buffer.push_back( buffers[channel][frame] ); } } file.write( reinterpret_cast( interleaved_int_buffer.data() ), frames * buffers.size() * sizeof( std::int16_t ) ); } }; } // namespace openmpt123 #endif // OPENMPT123_RAW_HPP libopenmpt-0.6.1+release.autotools/openmpt123/openmpt123_sdl2.hpp0000644000175000017500000001463713603442726021553 00000000000000/* * openmpt123_sdl2.hpp * ------------------- * Purpose: libopenmpt command line player * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #ifndef OPENMPT123_SDL2_HPP #define OPENMPT123_SDL2_HPP #include "openmpt123_config.hpp" #include "openmpt123.hpp" #if defined(MPT_WITH_SDL2) #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wimplicit-fallthrough" #pragma clang diagnostic ignored "-Wreserved-id-macro" #endif // __clang__ #include #if defined(__clang__) #pragma clang diagnostic pop #endif // __clang__ #ifdef main #undef main #endif #ifdef SDL_main #undef SDL_main #endif #if (SDL_COMPILEDVERSION < SDL_VERSIONNUM(2, 0, 4)) MPT_WARNING("Support for SDL2 < 2.0.4 has been deprecated and will be removed in a future openmpt123 version.") #endif namespace openmpt123 { struct sdl2_exception : public exception { private: static std::string text_from_code( int code ) { std::ostringstream s; s << code; return s.str(); } public: sdl2_exception( int code, const char * error ) : exception( text_from_code( code ) + " (" + ( error ? std::string(error) : std::string("NULL") ) + ")" ) { } }; static void check_sdl2_error( int e ) { if ( e < 0 ) { throw sdl2_exception( e, SDL_GetError() ); } } class sdl2_raii { public: sdl2_raii( Uint32 flags ) { check_sdl2_error( SDL_Init( flags ) ); } ~sdl2_raii() { SDL_Quit(); } }; class sdl2_stream_raii : public write_buffers_interface { private: std::ostream & log; sdl2_raii sdl2; int dev; std::size_t channels; bool use_float; std::size_t sampleQueueMaxFrames; std::vector sampleBufFloat; std::vector sampleBufInt; protected: std::uint32_t round_up_power2(std::uint32_t x) { std::uint32_t result = 1; while ( result < x ) { result *= 2; } return result; } public: sdl2_stream_raii( commandlineflags & flags, std::ostream & log_ ) : log(log_) , sdl2( SDL_INIT_NOPARACHUTE | SDL_INIT_TIMER | SDL_INIT_AUDIO ) , dev(-1) , channels(flags.channels) , use_float(flags.use_float) , sampleQueueMaxFrames(0) { if ( flags.buffer == default_high ) { flags.buffer = 160; } else if ( flags.buffer == default_low ) { flags.buffer = 80; } if ( flags.period == default_high ) { flags.period = 20; } else if ( flags.period == default_low ) { flags.period = 10; } flags.apply_default_buffer_sizes(); SDL_AudioSpec audiospec; std::memset( &audiospec, 0, sizeof( SDL_AudioSpec ) ); audiospec.freq = flags.samplerate; audiospec.format = ( flags.use_float ? AUDIO_F32SYS : AUDIO_S16SYS ); audiospec.channels = flags.channels; audiospec.silence = 0; audiospec.samples = round_up_power2( ( flags.buffer * flags.samplerate ) / ( 1000 * 2 ) ); audiospec.size = audiospec.samples * audiospec.channels * ( flags.use_float ? sizeof( float ) : sizeof( std::int16_t ) ); audiospec.callback = NULL; audiospec.userdata = NULL; if ( flags.verbose ) { log << "SDL2:" << std::endl; log << " latency: " << ( audiospec.samples * 2.0 / flags.samplerate ) << " (2 * " << audiospec.samples << ")" << std::endl; log << std::endl; } sampleQueueMaxFrames = round_up_power2( ( flags.buffer * flags.samplerate ) / ( 1000 * 2 ) ); SDL_AudioSpec audiospec_obtained; std::memset( &audiospec_obtained, 0, sizeof( SDL_AudioSpec ) ); std::memcpy( &audiospec_obtained, &audiospec, sizeof( SDL_AudioSpec ) ); dev = SDL_OpenAudioDevice( NULL, 0, &audiospec, &audiospec_obtained, 0 ); if ( dev < 0 ) { check_sdl2_error( dev ); } else if ( dev == 0 ) { check_sdl2_error( -1 ); } SDL_PauseAudioDevice( dev, 0 ); } ~sdl2_stream_raii() { SDL_PauseAudioDevice( dev, 1 ); SDL_CloseAudioDevice( dev ); } private: std::size_t get_num_writeable_frames() { std::size_t num_queued_frames = SDL_GetQueuedAudioSize( dev ) / ( use_float ? sizeof( float ) : sizeof( std::int16_t ) ) / channels; if ( num_queued_frames > sampleQueueMaxFrames ) { return 0; } return sampleQueueMaxFrames - num_queued_frames; } template void write_frames( const Tsample * buffer, std::size_t frames ) { while ( frames > 0 ) { std::size_t chunk_frames = std::min( frames, get_num_writeable_frames() ); if ( chunk_frames > 0 ) { check_sdl2_error( SDL_QueueAudio( dev, buffer, chunk_frames * channels * ( use_float ? sizeof( float ) : sizeof( std::int16_t ) ) ) ); frames -= chunk_frames; buffer += chunk_frames * channels; } else { SDL_Delay( 1 ); } } } public: void write( const std::vector buffers, std::size_t frames ) override { sampleBufFloat.clear(); for ( std::size_t frame = 0; frame < frames; ++frame ) { for ( std::size_t channel = 0; channel < channels; ++channel ) { sampleBufFloat.push_back( buffers[channel][frame] ); } } write_frames( sampleBufFloat.data(), frames ); } void write( const std::vector buffers, std::size_t frames ) override { sampleBufInt.clear(); for ( std::size_t frame = 0; frame < frames; ++frame ) { for ( std::size_t channel = 0; channel < channels; ++channel ) { sampleBufInt.push_back( buffers[channel][frame] ); } } write_frames( sampleBufInt.data(), frames ); } bool pause() override { SDL_PauseAudioDevice( dev, 1 ); return true; } bool unpause() override { SDL_PauseAudioDevice( dev, 0 ); return true; } bool sleep( int ms ) override { SDL_Delay( ms ); return true; } }; static std::string show_sdl2_devices( std::ostream & /* log */ ) { std::ostringstream devices; std::size_t device_index = 0; devices << " SDL2:" << std::endl; sdl2_raii sdl2( SDL_INIT_NOPARACHUTE | SDL_INIT_AUDIO ); for ( int driver = 0; driver < SDL_GetNumAudioDrivers(); ++driver ) { const char * driver_name = SDL_GetAudioDriver( driver ); if ( !driver_name ) { continue; } if ( std::string( driver_name ).empty() ) { continue; } if ( SDL_AudioInit( driver_name ) < 0 ) { continue; } for ( int device = 0; device < SDL_GetNumAudioDevices( 0 ); ++device ) { const char * device_name = SDL_GetAudioDeviceName( device, 0 ); if ( !device_name ) { continue; } if ( std::string( device_name ).empty() ) { continue; } devices << " " << device_index << ": " << driver_name << " - " << device_name << std::endl; device_index++; } SDL_AudioQuit(); } return devices.str(); } } // namespace openmpt123 #endif // MPT_WITH_SDL2 #endif // OPENMPT123_SDL2_HPP libopenmpt-0.6.1+release.autotools/openmpt123/openmpt123_sndfile.hpp0000644000175000017500000001540013202011064022276 00000000000000/* * openmpt123_sndfile.hpp * ---------------------- * Purpose: libopenmpt command line player * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #ifndef OPENMPT123_SNDFILE_HPP #define OPENMPT123_SNDFILE_HPP #include "openmpt123_config.hpp" #include "openmpt123.hpp" #if defined(MPT_WITH_SNDFILE) #include namespace openmpt123 { class sndfile_stream_raii : public file_audio_stream_base { private: commandlineflags flags; std::ostream & log; SNDFILE * sndfile; std::vector interleaved_float_buffer; std::vector interleaved_int_buffer; private: enum match_mode_enum { match_print, match_recurse, match_exact, match_better, match_any }; std::string match_mode_to_string( match_mode_enum match_mode ) { switch ( match_mode ) { case match_print : return "print" ; break; case match_recurse: return "recurse"; break; case match_exact : return "exact" ; break; case match_better : return "better" ; break; case match_any : return "any" ; break; } return ""; } int matched_result( const SF_FORMAT_INFO & format_info, const SF_FORMAT_INFO & subformat_info, match_mode_enum match_mode ) { if ( flags.verbose ) { log << "sndfile: using format '" << format_info.name << " (" << format_info.extension << ")" << " / " << subformat_info.name << "', " << "match: " << match_mode_to_string( match_mode ) << std::endl; } return ( format_info.format & SF_FORMAT_TYPEMASK ) | subformat_info.format; } int find_format( const std::string & extension, match_mode_enum match_mode ) { if ( match_mode == match_recurse ) { int result = 0; result = find_format( extension, match_exact ); if ( result ) { return result; } result = find_format( extension, match_better ); if ( result ) { return result; } result = find_format( extension, match_any ); if ( result ) { return result; } if ( result ) { return result; } return 0; } int format = 0; int major_count; sf_command( 0, SFC_GET_FORMAT_MAJOR_COUNT, &major_count, sizeof( int ) ); for ( int m = 0; m < major_count; m++ ) { SF_FORMAT_INFO format_info; format_info.format = m; sf_command( 0, SFC_GET_FORMAT_MAJOR, &format_info, sizeof( SF_FORMAT_INFO ) ); format = format_info.format; int subtype_count; sf_command( 0, SFC_GET_FORMAT_SUBTYPE_COUNT, &subtype_count, sizeof( int ) ); for ( int s = 0; s < subtype_count; s++ ) { SF_FORMAT_INFO subformat_info; subformat_info.format = s; sf_command( 0, SFC_GET_FORMAT_SUBTYPE, &subformat_info, sizeof( SF_FORMAT_INFO ) ); format = ( format & SF_FORMAT_TYPEMASK ) | subformat_info.format; SF_INFO sfinfo; std::memset( &sfinfo, 0, sizeof( SF_INFO ) ); sfinfo.channels = flags.channels; sfinfo.format = format; if ( sf_format_check( &sfinfo ) ) { switch ( match_mode ) { case match_print: log << "sndfile: " << ( format_info.name ? format_info.name : "" ) << " (" << ( format_info.extension ? format_info.extension : "" ) << ")" << " / " << ( subformat_info.name ? subformat_info.name : "" ) << " [" << std::setbase(16) << std::setw(8) << std::setfill('0') << format_info.format << "|" << std::setbase(16) << std::setw(8) << std::setfill('0') << subformat_info.format << "]" << std::endl; break; case match_recurse: break; case match_exact: if ( extension == format_info.extension ) { if ( flags.use_float && ( subformat_info.format == SF_FORMAT_FLOAT ) ) { return matched_result( format_info, subformat_info, match_mode ); } else if ( !flags.use_float && ( subformat_info.format == SF_FORMAT_PCM_16 ) ) { return matched_result( format_info, subformat_info, match_mode ); } } break; case match_better: if ( extension == format_info.extension ) { if ( flags.use_float && ( subformat_info.format == SF_FORMAT_FLOAT || subformat_info.format == SF_FORMAT_DOUBLE ) ) { return matched_result( format_info, subformat_info, match_mode ); } else if ( !flags.use_float && ( subformat_info.format & ( subformat_info.format == SF_FORMAT_PCM_16 || subformat_info.format == SF_FORMAT_PCM_24 || subformat_info.format == SF_FORMAT_PCM_32 ) ) ) { return matched_result( format_info, subformat_info, match_mode ); } } break; case match_any: if ( extension == format_info.extension ) { return matched_result( format_info, subformat_info, match_mode ); } break; } } } } return 0; } void write_metadata_field( int str_type, const std::string & str ) { if ( !str.empty() ) { sf_set_string( sndfile, str_type, str.c_str() ); } } public: sndfile_stream_raii( const std::string & filename, const commandlineflags & flags_, std::ostream & log_ ) : flags(flags_), log(log_), sndfile(0) { if ( flags.verbose ) { find_format( "", match_print ); log << std::endl; } int format = find_format( flags.output_extension, match_recurse ); if ( !format ) { throw exception( "unknown file type" ); } SF_INFO info; std::memset( &info, 0, sizeof( SF_INFO ) ); info.samplerate = flags.samplerate; info.channels = flags.channels; info.format = format; sndfile = sf_open( filename.c_str(), SFM_WRITE, &info ); } ~sndfile_stream_raii() { sf_close( sndfile ); sndfile = 0; } void write_metadata( std::map metadata ) override { write_metadata_field( SF_STR_TITLE, metadata[ "title" ] ); write_metadata_field( SF_STR_ARTIST, metadata[ "artist" ] ); write_metadata_field( SF_STR_DATE, metadata[ "date" ] ); write_metadata_field( SF_STR_COMMENT, metadata[ "message" ] ); write_metadata_field( SF_STR_SOFTWARE, append_software_tag( metadata[ "tracker" ] ) ); } void write( const std::vector buffers, std::size_t frames ) override { interleaved_float_buffer.clear(); for ( std::size_t frame = 0; frame < frames; frame++ ) { for ( std::size_t channel = 0; channel < buffers.size(); channel++ ) { interleaved_float_buffer.push_back( buffers[channel][frame] ); } } sf_writef_float( sndfile, interleaved_float_buffer.data(), frames ); } void write( const std::vector buffers, std::size_t frames ) override { interleaved_int_buffer.clear(); for ( std::size_t frame = 0; frame < frames; frame++ ) { for ( std::size_t channel = 0; channel < buffers.size(); channel++ ) { interleaved_int_buffer.push_back( buffers[channel][frame] ); } } sf_writef_short( sndfile, interleaved_int_buffer.data(), frames ); } }; } // namespace openmpt123 #endif // MPT_WITH_SNDFILE #endif // OPENMPT123_SNDFILE_HPP libopenmpt-0.6.1+release.autotools/openmpt123/openmpt123_stdout.hpp0000644000175000017500000000311713202011064022176 00000000000000/* * openmpt123_stdout.hpp * --------------------- * Purpose: libopenmpt command line player * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #ifndef OPENMPT123_STDOUT_HPP #define OPENMPT123_STDOUT_HPP #include "openmpt123_config.hpp" #include "openmpt123.hpp" namespace openmpt123 { class stdout_stream_raii : public write_buffers_interface { private: std::vector interleaved_float_buffer; std::vector interleaved_int_buffer; public: stdout_stream_raii() { return; } public: void write( const std::vector buffers, std::size_t frames ) override { interleaved_float_buffer.clear(); for ( std::size_t frame = 0; frame < frames; frame++ ) { for ( std::size_t channel = 0; channel < buffers.size(); channel++ ) { interleaved_float_buffer.push_back( buffers[channel][frame] ); } } std::cout.write( reinterpret_cast( interleaved_float_buffer.data() ), interleaved_float_buffer.size() * sizeof( float ) ); } void write( const std::vector buffers, std::size_t frames ) override { interleaved_int_buffer.clear(); for ( std::size_t frame = 0; frame < frames; frame++ ) { for ( std::size_t channel = 0; channel < buffers.size(); channel++ ) { interleaved_int_buffer.push_back( buffers[channel][frame] ); } } std::cout.write( reinterpret_cast( interleaved_int_buffer.data() ), interleaved_int_buffer.size() * sizeof( std::int16_t ) ); } }; } // namespace openmpt123 #endif // OPENMPT123_STDOUT_HPP libopenmpt-0.6.1+release.autotools/openmpt123/openmpt123_waveout.hpp0000644000175000017500000001350314107230757022367 00000000000000/* * openmpt123_waveout.hpp * ------------------------ * Purpose: libopenmpt command line player * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #ifndef OPENMPT123_WAVEOUT_HPP #define OPENMPT123_WAVEOUT_HPP #include "openmpt123_config.hpp" #include "openmpt123.hpp" #if defined(WIN32) namespace openmpt123 { struct waveout_exception : public exception { waveout_exception() : exception( "waveout" ) { } }; class waveout_stream_raii : public write_buffers_interface { private: HWAVEOUT waveout; std::size_t num_channels; std::size_t num_chunks; std::size_t frames_per_chunk; std::size_t bytes_per_chunk; std::vector waveheaders; std::vector > wavebuffers; std::deque byte_queue; public: waveout_stream_raii( commandlineflags & flags ) : waveout(NULL) , num_channels(0) , num_chunks(0) , frames_per_chunk(0) , bytes_per_chunk(0) { if ( flags.buffer == default_high ) { flags.buffer = 150; } else if ( flags.buffer == default_low ) { flags.buffer = 50; } if ( flags.period == default_high ) { flags.period = 30; } else if ( flags.period == default_low ) { flags.period = 10; } flags.apply_default_buffer_sizes(); WAVEFORMATEX wfx; ZeroMemory( &wfx, sizeof( wfx ) ); wfx.wFormatTag = flags.use_float ? WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM; wfx.nChannels = static_cast( flags.channels ); wfx.nSamplesPerSec = flags.samplerate; wfx.wBitsPerSample = flags.use_float ? 32 : 16; wfx.nBlockAlign = ( wfx.wBitsPerSample / 8 ) * wfx.nChannels; wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; wfx.cbSize = 0; std::istringstream device_string( flags.device ); int device = -1; device_string >> device; waveOutOpen( &waveout, device == -1 ? WAVE_MAPPER : device, &wfx, 0, 0, CALLBACK_NULL ); num_channels = flags.channels; std::size_t frames_per_buffer = flags.samplerate * flags.buffer / 1000; num_chunks = ( flags.buffer + flags.period - 1 ) / flags.period; if ( num_chunks < 2 ) { num_chunks = 2; } frames_per_chunk = ( frames_per_buffer + num_chunks - 1 ) / num_chunks; bytes_per_chunk = wfx.nBlockAlign * frames_per_chunk; waveheaders.resize( num_chunks ); wavebuffers.resize( num_chunks ); for ( std::size_t i = 0; i < num_chunks; ++i ) { wavebuffers[i].resize( bytes_per_chunk ); waveheaders[i] = WAVEHDR(); waveheaders[i].lpData = wavebuffers[i].data(); waveheaders[i].dwBufferLength = static_cast( wavebuffers[i].size() ); waveheaders[i].dwFlags = 0; waveOutPrepareHeader( waveout, &waveheaders[i], sizeof( WAVEHDR ) ); } } ~waveout_stream_raii() { if ( waveout ) { write_or_wait( true ); drain(); waveOutReset( waveout ); for ( std::size_t i = 0; i < num_chunks; ++i ) { waveheaders[i].dwBufferLength = static_cast( wavebuffers[i].size() ); waveOutUnprepareHeader( waveout, &waveheaders[i], sizeof( WAVEHDR ) ); } wavebuffers.clear(); waveheaders.clear(); frames_per_chunk = 0; num_chunks = 0; waveOutClose( waveout ); waveout = NULL; } } private: void drain() { std::size_t empty_chunks = 0; while ( empty_chunks != num_chunks ) { empty_chunks = 0; for ( std::size_t chunk = 0; chunk < num_chunks; ++chunk ) { DWORD flags = waveheaders[chunk].dwFlags; if ( !(flags & WHDR_INQUEUE) || (flags & WHDR_DONE) ) { empty_chunks++; } } if ( empty_chunks != num_chunks ) { Sleep( 1 ); } } } std::size_t wait_for_empty_chunk() { while ( true ) { for ( std::size_t chunk = 0; chunk < num_chunks; ++chunk ) { DWORD flags = waveheaders[chunk].dwFlags; if ( !(flags & WHDR_INQUEUE) || (flags & WHDR_DONE) ) { return chunk; } } Sleep( 1 ); } } void write_chunk() { std::size_t chunk = wait_for_empty_chunk(); std::size_t chunk_bytes = std::min( byte_queue.size(), bytes_per_chunk ); waveheaders[chunk].dwBufferLength = static_cast( chunk_bytes ); for ( std::size_t byte = 0; byte < chunk_bytes; ++byte ) { wavebuffers[chunk][byte] = byte_queue.front(); byte_queue.pop_front(); } waveOutWrite( waveout, &waveheaders[chunk], sizeof( WAVEHDR ) ); } void write_or_wait( bool flush = false ) { while ( byte_queue.size() >= bytes_per_chunk ) { write_chunk(); } if ( flush && !byte_queue.empty() ) { write_chunk(); } } template < typename Tsample > void write_buffers( const std::vector buffers, std::size_t frames ) { for ( std::size_t frame = 0; frame < frames; ++frame ) { for ( std::size_t channel = 0; channel < buffers.size(); ++channel ) { Tsample val = buffers[channel][frame]; char buf[ sizeof( Tsample ) ]; std::memcpy( buf, &val, sizeof( Tsample ) ); std::copy( buf, buf + sizeof( Tsample ), std::back_inserter( byte_queue ) ); } } write_or_wait(); } public: void write( const std::vector buffers, std::size_t frames ) override { write_buffers( buffers, frames ); } void write( const std::vector buffers, std::size_t frames ) override { write_buffers( buffers, frames ); } bool pause() override { waveOutPause( waveout ); return true; } bool unpause() override { waveOutRestart( waveout ); return true; } bool sleep( int ms ) override { Sleep( ms ); return true; } }; static std::string show_waveout_devices( std::ostream & /*log*/ ) { std::ostringstream devices; devices << " waveout:" << std::endl; for ( UINT i = 0; i < waveOutGetNumDevs(); ++i ) { devices << " " << i << ": "; WAVEOUTCAPSW caps; ZeroMemory( &caps, sizeof( caps ) ); waveOutGetDevCapsW( i, &caps, sizeof( caps ) ); devices << mpt::transcode( mpt::common_encoding::utf8, caps.szPname ); devices << std::endl; } return devices.str(); } } // namespace openmpt123 #endif // WIN32 #endif // OPENMPT123_WAVEOUT_HPP libopenmpt-0.6.1+release.autotools/openmpt123/.clang-format0000644000175000017500000000721414044173026020544 00000000000000# clang-format 11 Language: Cpp Standard: c++17 AccessModifierOffset: -2 #? AlignAfterOpenBracket: AlwaysBreak AlignConsecutiveAssignments: false AlignConsecutiveBitFields: false AlignConsecutiveDeclarations: false AlignConsecutiveMacros: true AlignEscapedNewlines: DontAlign AlignOperands: DontAlign AlignTrailingComments: true AllowAllArgumentsOnNextLine: true AllowAllConstructorInitializersOnNextLine: true AllowAllParametersOfDeclarationOnNextLine: true AllowShortBlocksOnASingleLine: Never AllowShortCaseLabelsOnASingleLine: false AllowShortEnumsOnASingleLine: false AllowShortFunctionsOnASingleLine: Empty AllowShortIfStatementsOnASingleLine: false AllowShortLambdasOnASingleLine: Inline AllowShortLoopsOnASingleLine: false AlwaysBreakAfterReturnType: None AlwaysBreakBeforeMultilineStrings: true AlwaysBreakTemplateDeclarations: Yes BinPackArguments: true BinPackParameters: false BraceWrapping: AfterCaseLabel: true AfterClass: false AfterControlStatement: MultiLine AfterEnum: false AfterFunction: false AfterNamespace: false #AfterObjCDeclaration AfterStruct: false AfterUnion: false AfterExternBlock: false BeforeCatch: false BeforeElse: false BeforeLambdaBody: true BeforeWhile: false IndentBraces: false SplitEmptyFunction: true SplitEmptyRecord: false SplitEmptyNamespace: true #BreakAfterJavaFieldAnnotations BreakBeforeBinaryOperators: NonAssignment BreakBeforeBraces: Custom BreakBeforeTernaryOperators: true BreakConstructorInitializers: BeforeComma BreakInheritanceList: BeforeComma BreakStringLiterals: false ColumnLimit: 0 CommentPragmas: '' #? CompactNamespaces: false ConstructorInitializerAllOnOneLineOrOnePerLine: true ConstructorInitializerIndentWidth: 2 #? ContinuationIndentWidth: 2 #? Cpp11BracedListStyle: true DeriveLineEnding: true DerivePointerAlignment: false FixNamespaceComments: true ForEachMacros: [] IncludeBlocks: Preserve IncludeCategories: [] #? IncludeIsMainRegex: '' #? IncludeIsMainSourceRegex: '' #? IndentCaseBlocks: true IndentCaseLabels: true IndentExternBlock: NoIndent IndentGotoLabels: false IndentPPDirectives: None #BeforeHash IndentWidth: 2 InsertTrailingCommas: None IndentWrappedFunctionNames: true #JavaImportGroups #JavaScriptQuotes #JavaScriptWrapImports KeepEmptyLinesAtTheStartOfBlocks: true MacroBlockBegin: '' #? MacroBlockEnd: '' #? MaxEmptyLinesToKeep: 3 NamespaceIndentation: None NamespaceMacros: [] #? #ObjCBinPackProtocolList #ObjCBlockIndentWidth #ObjCBreakBeforeNestedBlockParam #ObjCSpaceAfterProperty #ObjCSpaceBeforeProtocolList #PenaltyBreakAssignment #PenaltyBreakBeforeFirstCallParameter #PenaltyBreakComment #PenaltyBreakFirstLessLess #PenaltyBreakString #PenaltyBreakTemplateDeclaration #PenaltyExcessCharacter #PenaltyReturnTypeOnItsOwnLine PointerAlignment: Middle #RawStringFormats ReflowComments: false SortIncludes: false SortUsingDeclarations: true SpaceAfterCStyleCast: false SpaceAfterLogicalNot: false SpaceAfterTemplateKeyword: true SpaceBeforeAssignmentOperators: true SpaceBeforeCpp11BracedList: false SpaceBeforeCtorInitializerColon: true SpaceBeforeInheritanceColon: true SpaceBeforeParens: ControlStatements SpaceBeforeRangeBasedForLoopColon: true SpaceBeforeSquareBrackets: false SpaceInEmptyBlock: true SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 2 SpacesInAngles: false SpacesInCStyleCastParentheses: false SpacesInConditionalStatement: true SpacesInContainerLiterals: true SpacesInParentheses: true SpacesInSquareBrackets: false StatementMacros: [ 'MPT_WARNING', 'MPT_TEST_GROUP_INLINE_IDENTIFIER', 'MPT_TEST_GROUP_INLINE', 'MPT_TEST_GROUP_STATIC' ] #? TabWidth: 2 TypenameMacros: [] #? UseCRLF: false UseTab: ForContinuationAndIndentation WhitespaceSensitiveMacros: - MPT_PP_STRINGIFY libopenmpt-0.6.1+release.autotools/sounddsp/0000755000175000017500000000000014175541576016212 500000000000000libopenmpt-0.6.1+release.autotools/sounddsp/AGC.cpp0000644000175000017500000000631413242404421017211 00000000000000/* * AGC.cpp * ------- * Purpose: Automatic Gain Control * Notes : Ugh... This should really be removed at some point. * Authors: Olivier Lapicque * OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "../sounddsp/AGC.h" OPENMPT_NAMESPACE_BEGIN ////////////////////////////////////////////////////////////////////////////////// // Automatic Gain Control #ifndef NO_AGC #define AGC_PRECISION 10 #define AGC_UNITY (1 << AGC_PRECISION) // Limiter #define MIXING_LIMITMAX (0x08100000) #define MIXING_LIMITMIN (-MIXING_LIMITMAX) static UINT ProcessAGC(int *pBuffer, int *pRearBuffer, std::size_t nSamples, std::size_t nChannels, int nAGC) { if(nChannels == 1) { while(nSamples--) { int val = (int)(((int64)*pBuffer * (int32)nAGC) >> AGC_PRECISION); if(val < MIXING_LIMITMIN || val > MIXING_LIMITMAX) nAGC--; *pBuffer = val; pBuffer++; } } else { if(nChannels == 2) { while(nSamples--) { int fl = (int)(((int64)pBuffer[0] * (int32)nAGC) >> AGC_PRECISION); int fr = (int)(((int64)pBuffer[1] * (int32)nAGC) >> AGC_PRECISION); bool dec = false; dec = dec || (fl < MIXING_LIMITMIN || fl > MIXING_LIMITMAX); dec = dec || (fr < MIXING_LIMITMIN || fr > MIXING_LIMITMAX); if(dec) nAGC--; pBuffer[0] = fl; pBuffer[1] = fr; pBuffer += 2; } } else if(nChannels == 4) { while(nSamples--) { int fl = (int)(((int64)pBuffer[0] * (int32)nAGC) >> AGC_PRECISION); int fr = (int)(((int64)pBuffer[1] * (int32)nAGC) >> AGC_PRECISION); int rl = (int)(((int64)pRearBuffer[0] * (int32)nAGC) >> AGC_PRECISION); int rr = (int)(((int64)pRearBuffer[1] * (int32)nAGC) >> AGC_PRECISION); bool dec = false; dec = dec || (fl < MIXING_LIMITMIN || fl > MIXING_LIMITMAX); dec = dec || (fr < MIXING_LIMITMIN || fr > MIXING_LIMITMAX); dec = dec || (rl < MIXING_LIMITMIN || rl > MIXING_LIMITMAX); dec = dec || (rr < MIXING_LIMITMIN || rr > MIXING_LIMITMAX); if(dec) nAGC--; pBuffer[0] = fl; pBuffer[1] = fr; pRearBuffer[0] = rl; pRearBuffer[1] = rr; pBuffer += 2; pRearBuffer += 2; } } } return nAGC; } CAGC::CAGC() { Initialize(true, 44100); } void CAGC::Process(int *MixSoundBuffer, int *RearSoundBuffer, std::size_t count, std::size_t nChannels) { UINT agc = ProcessAGC(MixSoundBuffer, RearSoundBuffer, count, nChannels, m_nAGC); // 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 >= m_nAGC) && (m_nAGC < AGC_UNITY)) { m_nAGCRecoverCount += count; if(m_nAGCRecoverCount >= m_Timeout) { m_nAGCRecoverCount = 0; m_nAGC++; } } else { m_nAGC = agc; m_nAGCRecoverCount = 0; } } void CAGC::Adjust(UINT oldVol, UINT newVol) { m_nAGC = m_nAGC * oldVol / newVol; if (m_nAGC > AGC_UNITY) m_nAGC = AGC_UNITY; } void CAGC::Initialize(bool bReset, DWORD MixingFreq) { if(bReset) { m_nAGC = AGC_UNITY; m_nAGCRecoverCount = 0; } m_Timeout = (MixingFreq >> (AGC_PRECISION-8)) >> 1; } #else MPT_MSVC_WORKAROUND_LNK4221(AGC) #endif // NO_AGC OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/sounddsp/AGC.h0000644000175000017500000000131414052666041016661 00000000000000/* * AGC.h * ----- * Purpose: Automatic Gain Control * Notes : Ugh... This should really be removed at some point. * Authors: Olivier Lapicque * OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" OPENMPT_NAMESPACE_BEGIN #ifndef NO_AGC class CAGC { private: UINT m_nAGC; std::size_t m_nAGCRecoverCount; UINT m_Timeout; public: CAGC(); void Initialize(bool bReset, DWORD MixingFreq); public: void Process(int *MixSoundBuffer, int *RearSoundBuffer, std::size_t count, std::size_t nChannels); void Adjust(UINT oldVol, UINT newVol); }; #endif // NO_AGC OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/sounddsp/DSP.cpp0000644000175000017500000002655614053010430017251 00000000000000/* * DSP.cpp * ----------- * Purpose: Mixing code for various DSPs (EQ, Mega-Bass, ...) * Notes : Ugh... This should really be removed at some point. * Authors: Olivier Lapicque * OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "DSP.h" #include "openmpt/soundbase/MixSample.hpp" #include OPENMPT_NAMESPACE_BEGIN #ifndef NO_DSP // Bass Expansion #define DEFAULT_XBASS_RANGE 14 // (x+2)*20 Hz (320Hz) #define DEFAULT_XBASS_DEPTH 6 // 1+(3>>(x-4)) (+6dB) //////////////////////////////////////////////////////////////////// // DSP Effects internal state static void X86_StereoDCRemoval(int *, uint32 count, int32 &nDCRFlt_Y1l, int32 &nDCRFlt_X1l, int32 &nDCRFlt_Y1r, int32 &nDCRFlt_X1r); static void X86_MonoDCRemoval(int *, uint32 count, int32 &nDCRFlt_Y1l, int32 &nDCRFlt_X1l); /////////////////////////////////////////////////////////////////////////////////// // // Biquad setup // #define PI 3.14159265358979323f static inline float Sgn(float x) { return (x >= 0) ? 1.0f : -1.0f; } static void ShelfEQ(int32 scale, int32 &outA1, int32 &outB0, int32 &outB1, int32 F_c, int32 F_s, float gainDC, float gainFT, float gainPI) { float a1, b0, b1; float gainFT2, gainDC2, gainPI2; float alpha, beta0, beta1, rho; float wT, quad; wT = PI * F_c / F_s; gainPI2 = gainPI * gainPI; gainFT2 = gainFT * gainFT; gainDC2 = gainDC * gainDC; quad = gainPI2 + gainDC2 - (gainFT2*2); alpha = 0; if (quad != 0) { float lambda = (gainPI2 - gainDC2) / quad; alpha = (float)(lambda - Sgn(lambda)*sqrt(lambda*lambda - 1.0f)); } beta0 = 0.5f * ((gainDC + gainPI) + (gainDC - gainPI) * alpha); beta1 = 0.5f * ((gainDC - gainPI) + (gainDC + gainPI) * alpha); rho = (float)((sin((wT*0.5f) - (PI/4.0f))) / (sin((wT*0.5f) + (PI/4.0f)))); quad = 1.0f / (1.0f + rho*alpha); b0 = ((beta0 + rho*beta1) * quad); b1 = ((beta1 + rho*beta0) * quad); a1 = - ((rho + alpha) * quad); outA1 = mpt::saturate_round(a1 * scale); outB0 = mpt::saturate_round(b0 * scale); outB1 = mpt::saturate_round(b1 * scale); } CSurroundSettings::CSurroundSettings() : m_nProLogicDepth(12), m_nProLogicDelay(20) { } CMegaBassSettings::CMegaBassSettings() : m_nXBassDepth(DEFAULT_XBASS_DEPTH), m_nXBassRange(DEFAULT_XBASS_RANGE) { } CSurround::CSurround() { // Surround Encoding: 1 delay line + low-pass filter + high-pass filter nSurroundSize = 0; nSurroundPos = 0; nDolbyDepth = 0; // Surround Biquads nDolbyHP_Y1 = 0; nDolbyHP_X1 = 0; nDolbyLP_Y1 = 0; nDolbyHP_B0 = 0; nDolbyHP_B1 = 0; nDolbyHP_A1 = 0; nDolbyLP_B0 = 0; nDolbyLP_B1 = 0; nDolbyLP_A1 = 0; MemsetZero(SurroundBuffer); } CMegaBass::CMegaBass() { // Bass Expansion: low-pass filter nXBassFlt_Y1 = 0; nXBassFlt_X1 = 0; nXBassFlt_B0 = 0; nXBassFlt_B1 = 0; nXBassFlt_A1 = 0; // DC Removal Biquad nDCRFlt_Y1lf = 0; nDCRFlt_X1lf = 0; nDCRFlt_Y1rf = 0; nDCRFlt_X1rf = 0; nDCRFlt_Y1lb = 0; nDCRFlt_X1lb = 0; nDCRFlt_Y1rb = 0; nDCRFlt_X1rb = 0; } void CSurround::Initialize(bool bReset, DWORD MixingFreq) { MPT_UNREFERENCED_PARAMETER(bReset); if (!m_Settings.m_nProLogicDelay) m_Settings.m_nProLogicDelay = 20; // Pro-Logic Surround nSurroundPos = nSurroundSize = 0; { memset(SurroundBuffer, 0, sizeof(SurroundBuffer)); nSurroundSize = (MixingFreq * m_Settings.m_nProLogicDelay) / 1000; if (nSurroundSize > SURROUNDBUFFERSIZE) nSurroundSize = SURROUNDBUFFERSIZE; nDolbyDepth = m_Settings.m_nProLogicDepth; if (nDolbyDepth < 1) nDolbyDepth = 1; if (nDolbyDepth > 16) nDolbyDepth = 16; // Setup biquad filters ShelfEQ(1024, nDolbyHP_A1, nDolbyHP_B0, nDolbyHP_B1, 200, MixingFreq, 0, 0.5f, 1); ShelfEQ(1024, nDolbyLP_A1, nDolbyLP_B0, nDolbyLP_B1, 7000, MixingFreq, 1, 0.75f, 0); nDolbyHP_X1 = nDolbyHP_Y1 = 0; nDolbyLP_Y1 = 0; // Surround Level nDolbyHP_B0 = (nDolbyHP_B0 * nDolbyDepth) >> 5; nDolbyHP_B1 = (nDolbyHP_B1 * nDolbyDepth) >> 5; // +6dB nDolbyLP_B0 *= 2; nDolbyLP_B1 *= 2; } } void CMegaBass::Initialize(bool bReset, DWORD MixingFreq) { // Bass Expansion Reset { int32 a1 = 0, b0 = 1024, b1 = 0; int nXBassCutOff = 50 + (m_Settings.m_nXBassRange+2) * 20; int nXBassGain = m_Settings.m_nXBassDepth; nXBassGain = std::clamp(nXBassGain, 2, 8); nXBassCutOff = std::clamp(nXBassCutOff, 60, 600); ShelfEQ(1024, a1, b0, b1, nXBassCutOff, MixingFreq, 1.0f + (1.0f/16.0f) * (0x300 >> nXBassGain), 1.0f, 0.0000001f); if (nXBassGain > 5) { b0 >>= (nXBassGain-5); b1 >>= (nXBassGain-5); } nXBassFlt_A1 = a1; nXBassFlt_B0 = b0; nXBassFlt_B1 = b1; //Log("b0=%d b1=%d a1=%d\n", b0, b1, a1); } if (bReset) { nXBassFlt_X1 = 0; nXBassFlt_Y1 = 0; nDCRFlt_X1lf = 0; nDCRFlt_X1rf = 0; nDCRFlt_Y1lf = 0; nDCRFlt_Y1rf = 0; nDCRFlt_X1lb = 0; nDCRFlt_X1rb = 0; nDCRFlt_Y1lb = 0; nDCRFlt_Y1rb = 0; } } // 2-channel surround void CSurround::ProcessStereoSurround(int * MixSoundBuffer, int count) { int *pr = MixSoundBuffer, hy1 = nDolbyHP_Y1; for (int r=count; r; r--) { // Delay int secho = SurroundBuffer[nSurroundPos]; SurroundBuffer[nSurroundPos] = (pr[0]+pr[1]+256) >> 9; // High-pass int v0 = (nDolbyHP_B0 * secho + nDolbyHP_B1 * nDolbyHP_X1 + nDolbyHP_A1 * hy1) >> 10; nDolbyHP_X1 = secho; // Low-pass int v = (nDolbyLP_B0 * v0 + nDolbyLP_B1 * hy1 + nDolbyLP_A1 * nDolbyLP_Y1) >> (10-8); hy1 = v0; nDolbyLP_Y1 = v >> 8; // Add echo pr[0] += v; pr[1] -= v; if (++nSurroundPos >= nSurroundSize) nSurroundPos = 0; pr += 2; } nDolbyHP_Y1 = hy1; } // 4-channels surround void CSurround::ProcessQuadSurround(int * MixSoundBuffer, int * MixRearBuffer, int count) { int *pr = MixSoundBuffer, hy1 = nDolbyHP_Y1; for (int r=count; r; r--) { int vl = pr[0] >> 1; int vr = pr[1] >> 1; pr[(uint32)(MixRearBuffer-MixSoundBuffer)] += vl; pr[((uint32)(MixRearBuffer-MixSoundBuffer))+1] += vr; // Delay int secho = SurroundBuffer[nSurroundPos]; SurroundBuffer[nSurroundPos] = (vr+vl+256) >> 9; // High-pass int v0 = (nDolbyHP_B0 * secho + nDolbyHP_B1 * nDolbyHP_X1 + nDolbyHP_A1 * hy1) >> 10; nDolbyHP_X1 = secho; // Low-pass int v = (nDolbyLP_B0 * v0 + nDolbyLP_B1 * hy1 + nDolbyLP_A1 * nDolbyLP_Y1) >> (10-8); hy1 = v0; nDolbyLP_Y1 = v >> 8; // Add echo pr[(uint32)(MixRearBuffer-MixSoundBuffer)] += v; pr[((uint32)(MixRearBuffer-MixSoundBuffer))+1] += v; if (++nSurroundPos >= nSurroundSize) nSurroundPos = 0; pr += 2; } nDolbyHP_Y1 = hy1; } void CSurround::Process(int * MixSoundBuffer, int * MixRearBuffer, int count, uint32 nChannels) { if(nChannels >= 2) // Dolby Pro-Logic Surround { if (nChannels > 2) ProcessQuadSurround(MixSoundBuffer, MixRearBuffer, count); else ProcessStereoSurround(MixSoundBuffer, count); } } void CMegaBass::Process(int * MixSoundBuffer, int * MixRearBuffer, int count, uint32 nChannels) { if(nChannels >= 2) { X86_StereoDCRemoval(MixSoundBuffer, count, nDCRFlt_Y1lf, nDCRFlt_X1lf, nDCRFlt_Y1rf, nDCRFlt_X1rf); if(nChannels > 2) X86_StereoDCRemoval(MixRearBuffer, count, nDCRFlt_Y1lb, nDCRFlt_X1lb, nDCRFlt_Y1rb, nDCRFlt_X1rb); int *px = MixSoundBuffer; int *py = MixRearBuffer; int x1 = nXBassFlt_X1; int y1 = nXBassFlt_Y1; if(nChannels > 2) for (int x=count; x; x--) { int x_m = (px[0]+px[1]+py[0]+py[1]+0x100)>>9; y1 = (nXBassFlt_B0 * x_m + nXBassFlt_B1 * x1 + nXBassFlt_A1 * y1) >> (10-8); x1 = x_m; px[0] += y1; px[1] += y1; py[0] += y1; py[1] += y1; y1 = (y1+0x80) >> 8; px += 2; py += 2; } else for (int x=count; x; x--) { int x_m = (px[0]+px[1]+0x100)>>9; y1 = (nXBassFlt_B0 * x_m + nXBassFlt_B1 * x1 + nXBassFlt_A1 * y1) >> (10-8); x1 = x_m; px[0] += y1; px[1] += y1; y1 = (y1+0x80) >> 8; px += 2; } nXBassFlt_X1 = x1; nXBassFlt_Y1 = y1; } else { X86_MonoDCRemoval(MixSoundBuffer, count, nDCRFlt_Y1lf, nDCRFlt_X1lf); int *px = MixSoundBuffer; int x1 = nXBassFlt_X1; int y1 = nXBassFlt_Y1; for (int x=count; x; x--) { int x_m = (px[0]+0x80)>>8; y1 = (nXBassFlt_B0 * x_m + nXBassFlt_B1 * x1 + nXBassFlt_A1 * y1) >> (10-8); x1 = x_m; px[0] += y1; y1 = (y1+0x40) >> 8; px++; } nXBassFlt_X1 = x1; nXBassFlt_Y1 = y1; } } ////////////////////////////////////////////////////////////////////////// // // DC Removal // #define DCR_AMOUNT 9 static void X86_StereoDCRemoval(int *pBuffer, uint32 nSamples, int32 &nDCRFlt_Y1l, int32 &nDCRFlt_X1l, int32 &nDCRFlt_Y1r, int32 &nDCRFlt_X1r) { int y1l = nDCRFlt_Y1l, x1l = nDCRFlt_X1l; int y1r = nDCRFlt_Y1r, x1r = nDCRFlt_X1r; while(nSamples--) { int inL = pBuffer[0]; int inR = pBuffer[1]; int diffL = x1l - inL; int diffR = x1r - inR; x1l = inL; x1r = inR; int outL = diffL / (1 << (DCR_AMOUNT + 1)) - diffL + y1l; int outR = diffR / (1 << (DCR_AMOUNT + 1)) - diffR + y1r; pBuffer[0] = outL; pBuffer[1] = outR; pBuffer += 2; y1l = outL - outL / (1 << DCR_AMOUNT); y1r = outR - outR / (1 << DCR_AMOUNT); } nDCRFlt_Y1l = y1l; nDCRFlt_X1l = x1l; nDCRFlt_Y1r = y1r; nDCRFlt_X1r = x1r; } static void X86_MonoDCRemoval(int *pBuffer, uint32 nSamples, int32 &nDCRFlt_Y1l, int32 &nDCRFlt_X1l) { int y1l = nDCRFlt_Y1l, x1l = nDCRFlt_X1l; while(nSamples--) { int inM = pBuffer[0]; int diff = x1l - inM; x1l = inM; pBuffer[0] = inM = diff / (1 << (DCR_AMOUNT + 1)) - diff + y1l; pBuffer++; y1l = inM - inM / (1 << DCR_AMOUNT); } nDCRFlt_Y1l = y1l; nDCRFlt_X1l = x1l; } ///////////////////////////////////////////////////////////////// // Clean DSP Effects interface // [XBass level 0(quiet)-100(loud)], [cutoff in Hz 20-100] void CMegaBass::SetXBassParameters(uint32 nDepth, uint32 nRange) { if (nDepth > 100) nDepth = 100; uint32 gain = nDepth / 20; if (gain > 4) gain = 4; m_Settings.m_nXBassDepth = 8 - gain; // filter attenuation 1/256 .. 1/16 uint32 range = nRange / 5; if (range > 5) range -= 5; else range = 0; if (nRange > 16) nRange = 16; m_Settings.m_nXBassRange = 21 - range; // filter average on 0.5-1.6ms } // [Surround level 0(quiet)-100(heavy)] [delay in ms, usually 5-50ms] void CSurround::SetSurroundParameters(uint32 nDepth, uint32 nDelay) { uint32 gain = (nDepth * 16) / 100; if (gain > 16) gain = 16; if (gain < 1) gain = 1; m_Settings.m_nProLogicDepth = gain; if (nDelay < 4) nDelay = 4; if (nDelay > 50) nDelay = 50; m_Settings.m_nProLogicDelay = nDelay; } BitCrushSettings::BitCrushSettings() : m_Bits(8) { return; } BitCrush::BitCrush() { } void BitCrush::Initialize(bool bReset, DWORD MixingFreq) { MPT_UNREFERENCED_PARAMETER(bReset); MPT_UNREFERENCED_PARAMETER(MixingFreq); } void BitCrush::Process(int * MixSoundBuffer, int * MixRearBuffer, int count, uint32 nChannels) { if(m_Settings.m_Bits <= 0) { return; } if(m_Settings.m_Bits > MixSampleIntTraits::mix_precision_bits) { return; } unsigned int mask = ~((1u << (MixSampleIntTraits::mix_precision_bits - m_Settings.m_Bits)) - 1u); if(nChannels == 4) { for(int frame = 0; frame < count; ++frame) { MixSoundBuffer[frame*2 + 0] &= mask; MixSoundBuffer[frame*2 + 1] &= mask; MixRearBuffer[frame*2 + 0] &= mask; MixRearBuffer[frame*2 + 1] &= mask; } } else if(nChannels == 2) { for(int frame = 0; frame < count; ++frame) { MixSoundBuffer[frame*2 + 0] &= mask; MixSoundBuffer[frame*2 + 1] &= mask; } } else if(nChannels == 1) { for(int frame = 0; frame < count; ++frame) { MixSoundBuffer[frame] &= mask; } } } #else MPT_MSVC_WORKAROUND_LNK4221(DSP) #endif // NO_DSP OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/sounddsp/DSP.h0000644000175000017500000000562114052666041016722 00000000000000/* * DSP.h * ----- * Purpose: Mixing code for various DSPs (EQ, Mega-Bass, ...) * Notes : Ugh... This should really be removed at some point. * Authors: Olivier Lapicque * OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" OPENMPT_NAMESPACE_BEGIN #ifndef NO_DSP // Buffer Sizes #define SURROUNDBUFFERSIZE 2048 // 50ms @ 48kHz class CSurroundSettings { public: uint32 m_nProLogicDepth; uint32 m_nProLogicDelay; public: CSurroundSettings(); }; class CMegaBassSettings { public: uint32 m_nXBassDepth; uint32 m_nXBassRange; public: CMegaBassSettings(); }; struct BitCrushSettings { int m_Bits; BitCrushSettings(); }; class CSurround { public: CSurroundSettings m_Settings; // Surround Encoding: 1 delay line + low-pass filter + high-pass filter int32 nSurroundSize; int32 nSurroundPos; int32 nDolbyDepth; // Surround Biquads int32 nDolbyHP_Y1; int32 nDolbyHP_X1; int32 nDolbyLP_Y1; int32 nDolbyHP_B0; int32 nDolbyHP_B1; int32 nDolbyHP_A1; int32 nDolbyLP_B0; int32 nDolbyLP_B1; int32 nDolbyLP_A1; int32 SurroundBuffer[SURROUNDBUFFERSIZE]; public: CSurround(); public: void SetSettings(const CSurroundSettings &settings) { m_Settings = settings; } // [XBass level 0(quiet)-100(loud)], [cutoff in Hz 10-100] bool SetXBassParameters(uint32 nDepth, uint32 nRange); // [Surround level 0(quiet)-100(heavy)] [delay in ms, usually 5-40ms] void SetSurroundParameters(uint32 nDepth, uint32 nDelay); void Initialize(bool bReset, DWORD MixingFreq); void Process(int * MixSoundBuffer, int * MixRearBuffer, int count, uint32 nChannels); private: void ProcessStereoSurround(int * MixSoundBuffer, int count); void ProcessQuadSurround(int * MixSoundBuffer, int * MixRearBuffer, int count); }; class CMegaBass { public: CMegaBassSettings m_Settings; // Bass Expansion: low-pass filter int32 nXBassFlt_Y1; int32 nXBassFlt_X1; int32 nXBassFlt_B0; int32 nXBassFlt_B1; int32 nXBassFlt_A1; // DC Removal Biquad int32 nDCRFlt_Y1lf; int32 nDCRFlt_X1lf; int32 nDCRFlt_Y1rf; int32 nDCRFlt_X1rf; int32 nDCRFlt_Y1lb; int32 nDCRFlt_X1lb; int32 nDCRFlt_Y1rb; int32 nDCRFlt_X1rb; public: CMegaBass(); public: void SetSettings(const CMegaBassSettings &settings) { m_Settings = settings; } // [XBass level 0(quiet)-100(loud)], [cutoff in Hz 10-100] void SetXBassParameters(uint32 nDepth, uint32 nRange); void Initialize(bool bReset, DWORD MixingFreq); void Process(int * MixSoundBuffer, int * MixRearBuffer, int count, uint32 nChannels); }; class BitCrush { public: BitCrushSettings m_Settings; public: BitCrush(); public: void SetSettings(const BitCrushSettings &settings) { m_Settings = settings; } void Initialize(bool bReset, DWORD MixingFreq); void Process(int * MixSoundBuffer, int * MixRearBuffer, int count, uint32 nChannels); }; #endif // NO_DSP OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/sounddsp/EQ.cpp0000644000175000017500000001306214155422765017141 00000000000000/* * EQ.cpp * ------ * Purpose: Mixing code for equalizer. * Notes : Ugh... This should really be removed at some point. * Authors: Olivier Lapicque * OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "EQ.h" #include "mpt/audio/span.hpp" #include "mpt/base/numbers.hpp" #include "openmpt/base/Types.hpp" #include "openmpt/soundbase/MixSample.hpp" #include "openmpt/soundbase/MixSampleConvert.hpp" #ifndef NO_EQ #include "../misc/mptCPU.h" #endif #include #include #include #if defined(MPT_ENABLE_ARCH_INTRINSICS_SSE) #include #endif OPENMPT_NAMESPACE_BEGIN #ifndef NO_EQ static constexpr float EQ_BANDWIDTH = 2.0f; static constexpr std::array gEqLinearToDB = { 16, 19, 22, 25, 28, 31, 34, 37, 40, 43, 46, 49, 52, 55, 58, 61, 64, 76, 88, 100, 112, 124, 136, 148, 160, 172, 184, 196, 208, 220, 232, 244, 256 }; static constexpr std::array gEQDefaults = {{ // Default: Flat EQ {0,0,0,0,0, 1, 120}, {0,0,0,0,0, 1, 600}, {0,0,0,0,0, 1, 1200}, {0,0,0,0,0, 1, 3000}, {0,0,0,0,0, 1, 6000}, {0,0,0,0,0, 1, 10000} }}; template static void EQFilter(Tbuf & buf, const std::array &bands, std::array, MAX_EQ_CHANNELS> &states) { for(std::size_t frame = 0; frame < buf.size_frames(); ++frame) { for(std::size_t channel = 0; channel < channels; ++channel) { float sample = mix_sample_cast(buf(channel, frame)); for(std::size_t b = 0; b < std::size(bands); ++b) { const EQBANDSETTINGS &band = bands[b]; if(band.Gain != 1.0f) { EQBANDSTATE &bandState = states[channel][b]; float x = sample; float y = band.a1 * bandState.x1 + band.a2 * bandState.x2 + band.a0 * x + band.b1 * bandState.y1 + band.b2 * bandState.y2; bandState.x2 = bandState.x1; bandState.y2 = bandState.y1; bandState.x1 = x; bandState.y1 = y; sample = y; } } buf(channel, frame) = mix_sample_cast(sample); } } } template void CEQ::ProcessTemplate(TMixSample *frontBuffer, TMixSample *rearBuffer, std::size_t countFrames, std::size_t numChannels) { #if defined(MPT_ENABLE_ARCH_INTRINSICS_SSE) unsigned int old_csr = 0; if(CPU::HasFeatureSet(CPU::feature::sse)) { old_csr = _mm_getcsr(); _mm_setcsr((old_csr & ~(_MM_DENORMALS_ZERO_MASK | _MM_FLUSH_ZERO_MASK)) | _MM_DENORMALS_ZERO_ON | _MM_FLUSH_ZERO_ON); } #endif if(numChannels == 1) { mpt::audio_span_interleaved buf{ frontBuffer, 1, countFrames }; EQFilter<1>(buf, m_Bands, m_ChannelState); } else if(numChannels == 2) { mpt::audio_span_interleaved buf{ frontBuffer, 2, countFrames }; EQFilter<2>(buf, m_Bands, m_ChannelState); } else if(numChannels == 4) { std::array buffers = { &frontBuffer[0], &frontBuffer[1], &rearBuffer[0], &rearBuffer[1] }; mpt::audio_span_planar_strided buf{ buffers.data(), 4, countFrames, 2 }; EQFilter<4>(buf, m_Bands, m_ChannelState); } #if defined(MPT_ENABLE_ARCH_INTRINSICS_SSE) if(CPU::HasFeatureSet(CPU::feature::sse)) { _mm_setcsr(old_csr); } #endif } void CEQ::Process(MixSampleInt *frontBuffer, MixSampleInt *rearBuffer, std::size_t countFrames, std::size_t numChannels) { ProcessTemplate(frontBuffer, rearBuffer, countFrames, numChannels); } void CEQ::Process(MixSampleFloat *frontBuffer, MixSampleFloat *rearBuffer, std::size_t countFrames, std::size_t numChannels) { ProcessTemplate(frontBuffer, rearBuffer, countFrames, numChannels); } CEQ::CEQ() : m_Bands(gEQDefaults) { return; } void CEQ::Initialize(bool bReset, uint32 MixingFreq) { float fMixingFreq = static_cast(MixingFreq); // Gain = 0.5 (-6dB) .. 2 (+6dB) for(std::size_t band = 0; band < MAX_EQ_BANDS; ++band) { float k, k2, r, f; float v0, v1; bool b = bReset; f = m_Bands[band].CenterFrequency / fMixingFreq; if(f > 0.45f) { m_Bands[band].Gain = 1.0f; } k = f * mpt::numbers::pi_v; k = k + k*f; k2 = k*k; v0 = m_Bands[band].Gain; v1 = 1; if(m_Bands[band].Gain < 1.0f) { v0 *= (0.5f/EQ_BANDWIDTH); v1 *= (0.5f/EQ_BANDWIDTH); } else { v0 *= (1.0f/EQ_BANDWIDTH); v1 *= (1.0f/EQ_BANDWIDTH); } r = (1 + v0*k + k2) / (1 + v1*k + k2); if(r != m_Bands[band].a0) { m_Bands[band].a0 = r; b = true; } r = 2 * (k2 - 1) / (1 + v1*k + k2); if(r != m_Bands[band].a1) { m_Bands[band].a1 = r; b = true; } r = (1 - v0*k + k2) / (1 + v1*k + k2); if(r != m_Bands[band].a2) { m_Bands[band].a2 = r; b = true; } r = - 2 * (k2 - 1) / (1 + v1*k + k2); if(r != m_Bands[band].b1) { m_Bands[band].b1 = r; b = true; } r = - (1 - v1*k + k2) / (1 + v1*k + k2); if(r != m_Bands[band].b2) { m_Bands[band].b2 = r; b = true; } if(b) { for(std::size_t channel = 0; channel < MAX_EQ_CHANNELS; ++channel) { m_ChannelState[channel][band] = EQBANDSTATE{}; } } } } void CEQ::SetEQGains(const uint32 *pGains, const uint32 *pFreqs, bool bReset, uint32 MixingFreq) { for(std::size_t i = 0; i < MAX_EQ_BANDS; ++i) { m_Bands[i].Gain = static_cast(gEqLinearToDB[std::clamp(pGains[i], static_cast(0), static_cast(std::size(gEqLinearToDB) - 1))]) / 64.0f; m_Bands[i].CenterFrequency = static_cast(pFreqs[i]); } Initialize(bReset, MixingFreq); } #else MPT_MSVC_WORKAROUND_LNK4221(EQ) #endif // !NO_EQ OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/sounddsp/EQ.h0000644000175000017500000000302414056615252016576 00000000000000/* * EQ.h * ---- * Purpose: Mixing code for equalizer. * Notes : Ugh... This should really be removed at some point. * Authors: Olivier Lapicque * OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "openmpt/base/Types.hpp" #include "openmpt/soundbase/MixSample.hpp" #include #include OPENMPT_NAMESPACE_BEGIN #ifndef NO_EQ inline constexpr std::size_t MAX_EQ_CHANNELS = 4; inline constexpr std::size_t MAX_EQ_BANDS = 6; struct EQBANDSTATE { float x1 = 0.0f; float x2 = 0.0f; float y1 = 0.0f; float y2 = 0.0f; }; struct EQBANDSETTINGS { float a0; float a1; float a2; float b1; float b2; float Gain; float CenterFrequency; }; class CEQ { private: std::array, MAX_EQ_CHANNELS> m_ChannelState; std::array m_Bands; template void ProcessTemplate(TMixSample *frontBuffer, TMixSample *rearBuffer, std::size_t countFrames, std::size_t numChannels); public: CEQ(); void Initialize(bool bReset, uint32 MixingFreq); void Process(MixSampleInt *frontBuffer, MixSampleInt *rearBuffer, std::size_t countFrames, std::size_t numChannels); void Process(MixSampleFloat *frontBuffer, MixSampleFloat *rearBuffer, std::size_t countFrames, std::size_t numChannels); void SetEQGains(const uint32 *pGains, const uint32 *pFreqs, bool bReset, uint32 MixingFreq); }; #endif // !NO_EQ OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/sounddsp/Reverb.cpp0000644000175000017500000011730114155422765020062 00000000000000/* * Reverb.cpp * ---------- * Purpose: Mixing code for reverb. * Notes : Ugh... This should really be removed at some point. * Authors: Olivier Lapicque * OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #ifndef NO_REVERB #include "Reverb.h" #include "../soundlib/MixerLoops.h" #include "mpt/base/numbers.hpp" #if defined(MPT_ENABLE_ARCH_INTRINSICS_SSE2) #include #endif #endif // NO_REVERB OPENMPT_NAMESPACE_BEGIN #ifndef NO_REVERB #if defined(MPT_ENABLE_ARCH_INTRINSICS_SSE2) // Load two 32-bit values static MPT_FORCEINLINE __m128i Load64SSE(const int32 *x) { return _mm_loadl_epi64(reinterpret_cast(x)); } // Load four 16-bit values static MPT_FORCEINLINE __m128i Load64SSE(const LR16 (&x)[2]) { return _mm_loadl_epi64(&reinterpret_cast(x)); } // Store two 32-bit or four 16-bit values from register static MPT_FORCEINLINE void Store64SSE(int32 *dst, __m128i src) { return _mm_storel_epi64(reinterpret_cast<__m128i *>(dst), src); } static MPT_FORCEINLINE void Store64SSE(LR16 (&dst)[2], __m128i src) { return _mm_storel_epi64(&reinterpret_cast<__m128i &>(dst), src); } #endif CReverb::CReverb() { // Reverb mix buffers MemsetZero(g_RefDelay); MemsetZero(g_LateReverb); } static int32 OnePoleLowPassCoef(int32 scale, float g, float F_c, float F_s) { if(g > 0.999999f) return 0; g *= g; double scale_over_1mg = scale / (1.0 - g); double cosw = std::cos((2.0 * mpt::numbers::pi) * F_c / F_s); return mpt::saturate_round((1.0 - (std::sqrt((g + g) * (1.0 - cosw) - g * g * (1.0 - cosw * cosw)) + g * cosw)) * scale_over_1mg); } static float mBToLinear(int32 value_mB) { if(!value_mB) return 1; if(value_mB <= -100000) return 0; const double val = value_mB * 3.321928094887362304 / (100.0 * 20.0); // log2(10)/(100*20) return static_cast(std::pow(2.0, val - static_cast(0.5 + val))); } static int32 mBToLinear(int32 scale, int32 value_mB) { return mpt::saturate_round(mBToLinear(value_mB) * scale); } static constexpr std::pair ReverbPresets[NUM_REVERBTYPES] = { // Examples simulating General MIDI 2'musical' reverb presets // Name (Decay time) Description // Plate (1.3s) A plate reverb simulation. {{ -1000, -200, 1.30f,0.90f, 0,0.002f, 0,0.010f,100.0f, 75.0f }, "GM Plate"}, // Small Room (1.1s) A small size room with a length of 5m or so. {{ -1000, -600, 1.10f,0.83f, -400,0.005f, 500,0.010f,100.0f,100.0f }, "GM Small Room"}, // Medium Room (1.3s) A medium size room with a length of 10m or so. {{ -1000, -600, 1.30f,0.83f, -1000,0.010f, -200,0.020f,100.0f,100.0f }, "GM Medium Room"}, // Large Room (1.5s) A large size room suitable for live performances. {{ -1000, -600, 1.50f,0.83f, -1600,0.020f, -1000,0.040f,100.0f,100.0f }, "GM Large Room"}, // Medium Hall (1.8s) A medium size concert hall. {{ -1000, -600, 1.80f,0.70f, -1300,0.015f, -800,0.030f,100.0f,100.0f }, "GM Medium Hall"}, // Large Hall (1.8s) A large size concert hall suitable for a full orchestra. {{ -1000, -600, 1.80f,0.70f, -2000,0.030f, -1400,0.060f,100.0f,100.0f }, "GM Large Hall"}, {{ -1000, -100, 1.49f,0.83f, -2602,0.007f, 200,0.011f,100.0f,100.0f }, "Generic"}, {{ -1000,-6000, 0.17f,0.10f, -1204,0.001f, 207,0.002f,100.0f,100.0f }, "Padded Cell"}, {{ -1000, -454, 0.40f,0.83f, -1646,0.002f, 53,0.003f,100.0f,100.0f }, "Room"}, {{ -1000,-1200, 1.49f,0.54f, -370,0.007f, 1030,0.011f,100.0f, 60.0f }, "Bathroom"}, {{ -1000,-6000, 0.50f,0.10f, -1376,0.003f, -1104,0.004f,100.0f,100.0f }, "Living Room"}, {{ -1000, -300, 2.31f,0.64f, -711,0.012f, 83,0.017f,100.0f,100.0f }, "Stone Room"}, {{ -1000, -476, 4.32f,0.59f, -789,0.020f, -289,0.030f,100.0f,100.0f }, "Auditorium"}, {{ -1000, -500, 3.92f,0.70f, -1230,0.020f, -2,0.029f,100.0f,100.0f }, "Concert Hall"}, {{ -1000, 0, 2.91f,1.30f, -602,0.015f, -302,0.022f,100.0f,100.0f }, "Cave"}, {{ -1000, -698, 7.24f,0.33f, -1166,0.020f, 16,0.030f,100.0f,100.0f }, "Arena"}, {{ -1000,-1000,10.05f,0.23f, -602,0.020f, 198,0.030f,100.0f,100.0f }, "Hangar"}, {{ -1000,-4000, 0.30f,0.10f, -1831,0.002f, -1630,0.030f,100.0f,100.0f }, "Carpeted Hallway"}, {{ -1000, -300, 1.49f,0.59f, -1219,0.007f, 441,0.011f,100.0f,100.0f }, "Hallway"}, {{ -1000, -237, 2.70f,0.79f, -1214,0.013f, 395,0.020f,100.0f,100.0f }, "Stone Corridor"}, {{ -1000, -270, 1.49f,0.86f, -1204,0.007f, -4,0.011f,100.0f,100.0f }, "Alley"}, {{ -1000,-3300, 1.49f,0.54f, -2560,0.162f, -613,0.088f, 79.0f,100.0f }, "Forest"}, {{ -1000, -800, 1.49f,0.67f, -2273,0.007f, -2217,0.011f, 50.0f,100.0f }, "City"}, {{ -1000,-2500, 1.49f,0.21f, -2780,0.300f, -2014,0.100f, 27.0f,100.0f }, "Mountains"}, {{ -1000,-1000, 1.49f,0.83f,-10000,0.061f, 500,0.025f,100.0f,100.0f }, "Quarry"}, {{ -1000,-2000, 1.49f,0.50f, -2466,0.179f, -2514,0.100f, 21.0f,100.0f }, "Plain"}, {{ -1000, 0, 1.65f,1.50f, -1363,0.008f, -1153,0.012f,100.0f,100.0f }, "Parking Lot"}, {{ -1000,-1000, 2.81f,0.14f, 429,0.014f, 648,0.021f, 80.0f, 60.0f }, "Sewer Pipe"}, {{ -1000,-4000, 1.49f,0.10f, -449,0.007f, 1700,0.011f,100.0f,100.0f }, "Underwater"}, }; mpt::ustring GetReverbPresetName(uint32 preset) { return (preset < NUM_REVERBTYPES) ? mpt::ToUnicode(mpt::Charset::ASCII, ReverbPresets[preset].second) : mpt::ustring{}; } const SNDMIX_REVERB_PROPERTIES *GetReverbPreset(uint32 preset) { return (preset < NUM_REVERBTYPES) ? &ReverbPresets[preset].first : nullptr; } ////////////////////////////////////////////////////////////////////////// // // I3DL2 environmental reverb support // struct REFLECTIONPRESET { int32 lDelayFactor; int16 sGainLL, sGainRR, sGainLR, sGainRL; }; const REFLECTIONPRESET gReflectionsPreset[ENVIRONMENT_NUMREFLECTIONS] = { // %Delay, ll, rr, lr, rl {0, 9830, 6554, 0, 0}, {10, 6554, 13107, 0, 0}, {24, -9830, 13107, 0, 0}, {36, 13107, -6554, 0, 0}, {54, 16384, 16384, -1638, -1638}, {61, -13107, 8192, -328, -328}, {73, -11468, -11468, -3277, 3277}, {87, 13107, -9830, 4916, -4916} }; //////////////////////////////////////////////////////////////////////////////////// // // Implementation // MPT_FORCEINLINE int32 ftol(float f) { return static_cast(f); } static void I3dl2_to_Generic( const SNDMIX_REVERB_PROPERTIES *pReverb, EnvironmentReverb *pRvb, float flOutputFreq, int32 lMinRefDelay, int32 lMaxRefDelay, int32 lMinRvbDelay, int32 lMaxRvbDelay, int32 lTankLength) { float flDelayFactor, flDelayFactorHF, flDecayTimeHF; int32 lDensity, lTailDiffusion; // Common parameters pRvb->ReverbLevel = pReverb->lReverb; pRvb->ReflectionsLevel = pReverb->lReflections; pRvb->RoomHF = pReverb->lRoomHF; // HACK: Somewhat normalize the reverb output level int32 lMaxLevel = (pRvb->ReverbLevel > pRvb->ReflectionsLevel) ? pRvb->ReverbLevel : pRvb->ReflectionsLevel; if (lMaxLevel < -600) { lMaxLevel += 600; pRvb->ReverbLevel -= lMaxLevel; pRvb->ReflectionsLevel -= lMaxLevel; } // Pre-Diffusion factor (for both reflections and late reverb) lDensity = 8192 + ftol(79.31f * pReverb->flDensity); pRvb->PreDiffusion = lDensity; // Late reverb diffusion lTailDiffusion = ftol((0.15f + pReverb->flDiffusion * (0.36f*0.01f)) * 32767.0f); if (lTailDiffusion > 0x7f00) lTailDiffusion = 0x7f00; pRvb->TankDiffusion = lTailDiffusion; // Verify reflections and reverb delay parameters float flRefDelay = pReverb->flReflectionsDelay; if (flRefDelay > 0.100f) flRefDelay = 0.100f; int32 lReverbDelay = ftol(pReverb->flReverbDelay * flOutputFreq); int32 lReflectionsDelay = ftol(flRefDelay * flOutputFreq); int32 lReverbDecayTime = ftol(pReverb->flDecayTime * flOutputFreq); if (lReflectionsDelay < lMinRefDelay) { lReverbDelay -= (lMinRefDelay - lReflectionsDelay); lReflectionsDelay = lMinRefDelay; } if (lReflectionsDelay > lMaxRefDelay) { lReverbDelay += (lReflectionsDelay - lMaxRefDelay); lReflectionsDelay = lMaxRefDelay; } // Adjust decay time when adjusting reverb delay if (lReverbDelay < lMinRvbDelay) { lReverbDecayTime -= (lMinRvbDelay - lReverbDelay); lReverbDelay = lMinRvbDelay; } if (lReverbDelay > lMaxRvbDelay) { lReverbDecayTime += (lReverbDelay - lMaxRvbDelay); lReverbDelay = lMaxRvbDelay; } pRvb->ReverbDelay = lReverbDelay; pRvb->ReverbDecaySamples = lReverbDecayTime; // Setup individual reflections delay and gains for (uint32 iRef=0; iRefReflections[iRef]; ref.Delay = lReflectionsDelay + (gReflectionsPreset[iRef].lDelayFactor * lReverbDelay + 50)/100; ref.GainLL = gReflectionsPreset[iRef].sGainLL; ref.GainRL = gReflectionsPreset[iRef].sGainRL; ref.GainLR = gReflectionsPreset[iRef].sGainLR; ref.GainRR = gReflectionsPreset[iRef].sGainRR; } // Late reverb decay time if (lTankLength < 10) lTankLength = 10; flDelayFactor = (lReverbDecayTime <= lTankLength) ? 1.0f : ((float)lTankLength / (float)lReverbDecayTime); pRvb->ReverbDecay = ftol(std::pow(0.001f, flDelayFactor) * 32768.0f); // Late Reverb Decay HF flDecayTimeHF = (float)lReverbDecayTime * pReverb->flDecayHFRatio; flDelayFactorHF = (flDecayTimeHF <= (float)lTankLength) ? 1.0f : ((float)lTankLength / flDecayTimeHF); pRvb->flReverbDamping = std::pow(0.001f, flDelayFactorHF); } void CReverb::Shutdown(MixSampleInt &gnRvbROfsVol, MixSampleInt &gnRvbLOfsVol) { gnReverbSend = false; gnRvbLOfsVol = 0; gnRvbROfsVol = 0; // Clear out all reverb state g_bLastInPresent = false; g_bLastOutPresent = false; g_nLastRvbIn_xl = g_nLastRvbIn_xr = 0; g_nLastRvbIn_yl = g_nLastRvbIn_yr = 0; g_nLastRvbOut_xl = g_nLastRvbOut_xr = 0; MemsetZero(gnDCRRvb_X1); MemsetZero(gnDCRRvb_Y1); // Zero internal buffers MemsetZero(g_LateReverb.Diffusion1); MemsetZero(g_LateReverb.Diffusion2); MemsetZero(g_LateReverb.Delay1); MemsetZero(g_LateReverb.Delay2); MemsetZero(g_RefDelay.RefDelayBuffer); MemsetZero(g_RefDelay.PreDifBuffer); MemsetZero(g_RefDelay.RefOut); } void CReverb::Initialize(bool bReset, MixSampleInt &gnRvbROfsVol, MixSampleInt &gnRvbLOfsVol, uint32 MixingFreq) { if (m_Settings.m_nReverbType >= NUM_REVERBTYPES) m_Settings.m_nReverbType = 0; const SNDMIX_REVERB_PROPERTIES *rvbPreset = &ReverbPresets[m_Settings.m_nReverbType].first; if ((rvbPreset != m_currentPreset) || (bReset)) { // Reverb output frequency is half of the dry output rate float flOutputFrequency = (float)MixingFreq; EnvironmentReverb rvb; // Reset reverb parameters m_currentPreset = rvbPreset; I3dl2_to_Generic(rvbPreset, &rvb, flOutputFrequency, RVBMINREFDELAY, RVBMAXREFDELAY, RVBMINRVBDELAY, RVBMAXRVBDELAY, ( RVBDIF1L_LEN + RVBDIF1R_LEN + RVBDIF2L_LEN + RVBDIF2R_LEN + RVBDLY1L_LEN + RVBDLY1R_LEN + RVBDLY2L_LEN + RVBDLY2R_LEN) / 2); // Store reverb decay time (in samples) for reverb auto-shutdown gnReverbDecaySamples = rvb.ReverbDecaySamples; // Room attenuation at high frequencies int32 nRoomLP; nRoomLP = OnePoleLowPassCoef(32768, mBToLinear(rvb.RoomHF), 5000, flOutputFrequency); g_RefDelay.nCoeffs.c.l = (int16)nRoomLP; g_RefDelay.nCoeffs.c.r = (int16)nRoomLP; // Pre-Diffusion factor (for both reflections and late reverb) g_RefDelay.nPreDifCoeffs.c.l = (int16)(rvb.PreDiffusion*2); g_RefDelay.nPreDifCoeffs.c.r = (int16)(rvb.PreDiffusion*2); // Setup individual reflections delay and gains for (uint32 iRef=0; iRef<8; iRef++) { SWRvbReflection &ref = g_RefDelay.Reflections[iRef]; ref.DelayDest = rvb.Reflections[iRef].Delay; ref.Delay = ref.DelayDest; ref.Gains[0].c.l = rvb.Reflections[iRef].GainLL; ref.Gains[0].c.r = rvb.Reflections[iRef].GainRL; ref.Gains[1].c.l = rvb.Reflections[iRef].GainLR; ref.Gains[1].c.r = rvb.Reflections[iRef].GainRR; } g_LateReverb.nReverbDelay = rvb.ReverbDelay; // Reflections Master Gain uint32 lReflectionsGain = 0; if (rvb.ReflectionsLevel > -9000) { lReflectionsGain = mBToLinear(32768, rvb.ReflectionsLevel); } g_RefDelay.lMasterGain = lReflectionsGain; // Late reverb master gain uint32 lReverbGain = 0; if (rvb.ReverbLevel > -9000) { lReverbGain = mBToLinear(32768, rvb.ReverbLevel); } g_LateReverb.lMasterGain = lReverbGain; // Late reverb diffusion uint32 nTailDiffusion = rvb.TankDiffusion; if (nTailDiffusion > 0x7f00) nTailDiffusion = 0x7f00; g_LateReverb.nDifCoeffs[0].c.l = (int16)nTailDiffusion; g_LateReverb.nDifCoeffs[0].c.r = (int16)nTailDiffusion; g_LateReverb.nDifCoeffs[1].c.l = (int16)nTailDiffusion; g_LateReverb.nDifCoeffs[1].c.r = (int16)nTailDiffusion; g_LateReverb.Dif2InGains[0].c.l = 0x7000; g_LateReverb.Dif2InGains[0].c.r = 0x1000; g_LateReverb.Dif2InGains[1].c.l = 0x1000; g_LateReverb.Dif2InGains[1].c.r = 0x7000; // Late reverb decay time int32 nReverbDecay = rvb.ReverbDecay; Limit(nReverbDecay, 0, 0x7ff0); g_LateReverb.nDecayDC[0].c.l = (int16)nReverbDecay; g_LateReverb.nDecayDC[0].c.r = 0; g_LateReverb.nDecayDC[1].c.l = 0; g_LateReverb.nDecayDC[1].c.r = (int16)nReverbDecay; // Late Reverb Decay HF float fReverbDamping = rvb.flReverbDamping * rvb.flReverbDamping; int32 nDampingLowPass; nDampingLowPass = OnePoleLowPassCoef(32768, fReverbDamping, 5000, flOutputFrequency); Limit(nDampingLowPass, 0x100, 0x7f00); g_LateReverb.nDecayLP[0].c.l = (int16)nDampingLowPass; g_LateReverb.nDecayLP[0].c.r = 0; g_LateReverb.nDecayLP[1].c.l = 0; g_LateReverb.nDecayLP[1].c.r = (int16)nDampingLowPass; } if (bReset) { gnReverbSamples = 0; Shutdown(gnRvbROfsVol, gnRvbLOfsVol); } // Wait at least 5 seconds before shutting down the reverb if (gnReverbDecaySamples < MixingFreq*5) { gnReverbDecaySamples = MixingFreq*5; } } void CReverb::TouchReverbSendBuffer(MixSampleInt *MixReverbBuffer, MixSampleInt &gnRvbROfsVol, MixSampleInt &gnRvbLOfsVol, uint32 nSamples) { if(!gnReverbSend) { // and we did not clear the buffer yet, do it now because we will get new data StereoFill(MixReverbBuffer, nSamples, gnRvbROfsVol, gnRvbLOfsVol); } gnReverbSend = true; // we will have to process reverb } // Reverb void CReverb::Process(MixSampleInt *MixSoundBuffer, MixSampleInt *MixReverbBuffer, MixSampleInt &gnRvbROfsVol, MixSampleInt &gnRvbLOfsVol, uint32 nSamples) { if((!gnReverbSend) && (!gnReverbSamples)) { // no data is sent to reverb and reverb decayed completely return; } if(!gnReverbSend) { // no input data in MixReverbBuffer, so the buffer got not cleared in TouchReverbSendBuffer(), do it now for decay StereoFill(MixReverbBuffer, nSamples, gnRvbROfsVol, gnRvbLOfsVol); } uint32 nIn, nOut; // Dynamically adjust reverb master gains int32 lMasterGain; lMasterGain = ((g_RefDelay.lMasterGain * m_Settings.m_nReverbDepth) >> 4); if (lMasterGain > 0x7fff) lMasterGain = 0x7fff; g_RefDelay.ReflectionsGain.c.l = (int16)lMasterGain; g_RefDelay.ReflectionsGain.c.r = (int16)lMasterGain; lMasterGain = ((g_LateReverb.lMasterGain * m_Settings.m_nReverbDepth) >> 4); if (lMasterGain > 0x10000) lMasterGain = 0x10000; g_LateReverb.RvbOutGains[0].c.l = (int16)((lMasterGain+0x7f) >> 3); // l->l g_LateReverb.RvbOutGains[0].c.r = (int16)((lMasterGain+0xff) >> 4); // r->l g_LateReverb.RvbOutGains[1].c.l = (int16)((lMasterGain+0xff) >> 4); // l->r g_LateReverb.RvbOutGains[1].c.r = (int16)((lMasterGain+0x7f) >> 3); // r->r // Process Dry/Wet Mix int32 lMaxRvbGain = (g_RefDelay.lMasterGain > g_LateReverb.lMasterGain) ? g_RefDelay.lMasterGain : g_LateReverb.lMasterGain; if (lMaxRvbGain > 32768) lMaxRvbGain = 32768; int32 lDryVol = (36 - m_Settings.m_nReverbDepth)>>1; if (lDryVol < 8) lDryVol = 8; if (lDryVol > 16) lDryVol = 16; lDryVol = 16 - (((16-lDryVol) * lMaxRvbGain) >> 15); ReverbDryMix(MixSoundBuffer, MixReverbBuffer, lDryVol, nSamples); // Downsample 2x + 1st stage of lowpass filter nIn = ReverbProcessPreFiltering1x(MixReverbBuffer, nSamples); nOut = nIn; // Main reverb processing: split into small chunks (needed for short reverb delays) // Reverb Input + Low-Pass stage #2 + Pre-diffusion if (nIn > 0) ProcessPreDelay(&g_RefDelay, MixReverbBuffer, nIn); // Process Reverb Reflections and Late Reverberation int32 *pRvbOut = MixReverbBuffer; uint32 nRvbSamples = nOut; while (nRvbSamples > 0) { uint32 nPosRef = g_RefDelay.nRefOutPos & SNDMIX_REVERB_DELAY_MASK; uint32 nPosRvb = (nPosRef - g_LateReverb.nReverbDelay) & SNDMIX_REVERB_DELAY_MASK; uint32 nmax1 = (SNDMIX_REVERB_DELAY_MASK+1) - nPosRef; uint32 nmax2 = (SNDMIX_REVERB_DELAY_MASK+1) - nPosRvb; nmax1 = (nmax1 < nmax2) ? nmax1 : nmax2; uint32 n = nRvbSamples; if (n > nmax1) n = nmax1; if (n > 64) n = 64; // Reflections output + late reverb delay ProcessReflections(&g_RefDelay, &g_RefDelay.RefOut[nPosRef], pRvbOut, n); // Late Reverberation ProcessLateReverb(&g_LateReverb, &g_RefDelay.RefOut[nPosRvb], pRvbOut, n); // Update delay positions g_RefDelay.nRefOutPos = (g_RefDelay.nRefOutPos + n) & SNDMIX_REVERB_DELAY_MASK; g_RefDelay.nDelayPos = (g_RefDelay.nDelayPos + n) & SNDMIX_REFLECTIONS_DELAY_MASK; pRvbOut += n*2; nRvbSamples -= n; } // Adjust nDelayPos, in case nIn != nOut g_RefDelay.nDelayPos = (g_RefDelay.nDelayPos - nOut + nIn) & SNDMIX_REFLECTIONS_DELAY_MASK; // Upsample 2x ReverbProcessPostFiltering1x(MixReverbBuffer, MixSoundBuffer, nSamples); // Automatically shut down if needed if(gnReverbSend) gnReverbSamples = gnReverbDecaySamples; // reset decay counter else if(gnReverbSamples > nSamples) gnReverbSamples -= nSamples; // decay else // decayed { Shutdown(gnRvbROfsVol, gnRvbLOfsVol); gnReverbSamples = 0; } gnReverbSend = false; // no input data in MixReverbBuffer } void CReverb::ReverbDryMix(int32 * MPT_RESTRICT pDry, int32 * MPT_RESTRICT pWet, int lDryVol, uint32 nSamples) { for (uint32 i=0; i>4) * lDryVol; pDry[i*2+1] += (pWet[i*2+1]>>4) * lDryVol; } } uint32 CReverb::ReverbProcessPreFiltering2x(int32 * MPT_RESTRICT pWet, uint32 nSamples) { uint32 nOutSamples = 0; int lowpass = g_RefDelay.nCoeffs.c.l; int y1_l = g_nLastRvbIn_yl, y1_r = g_nLastRvbIn_yr; uint32 n = nSamples; if (g_bLastInPresent) { int x1_l = g_nLastRvbIn_xl, x1_r = g_nLastRvbIn_xr; int x2_l = pWet[0], x2_r = pWet[1]; x1_l = (x1_l+x2_l)>>13; x1_r = (x1_r+x2_r)>>13; y1_l = x1_l + (((x1_l - y1_l)*lowpass)>>15); y1_r = x1_r + (((x1_r - y1_r)*lowpass)>>15); pWet[0] = y1_l; pWet[1] = y1_r; pWet+=2; n--; nOutSamples = 1; g_bLastInPresent = false; } if (n & 1) { n--; g_nLastRvbIn_xl = pWet[n*2]; g_nLastRvbIn_xr = pWet[n*2+1]; g_bLastInPresent = true; } n >>= 1; for (uint32 i=0; i>13; int x1_r = pWet[i*4+1]; int x2_r = pWet[i*4+3]; x1_r = (x1_r+x2_r)>>13; y1_l = x1_l + (((x1_l - y1_l)*lowpass)>>15); y1_r = x1_r + (((x1_r - y1_r)*lowpass)>>15); pWet[i*2] = y1_l; pWet[i*2+1] = y1_r; } g_nLastRvbIn_yl = y1_l; g_nLastRvbIn_yr = y1_r; return nOutSamples + n; } uint32 CReverb::ReverbProcessPreFiltering1x(int32 * MPT_RESTRICT pWet, uint32 nSamples) { int lowpass = g_RefDelay.nCoeffs.c.l; int y1_l = g_nLastRvbIn_yl, y1_r = g_nLastRvbIn_yr; for (uint32 i=0; i> 12; int x_r = pWet[i*2+1] >> 12; y1_l = x_l + (((x_l - y1_l)*lowpass)>>15); y1_r = x_r + (((x_r - y1_r)*lowpass)>>15); pWet[i*2] = y1_l; pWet[i*2+1] = y1_r; } g_nLastRvbIn_yl = y1_l; g_nLastRvbIn_yr = y1_r; return nSamples; } void CReverb::ReverbProcessPostFiltering2x(const int32 * MPT_RESTRICT pRvb, int32 * MPT_RESTRICT pDry, uint32 nSamples) { uint32 n0 = nSamples, n; int x1_l = g_nLastRvbOut_xl, x1_r = g_nLastRvbOut_xr; if (g_bLastOutPresent) { pDry[0] += x1_l; pDry[1] += x1_r; pDry += 2; n0--; g_bLastOutPresent = false; } n = n0 >> 1; for (uint32 i=0; i>1; pDry[i*4+1] += (x_r + x1_r)>>1; pDry[i*4+2] += x_l; pDry[i*4+3] += x_r; x1_l = x_l; x1_r = x_r; } if (n0 & 1) { int x_l = pRvb[n*2], x_r = pRvb[n*2+1]; pDry[n*4] += (x_l + x1_l)>>1; pDry[n*4+1] += (x_r + x1_r)>>1; x1_l = x_l; x1_r = x_r; g_bLastOutPresent = true; } g_nLastRvbOut_xl = x1_l; g_nLastRvbOut_xr = x1_r; } #define DCR_AMOUNT 9 // Stereo Add + DC removal void CReverb::ReverbProcessPostFiltering1x(const int32 * MPT_RESTRICT pRvb, int32 * MPT_RESTRICT pDry, uint32 nSamples) { #if defined(MPT_ENABLE_ARCH_INTRINSICS_SSE2) if(CPU::HasFeatureSet(CPU::feature::sse2)) { __m128i nDCRRvb_Y1 = Load64SSE(gnDCRRvb_Y1); __m128i nDCRRvb_X1 = Load64SSE(gnDCRRvb_X1); __m128i in = _mm_set1_epi32(0); while(nSamples--) { in = Load64SSE(pRvb); pRvb += 2; // x(n-1) - x(n) __m128i diff = _mm_sub_epi32(nDCRRvb_X1, in); nDCRRvb_X1 = _mm_add_epi32(nDCRRvb_Y1, _mm_sub_epi32(_mm_srai_epi32(diff, DCR_AMOUNT + 1), diff)); __m128i out = _mm_add_epi32(Load64SSE(pDry), nDCRRvb_X1); nDCRRvb_Y1 = _mm_sub_epi32(nDCRRvb_X1, _mm_srai_epi32(nDCRRvb_X1, DCR_AMOUNT)); nDCRRvb_X1 = in; Store64SSE(pDry, out); pDry += 2; } Store64SSE(gnDCRRvb_X1, in); Store64SSE(gnDCRRvb_Y1, nDCRRvb_Y1); return; } #endif int32 X1L = gnDCRRvb_X1[0], X1R = gnDCRRvb_X1[1]; int32 Y1L = gnDCRRvb_Y1[0], Y1R = gnDCRRvb_Y1[1]; int32 inL = 0, inR = 0; while(nSamples--) { inL = pRvb[0]; inR = pRvb[1]; pRvb += 2; int32 outL = pDry[0], outR = pDry[1]; // x(n-1) - x(n) X1L -= inL; X1R -= inR; X1L = X1L / (1 << (DCR_AMOUNT + 1)) - X1L; X1R = X1R / (1 << (DCR_AMOUNT + 1)) - X1R; Y1L += X1L; Y1R += X1R; // add to dry mix outL += Y1L; outR += Y1R; Y1L -= Y1L / (1 << DCR_AMOUNT); Y1R -= Y1R / (1 << DCR_AMOUNT); X1L = inL; X1R = inR; pDry[0] = outL; pDry[1] = outR; pDry += 2; } gnDCRRvb_Y1[0] = Y1L; gnDCRRvb_Y1[1] = Y1R; gnDCRRvb_X1[0] = inL; gnDCRRvb_X1[1] = inR; } void CReverb::ReverbDCRemoval(int32 * MPT_RESTRICT pBuffer, uint32 nSamples) { #if defined(MPT_ENABLE_ARCH_INTRINSICS_SSE2) if(CPU::HasFeatureSet(CPU::feature::sse2)) { __m128i nDCRRvb_Y1 = Load64SSE(gnDCRRvb_Y1); __m128i nDCRRvb_X1 = Load64SSE(gnDCRRvb_X1); while(nSamples--) { __m128i in = Load64SSE(pBuffer); __m128i diff = _mm_sub_epi32(nDCRRvb_X1, in); __m128i out = _mm_add_epi32(nDCRRvb_Y1, _mm_sub_epi32(_mm_srai_epi32(diff, DCR_AMOUNT + 1), diff)); Store64SSE(pBuffer, out); pBuffer += 2; nDCRRvb_Y1 = _mm_sub_epi32(out, _mm_srai_epi32(out, DCR_AMOUNT)); nDCRRvb_X1 = in; } Store64SSE(gnDCRRvb_X1, nDCRRvb_X1); Store64SSE(gnDCRRvb_Y1, nDCRRvb_Y1); return; } #endif int32 X1L = gnDCRRvb_X1[0], X1R = gnDCRRvb_X1[1]; int32 Y1L = gnDCRRvb_Y1[0], Y1R = gnDCRRvb_Y1[1]; int32 inL = 0, inR = 0; while(nSamples--) { inL = pBuffer[0]; inR = pBuffer[1]; // x(n-1) - x(n) X1L -= inL; X1R -= inR; X1L = X1L / (1 << (DCR_AMOUNT + 1)) - X1L; X1R = X1R / (1 << (DCR_AMOUNT + 1)) - X1R; Y1L += X1L; Y1R += X1R; pBuffer[0] = Y1L; pBuffer[1] = Y1R; pBuffer += 2; Y1L -= Y1L / (1 << DCR_AMOUNT); Y1R -= Y1R / (1 << DCR_AMOUNT); X1L = inL; X1R = inR; } gnDCRRvb_Y1[0] = Y1L; gnDCRRvb_Y1[1] = Y1R; gnDCRRvb_X1[0] = inL; gnDCRRvb_X1[1] = inR; } ////////////////////////////////////////////////////////////////////////// // // Pre-Delay: // // 1. Saturate and low-pass the reverb input (stage 2 of roomHF) // 2. Process pre-diffusion // 3. Insert the result in the reflections delay buffer // // Save some typing static MPT_FORCEINLINE int32 Clamp16(int32 x) { return Clamp(x, std::numeric_limits::min(), std::numeric_limits::max()); } void CReverb::ProcessPreDelay(SWRvbRefDelay * MPT_RESTRICT pPreDelay, const int32 * MPT_RESTRICT pIn, uint32 nSamples) { uint32 preDifPos = pPreDelay->nPreDifPos; uint32 delayPos = pPreDelay->nDelayPos - 1; #if defined(MPT_ENABLE_ARCH_INTRINSICS_SSE2) if(CPU::HasFeatureSet(CPU::feature::sse2)) { __m128i coeffs = _mm_cvtsi32_si128(pPreDelay->nCoeffs.lr); __m128i history = _mm_cvtsi32_si128(pPreDelay->History.lr); __m128i preDifCoeffs = _mm_cvtsi32_si128(pPreDelay->nPreDifCoeffs.lr); while(nSamples--) { __m128i in32 = Load64SSE(pIn); // 16-bit unsaturated reverb input [ r | l ] __m128i inSat = _mm_packs_epi32(in32, in32); // [ r | l | r | l ] (16-bit saturated) pIn += 2; // Low-pass __m128i lp = _mm_mulhi_epi16(_mm_subs_epi16(history, inSat), coeffs); __m128i preDif = _mm_cvtsi32_si128(pPreDelay->PreDifBuffer[preDifPos].lr); history = _mm_adds_epi16(_mm_adds_epi16(lp, lp), inSat); // Pre-Diffusion preDifPos = (preDifPos + 1) & SNDMIX_PREDIFFUSION_DELAY_MASK; delayPos = (delayPos + 1) & SNDMIX_REFLECTIONS_DELAY_MASK; __m128i preDif2 = _mm_subs_epi16(history, _mm_mulhi_epi16(preDif, preDifCoeffs)); pPreDelay->PreDifBuffer[preDifPos].lr = _mm_cvtsi128_si32(preDif2); pPreDelay->RefDelayBuffer[delayPos].lr = _mm_cvtsi128_si32(_mm_adds_epi16(_mm_mulhi_epi16(preDifCoeffs, preDif2), preDif)); } pPreDelay->nPreDifPos = preDifPos; pPreDelay->History.lr = _mm_cvtsi128_si32(history); return; } #endif const int32 coeffsL = pPreDelay->nCoeffs.c.l, coeffsR = pPreDelay->nCoeffs.c.r; const int32 preDifCoeffsL = pPreDelay->nPreDifCoeffs.c.l, preDifCoeffsR = pPreDelay->nPreDifCoeffs.c.r; int16 historyL = pPreDelay->History.c.l, historyR = pPreDelay->History.c.r; while(nSamples--) { int32 inL = Clamp16(pIn[0]); int32 inR = Clamp16(pIn[1]); pIn += 2; // Low-pass int32 lpL = (Clamp16(historyL - inL) * coeffsL) / 65536; int32 lpR = (Clamp16(historyR - inR) * coeffsR) / 65536; historyL = mpt::saturate_cast(Clamp16(lpL + lpL) + inL); historyR = mpt::saturate_cast(Clamp16(lpR + lpR) + inR); // Pre-Diffusion int32 preDifL = pPreDelay->PreDifBuffer[preDifPos].c.l; int32 preDifR = pPreDelay->PreDifBuffer[preDifPos].c.r; preDifPos = (preDifPos + 1) & SNDMIX_PREDIFFUSION_DELAY_MASK; delayPos = (delayPos + 1) & SNDMIX_REFLECTIONS_DELAY_MASK; int16 preDif2L = mpt::saturate_cast(historyL - preDifL * preDifCoeffsL / 65536); int16 preDif2R = mpt::saturate_cast(historyR - preDifR * preDifCoeffsR / 65536); pPreDelay->PreDifBuffer[preDifPos].c.l = preDif2L; pPreDelay->PreDifBuffer[preDifPos].c.r = preDif2R; pPreDelay->RefDelayBuffer[delayPos].c.l = mpt::saturate_cast(preDifCoeffsL * preDif2L / 65536 + preDifL); pPreDelay->RefDelayBuffer[delayPos].c.r = mpt::saturate_cast(preDifCoeffsR * preDif2R / 65536 + preDifR); } pPreDelay->nPreDifPos = preDifPos; pPreDelay->History.c.l = historyL; pPreDelay->History.c.r = historyR; } //////////////////////////////////////////////////////////////////// // // ProcessReflections: // First stage: // - process 4 reflections, output to pRefOut // - output results to pRefOut // Second stage: // - process another 3 reflections // - sum with pRefOut // - apply reflections master gain and accumulate in the given output // void CReverb::ProcessReflections(SWRvbRefDelay * MPT_RESTRICT pPreDelay, LR16 * MPT_RESTRICT pRefOut, int32 * MPT_RESTRICT pOut, uint32 nSamples) { #if defined(MPT_ENABLE_ARCH_INTRINSICS_SSE2) if(CPU::HasFeatureSet(CPU::feature::sse2)) { union { __m128i xmm; int16 i[8]; } pos; const LR16 *refDelayBuffer = pPreDelay->RefDelayBuffer; #define GETDELAY(x) static_cast(pPreDelay->Reflections[x].Delay) __m128i delayPos = _mm_set_epi16(GETDELAY(7), GETDELAY(6), GETDELAY(5), GETDELAY(4), GETDELAY(3), GETDELAY(2), GETDELAY(1), GETDELAY(0)); #undef GETDELAY delayPos = _mm_sub_epi16(_mm_set1_epi16(static_cast(pPreDelay->nDelayPos - 1)), delayPos); __m128i gain12 = _mm_unpacklo_epi64(Load64SSE(pPreDelay->Reflections[0].Gains), Load64SSE(pPreDelay->Reflections[1].Gains)); __m128i gain34 = _mm_unpacklo_epi64(Load64SSE(pPreDelay->Reflections[2].Gains), Load64SSE(pPreDelay->Reflections[3].Gains)); __m128i gain56 = _mm_unpacklo_epi64(Load64SSE(pPreDelay->Reflections[4].Gains), Load64SSE(pPreDelay->Reflections[5].Gains)); __m128i gain78 = _mm_unpacklo_epi64(Load64SSE(pPreDelay->Reflections[6].Gains), Load64SSE(pPreDelay->Reflections[7].Gains)); // For 28-bit final output: 16+15-3 = 28 __m128i refGain = _mm_srai_epi32(_mm_set_epi32(0, 0, pPreDelay->ReflectionsGain.c.r, pPreDelay->ReflectionsGain.c.l), 3); __m128i delayInc = _mm_set1_epi16(1), delayMask = _mm_set1_epi16(SNDMIX_REFLECTIONS_DELAY_MASK); while(nSamples--) { delayPos = _mm_and_si128(_mm_add_epi16(delayInc, delayPos), delayMask); _mm_storeu_si128(&pos.xmm, delayPos); __m128i ref12 = _mm_set_epi32(refDelayBuffer[pos.i[1]].lr, refDelayBuffer[pos.i[1]].lr, refDelayBuffer[pos.i[0]].lr, refDelayBuffer[pos.i[0]].lr); __m128i ref34 = _mm_set_epi32(refDelayBuffer[pos.i[3]].lr, refDelayBuffer[pos.i[3]].lr, refDelayBuffer[pos.i[2]].lr, refDelayBuffer[pos.i[2]].lr); __m128i ref56 = _mm_set_epi32(refDelayBuffer[pos.i[5]].lr, refDelayBuffer[pos.i[5]].lr, refDelayBuffer[pos.i[4]].lr, refDelayBuffer[pos.i[4]].lr); __m128i ref78 = _mm_set_epi32(0, 0, refDelayBuffer[pos.i[6]].lr, refDelayBuffer[pos.i[6]].lr); // First stage __m128i refOut1 = _mm_add_epi32(_mm_madd_epi16(ref12, gain12), _mm_madd_epi16(ref34, gain34)); refOut1 = _mm_srai_epi32(_mm_add_epi32(refOut1, _mm_shuffle_epi32(refOut1, _MM_SHUFFLE(1, 0, 3, 2))), 15); // Second stage __m128i refOut2 = _mm_add_epi32(_mm_madd_epi16(ref56, gain56), _mm_madd_epi16(ref78, gain78)); refOut2 = _mm_srai_epi32(_mm_add_epi32(refOut2, _mm_shuffle_epi32(refOut2, _MM_SHUFFLE(1, 0, 3, 2))), 15); // Saturate to 16-bit and sum stages __m128i refOut = _mm_adds_epi16(_mm_packs_epi32(refOut1, refOut1), _mm_packs_epi32(refOut2, refOut2)); pRefOut->lr = _mm_cvtsi128_si32(refOut); pRefOut++; __m128i out = _mm_madd_epi16(_mm_unpacklo_epi16(refOut, refOut), refGain); // Apply reflections gain // At this, point, this is the only output of the reverb Store64SSE(pOut, out); pOut += 2; } return; } #endif int pos[7]; for(int i = 0; i < 7; i++) pos[i] = pPreDelay->nDelayPos - pPreDelay->Reflections[i].Delay - 1; // For 28-bit final output: 16+15-3 = 28 int16 refGain = pPreDelay->ReflectionsGain.c.l / (1 << 3); while(nSamples--) { // First stage int32 refOutL = 0, refOutR = 0; for(int i = 0; i < 4; i++) { pos[i] = (pos[i] + 1) & SNDMIX_REFLECTIONS_DELAY_MASK; int16 refL = pPreDelay->RefDelayBuffer[pos[i]].c.l, refR = pPreDelay->RefDelayBuffer[pos[i]].c.r; refOutL += refL * pPreDelay->Reflections[i].Gains[0].c.l + refR * pPreDelay->Reflections[i].Gains[0].c.r; refOutR += refL * pPreDelay->Reflections[i].Gains[1].c.l + refR * pPreDelay->Reflections[i].Gains[1].c.r; } int16 stage1l = mpt::saturate_cast(refOutL / (1 << 15)); int16 stage1r = mpt::saturate_cast(refOutR / (1 << 15)); // Second stage refOutL = 0; refOutR = 0; for(int i = 4; i < 7; i++) { pos[i] = (pos[i] + 1) & SNDMIX_REFLECTIONS_DELAY_MASK; int16 refL = pPreDelay->RefDelayBuffer[pos[i]].c.l, refR = pPreDelay->RefDelayBuffer[pos[i]].c.r; refOutL += refL * pPreDelay->Reflections[i].Gains[0].c.l + refR * pPreDelay->Reflections[i].Gains[0].c.r; refOutR += refL * pPreDelay->Reflections[i].Gains[1].c.l + refR * pPreDelay->Reflections[i].Gains[1].c.r; } pOut[0] = (pRefOut->c.l = mpt::saturate_cast(stage1l + refOutL / (1 << 15))) * refGain; pOut[1] = (pRefOut->c.r = mpt::saturate_cast(stage1r + refOutR / (1 << 15))) * refGain; pRefOut++; pOut += 2; } } ////////////////////////////////////////////////////////////////////////// // // Late reverberation (with SW reflections) // void CReverb::ProcessLateReverb(SWLateReverb * MPT_RESTRICT pReverb, LR16 * MPT_RESTRICT pRefOut, int32 * MPT_RESTRICT pMixOut, uint32 nSamples) { // Calculate delay line offset from current delay position #define DELAY_OFFSET(x) ((delayPos - (x)) & RVBDLY_MASK) #if defined(MPT_ENABLE_ARCH_INTRINSICS_SSE2) if(CPU::HasFeatureSet(CPU::feature::sse2)) { int delayPos = pReverb->nDelayPos & RVBDLY_MASK; __m128i rvbOutGains = Load64SSE(pReverb->RvbOutGains); __m128i difCoeffs = Load64SSE(pReverb->nDifCoeffs); __m128i decayLP = Load64SSE(pReverb->nDecayLP); __m128i lpHistory = Load64SSE(pReverb->LPHistory); while(nSamples--) { __m128i refIn = _mm_cvtsi32_si128(pRefOut->lr); // 16-bit stereo input pRefOut++; __m128i delay2 = _mm_unpacklo_epi32( _mm_cvtsi32_si128(pReverb->Delay2[DELAY_OFFSET(RVBDLY2L_LEN)].lr), _mm_cvtsi32_si128(pReverb->Delay2[DELAY_OFFSET(RVBDLY2R_LEN)].lr)); // Unsigned to avoid sign extension uint16 diff1L = pReverb->Diffusion1[DELAY_OFFSET(RVBDIF1L_LEN)].c.l; uint16 diff1R = pReverb->Diffusion1[DELAY_OFFSET(RVBDIF1R_LEN)].c.r; int32 diffusion1 = diff1L | (diff1R << 16); // diffusion1 history uint16 diff2L = pReverb->Diffusion2[DELAY_OFFSET(RVBDIF2L_LEN)].c.l; uint16 diff2R = pReverb->Diffusion2[DELAY_OFFSET(RVBDIF2R_LEN)].c.r; int32 diffusion2 = diff2L | (diff2R << 16); // diffusion2 history __m128i lpDecay = _mm_mulhi_epi16(_mm_subs_epi16(lpHistory, delay2), decayLP); lpHistory = _mm_adds_epi16(_mm_adds_epi16(lpDecay, lpDecay), delay2); // Low-passed decay // Apply decay gain __m128i histDecay = _mm_srai_epi32(_mm_madd_epi16(Load64SSE(pReverb->nDecayDC), lpHistory), 15); __m128i histDecayPacked = _mm_shuffle_epi32(_mm_packs_epi32(histDecay, histDecay), _MM_SHUFFLE(2, 0, 2, 0)); __m128i histDecayIn = _mm_adds_epi16(_mm_shuffle_epi32(_mm_packs_epi32(histDecay, histDecay), _MM_SHUFFLE(2, 0, 2, 0)), _mm_srai_epi16(_mm_unpacklo_epi32(refIn, refIn), 2)); __m128i histDecayInDiff = _mm_subs_epi16(histDecayIn, _mm_mulhi_epi16(_mm_cvtsi32_si128(diffusion1), difCoeffs)); pReverb->Diffusion1[delayPos].lr = _mm_cvtsi128_si32(histDecayInDiff); __m128i delay1Out = _mm_adds_epi16(_mm_mulhi_epi16(difCoeffs, histDecayInDiff), _mm_cvtsi32_si128(diffusion1)); // Insert the diffusion output in the reverb delay line pReverb->Delay1[delayPos].lr = _mm_cvtsi128_si32(delay1Out); __m128i histDecayInDelay = _mm_adds_epi16(histDecayIn, _mm_unpacklo_epi32(delay1Out, delay1Out)); // Input to second diffuser __m128i delay1 = _mm_unpacklo_epi32( _mm_cvtsi32_si128(pReverb->Delay1[DELAY_OFFSET(RVBDLY1L_LEN)].lr), _mm_cvtsi32_si128(pReverb->Delay1[DELAY_OFFSET(RVBDLY1R_LEN)].lr)); __m128i delay1Gains = _mm_srai_epi32(_mm_madd_epi16(delay1, Load64SSE(pReverb->Dif2InGains)), 15); __m128i delay1GainsSat = _mm_shuffle_epi32(_mm_packs_epi32(delay1Gains, delay1Gains), _MM_SHUFFLE(2, 0, 2, 0)); __m128i histDelay1 = _mm_subs_epi16(_mm_adds_epi16(histDecayInDelay, delay1), delay1GainsSat); // accumulate with reverb output __m128i diff2out = _mm_subs_epi16(delay1GainsSat, _mm_mulhi_epi16(_mm_cvtsi32_si128(diffusion2), difCoeffs)); __m128i diff2outCoeffs = _mm_mulhi_epi16(difCoeffs, diff2out); pReverb->Diffusion2[delayPos].lr = _mm_cvtsi128_si32(diff2out); __m128i mixOut = Load64SSE(pMixOut); __m128i delay2out = _mm_adds_epi16(diff2outCoeffs, _mm_cvtsi32_si128(diffusion2)); pReverb->Delay2[delayPos].lr = _mm_cvtsi128_si32(delay2out); delayPos = (delayPos + 1) & RVBDLY_MASK; // Accumulate with reverb output __m128i out = _mm_add_epi32(_mm_madd_epi16(_mm_adds_epi16(histDelay1, delay2out), rvbOutGains), mixOut); Store64SSE(pMixOut, out); pMixOut += 2; } Store64SSE(pReverb->LPHistory, lpHistory); pReverb->nDelayPos = delayPos; return; } #endif int delayPos = pReverb->nDelayPos & RVBDLY_MASK; while(nSamples--) { int16 refInL = pRefOut->c.l, refInR = pRefOut->c.r; pRefOut++; int32 delay2LL = pReverb->Delay2[DELAY_OFFSET(RVBDLY2L_LEN)].c.l, delay2LR = pReverb->Delay2[DELAY_OFFSET(RVBDLY2L_LEN)].c.r; int32 delay2RL = pReverb->Delay2[DELAY_OFFSET(RVBDLY2R_LEN)].c.l, delay2RR = pReverb->Delay2[DELAY_OFFSET(RVBDLY2R_LEN)].c.r; int32 diff1L = pReverb->Diffusion1[DELAY_OFFSET(RVBDIF1L_LEN)].c.l; int32 diff1R = pReverb->Diffusion1[DELAY_OFFSET(RVBDIF1R_LEN)].c.r; int32 diff2L = pReverb->Diffusion2[DELAY_OFFSET(RVBDIF2L_LEN)].c.l; int32 diff2R = pReverb->Diffusion2[DELAY_OFFSET(RVBDIF2R_LEN)].c.r; int32 lpDecayLL = Clamp16(pReverb->LPHistory[0].c.l - delay2LL) * pReverb->nDecayLP[0].c.l / 65536; int32 lpDecayLR = Clamp16(pReverb->LPHistory[0].c.r - delay2LR) * pReverb->nDecayLP[0].c.r / 65536; int32 lpDecayRL = Clamp16(pReverb->LPHistory[1].c.l - delay2RL) * pReverb->nDecayLP[1].c.l / 65536; int32 lpDecayRR = Clamp16(pReverb->LPHistory[1].c.r - delay2RR) * pReverb->nDecayLP[1].c.r / 65536; // Low-passed decay pReverb->LPHistory[0].c.l = mpt::saturate_cast(Clamp16(lpDecayLL + lpDecayLL) + delay2LL); pReverb->LPHistory[0].c.r = mpt::saturate_cast(Clamp16(lpDecayLR + lpDecayLR) + delay2LR); pReverb->LPHistory[1].c.l = mpt::saturate_cast(Clamp16(lpDecayRL + lpDecayRL) + delay2RL); pReverb->LPHistory[1].c.r = mpt::saturate_cast(Clamp16(lpDecayRR + lpDecayRR) + delay2RR); // Apply decay gain int32 histDecayL = Clamp16((int32)pReverb->nDecayDC[0].c.l * pReverb->LPHistory[0].c.l / (1 << 15)); int32 histDecayR = Clamp16((int32)pReverb->nDecayDC[1].c.r * pReverb->LPHistory[1].c.r / (1 << 15)); int32 histDecayInL = Clamp16(histDecayL + refInL / 4); int32 histDecayInR = Clamp16(histDecayR + refInR / 4); int32 histDecayInDiffL = Clamp16(histDecayInL - diff1L * pReverb->nDifCoeffs[0].c.l / 65536); int32 histDecayInDiffR = Clamp16(histDecayInR - diff1R * pReverb->nDifCoeffs[0].c.r / 65536); pReverb->Diffusion1[delayPos].c.l = static_cast(histDecayInDiffL); pReverb->Diffusion1[delayPos].c.r = static_cast(histDecayInDiffR); int32 delay1L = Clamp16(pReverb->nDifCoeffs[0].c.l * histDecayInDiffL / 65536 + diff1L); int32 delay1R = Clamp16(pReverb->nDifCoeffs[0].c.r * histDecayInDiffR / 65536 + diff1R); // Insert the diffusion output in the reverb delay line pReverb->Delay1[delayPos].c.l = static_cast(delay1L); pReverb->Delay1[delayPos].c.r = static_cast(delay1R); int32 histDecayInDelayL = Clamp16(histDecayInL + delay1L); int32 histDecayInDelayR = Clamp16(histDecayInR + delay1R); // Input to second diffuser int32 delay1LL = pReverb->Delay1[DELAY_OFFSET(RVBDLY1L_LEN)].c.l, delay1LR = pReverb->Delay1[DELAY_OFFSET(RVBDLY1L_LEN)].c.r; int32 delay1RL = pReverb->Delay1[DELAY_OFFSET(RVBDLY1R_LEN)].c.l, delay1RR = pReverb->Delay1[DELAY_OFFSET(RVBDLY1R_LEN)].c.r; int32 delay1GainsL = Clamp16((delay1LL * pReverb->Dif2InGains[0].c.l + delay1LR * pReverb->Dif2InGains[0].c.r) / (1 << 15)); int32 delay1GainsR = Clamp16((delay1RL * pReverb->Dif2InGains[1].c.l + delay1RR * pReverb->Dif2InGains[1].c.r) / (1 << 15)); // accumulate with reverb output int32 histDelay1LL = Clamp16(Clamp16(histDecayInDelayL + delay1LL) - delay1GainsL); int32 histDelay1LR = Clamp16(Clamp16(histDecayInDelayR + delay1LR) - delay1GainsR); int32 histDelay1RL = Clamp16(Clamp16(histDecayInDelayL + delay1RL) - delay1GainsL); int32 histDelay1RR = Clamp16(Clamp16(histDecayInDelayR + delay1RR) - delay1GainsR); int32 diff2outL = Clamp16(delay1GainsL - diff2L * pReverb->nDifCoeffs[0].c.l / 65536); int32 diff2outR = Clamp16(delay1GainsR - diff2R * pReverb->nDifCoeffs[0].c.r / 65536); int32 diff2outCoeffsL = pReverb->nDifCoeffs[0].c.l * diff2outL / 65536; int32 diff2outCoeffsR = pReverb->nDifCoeffs[0].c.r * diff2outR / 65536; pReverb->Diffusion2[delayPos].c.l = static_cast(diff2outL); pReverb->Diffusion2[delayPos].c.r = static_cast(diff2outR); int32 delay2outL = Clamp16(diff2outCoeffsL + diff2L); int32 delay2outR = Clamp16(diff2outCoeffsR + diff2R); pReverb->Delay2[delayPos].c.l = static_cast(delay2outL); pReverb->Delay2[delayPos].c.r = static_cast(delay2outR); delayPos = (delayPos + 1) & RVBDLY_MASK; // Accumulate with reverb output pMixOut[0] += Clamp16(histDelay1LL + delay2outL) * pReverb->RvbOutGains[0].c.l + Clamp16(histDelay1LR + delay2outR) * pReverb->RvbOutGains[0].c.r; pMixOut[1] += Clamp16(histDelay1RL + Clamp16(diff2outCoeffsL)) * pReverb->RvbOutGains[1].c.l + Clamp16(histDelay1RR + Clamp16(diff2outCoeffsR)) * pReverb->RvbOutGains[1].c.r; pMixOut += 2; } pReverb->nDelayPos = delayPos; #undef DELAY_OFFSET } #else MPT_MSVC_WORKAROUND_LNK4221(Reverb) #endif // NO_REVERB OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/sounddsp/Reverb.h0000644000175000017500000001577114072325166017532 00000000000000/* * Reverb.h * -------- * Purpose: Mixing code for reverb. * Notes : Ugh... This should really be removed at some point. * Authors: Olivier Lapicque * OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #ifndef NO_REVERB #include "../soundlib/Mixer.h" // For MIXBUFFERSIZE OPENMPT_NAMESPACE_BEGIN //////////////////////////////////////////////////////////////////////// // Reverberation ///////////////////////////////////////////////////////////////////////////// // // SW Reverb structures // // Length-1 (in samples) of the reflections delay buffer: 32K, 371ms@22kHz #define SNDMIX_REFLECTIONS_DELAY_MASK 0x1fff #define SNDMIX_PREDIFFUSION_DELAY_MASK 0x7f // 128 samples #define SNDMIX_REVERB_DELAY_MASK 0xfff // 4K samples (92ms @ 44kHz) union LR16 { struct { int16 l, r; } c; int32 lr; }; struct SWRvbReflection { uint32 Delay, DelayDest; LR16 Gains[2]; // g_ll, g_rl, g_lr, g_rr }; struct SWRvbRefDelay { uint32 nDelayPos, nPreDifPos, nRefOutPos; int32 lMasterGain; // reflections linear master gain LR16 nCoeffs; // room low-pass coefficients LR16 History; // room low-pass history LR16 nPreDifCoeffs; // prediffusion coefficients LR16 ReflectionsGain; // master reflections gain SWRvbReflection Reflections[8]; // Up to 8 SW Reflections LR16 RefDelayBuffer[SNDMIX_REFLECTIONS_DELAY_MASK + 1]; // reflections delay buffer LR16 PreDifBuffer[SNDMIX_PREDIFFUSION_DELAY_MASK + 1]; // pre-diffusion LR16 RefOut[SNDMIX_REVERB_DELAY_MASK + 1]; // stereo output of reflections }; struct SNDMIX_REVERB_PROPERTIES; // Late reverberation // Tank diffusers lengths #define RVBDIF1L_LEN (149*2) // 6.8ms #define RVBDIF1R_LEN (223*2) // 10.1ms #define RVBDIF2L_LEN (421*2) // 19.1ms #define RVBDIF2R_LEN (647*2) // 29.3ms // Tank delay lines lengths #define RVBDLY1L_LEN (683*2) // 30.9ms #define RVBDLY1R_LEN (811*2) // 36.7ms #define RVBDLY2L_LEN (773*2) // 35.1ms #define RVBDLY2R_LEN (1013*2) // 45.9ms // Tank delay lines mask #define RVBDLY_MASK 2047 // Min/Max reflections delay #define RVBMINREFDELAY 96 // 96 samples #define RVBMAXREFDELAY 7500 // 7500 samples // Min/Max reverb delay #define RVBMINRVBDELAY 128 // 256 samples (11.6ms @ 22kHz) #define RVBMAXRVBDELAY 3800 // 1900 samples (86ms @ 24kHz) struct SWLateReverb { uint32 nReverbDelay; // Reverb delay (in samples) uint32 nDelayPos; // Delay line position LR16 nDifCoeffs[2]; // Reverb diffusion LR16 nDecayDC[2]; // Reverb DC decay LR16 nDecayLP[2]; // Reverb HF decay LR16 LPHistory[2]; // Low-pass history LR16 Dif2InGains[2]; // 2nd diffuser input gains LR16 RvbOutGains[2]; // 4x2 Reverb output gains int32 lMasterGain; // late reverb master gain int32 lDummyAlign; // Tank Delay lines LR16 Diffusion1[RVBDLY_MASK + 1]; // {dif1_l, dif1_r} LR16 Diffusion2[RVBDLY_MASK + 1]; // {dif2_l, dif2_r} LR16 Delay1[RVBDLY_MASK + 1]; // {dly1_l, dly1_r} LR16 Delay2[RVBDLY_MASK + 1]; // {dly2_l, dly2_r} }; #define ENVIRONMENT_NUMREFLECTIONS 8 struct EnvironmentReflection { int16 GainLL, GainRR, GainLR, GainRL; // +/- 32K scale uint32 Delay; // In samples }; struct EnvironmentReverb { int32 ReverbLevel; // Late reverb gain (mB) int32 ReflectionsLevel; // Master reflections gain (mB) int32 RoomHF; // Room gain HF (mB) uint32 ReverbDecay; // Reverb tank decay (0-7fff scale) int32 PreDiffusion; // Reverb pre-diffusion amount (+/- 32K scale) int32 TankDiffusion; // Reverb tank diffusion (+/- 32K scale) uint32 ReverbDelay; // Reverb delay (in samples) float flReverbDamping; // HF tank gain [0.0, 1.0] int32 ReverbDecaySamples; // Reverb decay time (in samples) EnvironmentReflection Reflections[ENVIRONMENT_NUMREFLECTIONS]; }; class CReverbSettings { public: uint32 m_nReverbDepth = 8; // 50% uint32 m_nReverbType = 0; }; class CReverb { public: CReverbSettings m_Settings; private: const SNDMIX_REVERB_PROPERTIES *m_currentPreset = nullptr; bool gnReverbSend = false; uint32 gnReverbSamples = 0; uint32 gnReverbDecaySamples = 0; // Internal reverb state bool g_bLastInPresent = 0; bool g_bLastOutPresent = 0; int g_nLastRvbIn_xl = 0; int g_nLastRvbIn_xr = 0; int g_nLastRvbIn_yl = 0; int g_nLastRvbIn_yr = 0; int g_nLastRvbOut_xl = 0; int g_nLastRvbOut_xr = 0; int32 gnDCRRvb_Y1[2] = { 0, 0 }; int32 gnDCRRvb_X1[2] = { 0, 0 }; // Reverb mix buffers SWRvbRefDelay g_RefDelay; SWLateReverb g_LateReverb; public: CReverb(); public: void Initialize(bool bReset, MixSampleInt &gnRvbROfsVol, MixSampleInt &gnRvbLOfsVol, uint32 MixingFreq); // can be called multiple times or never (if no data is sent to reverb) void TouchReverbSendBuffer(MixSampleInt *MixReverbBuffer, MixSampleInt &gnRvbROfsVol, MixSampleInt &gnRvbLOfsVol, uint32 nSamples); // call once after all data has been sent. void Process(MixSampleInt *MixSoundBuffer, MixSampleInt *MixReverbBuffer, MixSampleInt &gnRvbROfsVol, MixSampleInt &gnRvbLOfsVol, uint32 nSamples); private: void Shutdown(MixSampleInt &gnRvbROfsVol, MixSampleInt &gnRvbLOfsVol); // Pre/Post resampling and filtering uint32 ReverbProcessPreFiltering1x(int32 *pWet, uint32 nSamples); uint32 ReverbProcessPreFiltering2x(int32 *pWet, uint32 nSamples); void ReverbProcessPostFiltering1x(const int32 *pRvb, int32 *pDry, uint32 nSamples); void ReverbProcessPostFiltering2x(const int32 *pRvb, int32 *pDry, uint32 nSamples); void ReverbDCRemoval(int32 *pBuffer, uint32 nSamples); void ReverbDryMix(int32 *pDry, int32 *pWet, int lDryVol, uint32 nSamples); // Process pre-diffusion and pre-delay static void ProcessPreDelay(SWRvbRefDelay *pPreDelay, const int32 *pIn, uint32 nSamples); // Process reflections static void ProcessReflections(SWRvbRefDelay *pPreDelay, LR16 *pRefOut, int32 *pMixOut, uint32 nSamples); // Process Late Reverb (SW Reflections): stereo reflections output, 32-bit reverb output, SW reverb gain static void ProcessLateReverb(SWLateReverb *pReverb, LR16 *pRefOut, int32 *pMixOut, uint32 nSamples); }; ///////////////////////////////////////////////////////////////////////////////// // // I3DL2 reverb presets // struct SNDMIX_REVERB_PROPERTIES { int32 lRoom; // [-10000, 0] default: -10000 mB int32 lRoomHF; // [-10000, 0] default: 0 mB float flDecayTime; // [0.1, 20.0] default: 1.0 s float flDecayHFRatio; // [0.1, 2.0] default: 0.5 int32 lReflections; // [-10000, 1000] default: -10000 mB float flReflectionsDelay; // [0.0, 0.3] default: 0.02 s int32 lReverb; // [-10000, 2000] default: -10000 mB float flReverbDelay; // [0.0, 0.1] default: 0.04 s float flDiffusion; // [0.0, 100.0] default: 100.0 % float flDensity; // [0.0, 100.0] default: 100.0 % }; enum : uint32 { NUM_REVERBTYPES = 29 }; mpt::ustring GetReverbPresetName(uint32 preset); const SNDMIX_REVERB_PROPERTIES *GetReverbPreset(uint32 preset); OPENMPT_NAMESPACE_END #endif // NO_REVERB libopenmpt-0.6.1+release.autotools/soundlib/0000755000175000017500000000000014175541576016172 500000000000000libopenmpt-0.6.1+release.autotools/soundlib/plugins/0000755000175000017500000000000014175541576017653 500000000000000libopenmpt-0.6.1+release.autotools/soundlib/plugins/dmo/0000755000175000017500000000000014175541576020432 500000000000000libopenmpt-0.6.1+release.autotools/soundlib/plugins/dmo/DMOPlugin.cpp0000644000175000017500000002571614155206341022651 00000000000000/* * DMOPlugin.h * ----------- * Purpose: DirectX Media Object plugin handling / processing. * Notes : Some default plugins only have the same output characteristics in the floating point code path (compared to integer PCM) * if we feed them input in the range [-32768, +32768] rather than the more usual [-1, +1]. * Hence, OpenMPT uses this range for both the floating-point and integer path. * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "mpt/base/aligned_array.hpp" #if defined(MPT_WITH_DMO) #include "mpt/uuid/guid.hpp" #include "../../Sndfile.h" #include "DMOPlugin.h" #include "../PluginManager.h" #include #include #include #endif // MPT_WITH_DMO OPENMPT_NAMESPACE_BEGIN #if defined(MPT_WITH_DMO) #ifdef MPT_ALL_LOGGING #define DMO_LOG #else #define DMO_LOG #endif IMixPlugin* DMOPlugin::Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct) { CLSID clsid; if(mpt::VerifyStringToCLSID(factory.dllPath.AsNative(), clsid)) { IMediaObject *pMO = nullptr; IMediaObjectInPlace *pMOIP = nullptr; if ((CoCreateInstance(clsid, nullptr, CLSCTX_INPROC_SERVER, IID_IMediaObject, (VOID **)&pMO) == S_OK) && (pMO)) { if (pMO->QueryInterface(IID_IMediaObjectInPlace, (void **)&pMOIP) != S_OK) pMOIP = nullptr; } else pMO = nullptr; if ((pMO) && (pMOIP)) { DWORD dwInputs = 0, dwOutputs = 0; pMO->GetStreamCount(&dwInputs, &dwOutputs); if (dwInputs == 1 && dwOutputs == 1) { DMOPlugin *p = new (std::nothrow) DMOPlugin(factory, sndFile, mixStruct, pMO, pMOIP, clsid.Data1); return p; } #ifdef DMO_LOG MPT_LOG_GLOBAL(LogDebug, "DMO", factory.libraryName.ToUnicode() + U_(": Unable to use this DMO")); #endif } #ifdef DMO_LOG else MPT_LOG_GLOBAL(LogDebug, "DMO", factory.libraryName.ToUnicode() + U_(": Failed to get IMediaObject & IMediaObjectInPlace interfaces")); #endif if (pMO) pMO->Release(); if (pMOIP) pMOIP->Release(); } return nullptr; } DMOPlugin::DMOPlugin(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct, IMediaObject *pMO, IMediaObjectInPlace *pMOIP, uint32 uid) : IMixPlugin(factory, sndFile, mixStruct) , m_pMediaObject(pMO) , m_pMediaProcess(pMOIP) , m_pParamInfo(nullptr) , m_pMediaParams(nullptr) , m_nSamplesPerSec(sndFile.GetSampleRate()) , m_uid(uid) { if(FAILED(m_pMediaObject->QueryInterface(IID_IMediaParamInfo, (void **)&m_pParamInfo))) m_pParamInfo = nullptr; if (FAILED(m_pMediaObject->QueryInterface(IID_IMediaParams, (void **)&m_pMediaParams))) m_pMediaParams = nullptr; m_alignedBuffer.f32 = mpt::align_bytes<16, MIXBUFFERSIZE * 2>(m_interleavedBuffer.f32); m_mixBuffer.Initialize(2, 2); InsertIntoFactoryList(); } DMOPlugin::~DMOPlugin() { if(m_pMediaParams) { m_pMediaParams->Release(); m_pMediaParams = nullptr; } if(m_pParamInfo) { m_pParamInfo->Release(); m_pParamInfo = nullptr; } if(m_pMediaProcess) { m_pMediaProcess->Release(); m_pMediaProcess = nullptr; } if(m_pMediaObject) { m_pMediaObject->Release(); m_pMediaObject = nullptr; } } uint32 DMOPlugin::GetLatency() const { REFERENCE_TIME time; // Unit 100-nanoseconds if(m_pMediaProcess->GetLatency(&time) == S_OK) { return static_cast(time * m_nSamplesPerSec / (10 * 1000 * 1000)); } return 0; } static constexpr float _f2si = 32768.0f; static constexpr float _si2f = 1.0f / 32768.0f; static void InterleaveStereo(const float * MPT_RESTRICT inputL, const float * MPT_RESTRICT inputR, float * MPT_RESTRICT output, uint32 numFrames) { while(numFrames--) { *(output++) = *(inputL++) * _f2si; *(output++) = *(inputR++) * _f2si; } } static void DeinterleaveStereo(const float * MPT_RESTRICT input, float * MPT_RESTRICT outputL, float * MPT_RESTRICT outputR, uint32 numFrames) { while(numFrames--) { *(outputL++) = *(input++) * _si2f; *(outputR++) = *(input++) * _si2f; } } // Interleave two float streams into one int16 stereo stream. static void InterleaveFloatToInt16(const float * MPT_RESTRICT inputL, const float * MPT_RESTRICT inputR, int16 * MPT_RESTRICT output, uint32 numFrames) { while(numFrames--) { *(output++) = static_cast(Clamp(*(inputL++) * _f2si, static_cast(int16_min), static_cast(int16_max))); *(output++) = static_cast(Clamp(*(inputR++) * _f2si, static_cast(int16_min), static_cast(int16_max))); } } // Deinterleave an int16 stereo stream into two float streams. static void DeinterleaveInt16ToFloat(const int16 * MPT_RESTRICT input, float * MPT_RESTRICT outputL, float * MPT_RESTRICT outputR, uint32 numFrames) { while(numFrames--) { *outputL++ += _si2f * static_cast(*input++); *outputR++ += _si2f * static_cast(*input++); } } void DMOPlugin::Process(float *pOutL, float *pOutR, uint32 numFrames) { if(!numFrames || !m_mixBuffer.Ok()) return; m_mixBuffer.ClearOutputBuffers(numFrames); REFERENCE_TIME startTime = Util::muldiv(m_SndFile.GetTotalSampleCount(), 10000000, m_nSamplesPerSec); if(m_useFloat) { InterleaveStereo(m_mixBuffer.GetInputBuffer(0), m_mixBuffer.GetInputBuffer(1), m_alignedBuffer.f32, numFrames); m_pMediaProcess->Process(numFrames * 2 * sizeof(float), reinterpret_cast(m_alignedBuffer.f32), startTime, DMO_INPLACE_NORMAL); DeinterleaveStereo(m_alignedBuffer.f32, m_mixBuffer.GetOutputBuffer(0), m_mixBuffer.GetOutputBuffer(1), numFrames); } else { InterleaveFloatToInt16(m_mixBuffer.GetInputBuffer(0), m_mixBuffer.GetInputBuffer(1), m_alignedBuffer.i16, numFrames); m_pMediaProcess->Process(numFrames * 2 * sizeof(int16), reinterpret_cast(m_alignedBuffer.i16), startTime, DMO_INPLACE_NORMAL); DeinterleaveInt16ToFloat(m_alignedBuffer.i16, m_mixBuffer.GetOutputBuffer(0), m_mixBuffer.GetOutputBuffer(1), numFrames); } ProcessMixOps(pOutL, pOutR, m_mixBuffer.GetOutputBuffer(0), m_mixBuffer.GetOutputBuffer(1), numFrames); } PlugParamIndex DMOPlugin::GetNumParameters() const { DWORD dwParamCount = 0; m_pParamInfo->GetParamCount(&dwParamCount); return dwParamCount; } PlugParamValue DMOPlugin::GetParameter(PlugParamIndex index) { if(index < GetNumParameters() && m_pParamInfo != nullptr && m_pMediaParams != nullptr) { MP_PARAMINFO mpi; MP_DATA md; MemsetZero(mpi); md = 0; if (m_pParamInfo->GetParamInfo(index, &mpi) == S_OK && m_pMediaParams->GetParam(index, &md) == S_OK) { float fValue, fMin, fMax, fDefault; fValue = md; fMin = mpi.mpdMinValue; fMax = mpi.mpdMaxValue; fDefault = mpi.mpdNeutralValue; if (mpi.mpType == MPT_BOOL) { fMin = 0; fMax = 1; } fValue -= fMin; if (fMax > fMin) fValue /= (fMax - fMin); return fValue; } } return 0; } void DMOPlugin::SetParameter(PlugParamIndex index, PlugParamValue value) { if(index < GetNumParameters() && m_pParamInfo != nullptr && m_pMediaParams != nullptr) { MP_PARAMINFO mpi; MemsetZero(mpi); if (m_pParamInfo->GetParamInfo(index, &mpi) == S_OK) { float fMin = mpi.mpdMinValue; float fMax = mpi.mpdMaxValue; if (mpi.mpType == MPT_BOOL) { fMin = 0; fMax = 1; value = (value > 0.5f) ? 1.0f : 0.0f; } if (fMax > fMin) value *= (fMax - fMin); value += fMin; value = mpt::safe_clamp(value, fMin, fMax); if (mpi.mpType != MPT_FLOAT) value = mpt::round(value); m_pMediaParams->SetParam(index, value); } } } void DMOPlugin::Resume() { m_nSamplesPerSec = m_SndFile.GetSampleRate(); m_isResumed = true; DMO_MEDIA_TYPE mt; WAVEFORMATEX wfx; mt.majortype = MEDIATYPE_Audio; mt.subtype = MEDIASUBTYPE_PCM; mt.bFixedSizeSamples = TRUE; mt.bTemporalCompression = FALSE; mt.formattype = FORMAT_WaveFormatEx; mt.pUnk = nullptr; mt.pbFormat = (LPBYTE)&wfx; mt.cbFormat = sizeof(WAVEFORMATEX); mt.lSampleSize = 2 * sizeof(float); wfx.wFormatTag = 3; // WAVE_FORMAT_IEEE_FLOAT; wfx.nChannels = 2; wfx.nSamplesPerSec = m_nSamplesPerSec; wfx.wBitsPerSample = sizeof(float) * 8; wfx.nBlockAlign = wfx.nChannels * (wfx.wBitsPerSample / 8); wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; wfx.cbSize = 0; // First try 32-bit float (DirectX 9+) m_useFloat = true; if(FAILED(m_pMediaObject->SetInputType(0, &mt, 0)) || FAILED(m_pMediaObject->SetOutputType(0, &mt, 0))) { m_useFloat = false; // Try again with 16-bit PCM mt.lSampleSize = 2 * sizeof(int16); wfx.wFormatTag = WAVE_FORMAT_PCM; wfx.wBitsPerSample = sizeof(int16) * 8; wfx.nBlockAlign = wfx.nChannels * (wfx.wBitsPerSample / 8); wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; if(FAILED(m_pMediaObject->SetInputType(0, &mt, 0)) || FAILED(m_pMediaObject->SetOutputType(0, &mt, 0))) { #ifdef DMO_LOG MPT_LOG_GLOBAL(LogDebug, "DMO", U_("DMO: Failed to set I/O media type")); #endif } } } void DMOPlugin::PositionChanged() { m_pMediaObject->Discontinuity(0); m_pMediaObject->Flush(); } void DMOPlugin::Suspend() { m_isResumed = false; m_pMediaObject->Flush(); m_pMediaObject->SetInputType(0, nullptr, DMO_SET_TYPEF_CLEAR); m_pMediaObject->SetOutputType(0, nullptr, DMO_SET_TYPEF_CLEAR); } #ifdef MODPLUG_TRACKER CString DMOPlugin::GetParamName(PlugParamIndex param) { if(param < GetNumParameters() && m_pParamInfo != nullptr) { MP_PARAMINFO mpi; mpi.mpType = MPT_INT; mpi.szUnitText[0] = 0; mpi.szLabel[0] = 0; if(m_pParamInfo->GetParamInfo(param, &mpi) == S_OK) { return mpt::ToCString(mpi.szLabel); } } return CString(); } CString DMOPlugin::GetParamLabel(PlugParamIndex param) { if(param < GetNumParameters() && m_pParamInfo != nullptr) { MP_PARAMINFO mpi; mpi.mpType = MPT_INT; mpi.szUnitText[0] = 0; mpi.szLabel[0] = 0; if(m_pParamInfo->GetParamInfo(param, &mpi) == S_OK) { return mpt::ToCString(mpi.szUnitText); } } return CString(); } CString DMOPlugin::GetParamDisplay(PlugParamIndex param) { if(param < GetNumParameters() && m_pParamInfo != nullptr && m_pMediaParams != nullptr) { MP_PARAMINFO mpi; mpi.mpType = MPT_INT; mpi.szUnitText[0] = 0; mpi.szLabel[0] = 0; if (m_pParamInfo->GetParamInfo(param, &mpi) == S_OK) { MP_DATA md; if(m_pMediaParams->GetParam(param, &md) == S_OK) { switch(mpi.mpType) { case MPT_FLOAT: { CString s; s.Format(_T("%.2f"), md); return s; } break; case MPT_BOOL: return ((int)md) ? _T("Yes") : _T("No"); break; case MPT_ENUM: { WCHAR *text = nullptr; m_pParamInfo->GetParamText(param, &text); const int nValue = mpt::saturate_round(md * (mpi.mpdMaxValue - mpi.mpdMinValue)); // Always skip first two strings (param name, unit name) for(int i = 0; i < nValue + 2; i++) { text += wcslen(text) + 1; } return mpt::ToCString(text); } break; case MPT_INT: default: { CString s; s.Format(_T("%d"), mpt::saturate_round(md)); return s; } break; } } } } return CString(); } #endif // MODPLUG_TRACKER #else // !MPT_WITH_DMO MPT_MSVC_WORKAROUND_LNK4221(DMOPlugin) #endif // MPT_WITH_DMO OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/plugins/dmo/DMOPlugin.h0000644000175000017500000000564413723533767022333 00000000000000/* * DMOPlugin.h * ----------- * Purpose: DirectX Media Object plugin handling / processing. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #if defined(MPT_WITH_DMO) #include "../PlugInterface.h" #include #include typedef interface IMediaObject IMediaObject; typedef interface IMediaObjectInPlace IMediaObjectInPlace; typedef interface IMediaParamInfo IMediaParamInfo; typedef interface IMediaParams IMediaParams; OPENMPT_NAMESPACE_BEGIN class DMOPlugin final : public IMixPlugin { protected: IMediaObject *m_pMediaObject; IMediaObjectInPlace *m_pMediaProcess; IMediaParamInfo *m_pParamInfo; IMediaParams *m_pMediaParams; uint32 m_nSamplesPerSec; const uint32 m_uid; union { int16 *i16; float *f32; } m_alignedBuffer; union { int16 i16[MIXBUFFERSIZE * 2 + 16]; // 16-bit PCM Stereo interleaved float f32[MIXBUFFERSIZE * 2 + 16]; // 32-bit Float Stereo interleaved } m_interleavedBuffer; bool m_useFloat; public: static IMixPlugin* Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct); protected: DMOPlugin(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct, IMediaObject *pMO, IMediaObjectInPlace *pMOIP, uint32 uid); ~DMOPlugin(); public: void Release() override { delete this; } int32 GetUID() const override { return m_uid; } int32 GetVersion() const override { return 2; } void Idle() override { } uint32 GetLatency() const override; void Process(float *pOutL, float *pOutR, uint32 numFrames) override; int32 GetNumPrograms() const override { return 0; } int32 GetCurrentProgram() override { return 0; } void SetCurrentProgram(int32 /*nIndex*/) override { } PlugParamIndex GetNumParameters() const override; PlugParamValue GetParameter(PlugParamIndex index) override; void SetParameter(PlugParamIndex index, PlugParamValue value) override; void Resume() override; void Suspend() override; void PositionChanged() override; bool IsInstrument() const override { return false; } bool CanRecieveMidiEvents() override { return false; } bool ShouldProcessSilence() override { return true; } #ifdef MODPLUG_TRACKER CString GetDefaultEffectName() override { return CString(); } CString GetParamName(PlugParamIndex param) override; CString GetParamLabel(PlugParamIndex param) override; CString GetParamDisplay(PlugParamIndex param) override; // TODO we could simply add our own preset mechanism. But is it really useful for these plugins? CString GetCurrentProgramName() override { return CString(); } void SetCurrentProgramName(const CString &) override { } CString GetProgramName(int32) override { return CString(); } bool HasEditor() const override { return false; } #endif int GetNumInputChannels() const override { return 2; } int GetNumOutputChannels() const override { return 2; } }; OPENMPT_NAMESPACE_END #endif // MPT_WITH_DMO libopenmpt-0.6.1+release.autotools/soundlib/plugins/dmo/DMOUtils.cpp0000644000175000017500000000227114126135501022477 00000000000000/* * DMOUtils.cpp * ------------ * Purpose: Utility functions shared by DMO plugins * Notes : none * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "DMOUtils.h" #ifndef NO_PLUGINS #include "../../Sndfile.h" #endif // !NO_PLUGINS OPENMPT_NAMESPACE_BEGIN #ifndef NO_PLUGINS namespace DMO { // Computes (log2(x) + 1) * 2 ^ (shiftL - shiftR) (x = -2^31...2^31) float logGain(float x, int32 shiftL, int32 shiftR) { uint32 intSample = static_cast(static_cast(x)); const uint32 sign = intSample & 0x80000000; if(sign) intSample = (~intSample) + 1; // Multiply until overflow (or edge shift factor is reached) while(shiftL > 0 && intSample < 0x80000000) { intSample += intSample; shiftL--; } // Unsign clipped sample if(intSample >= 0x80000000) { intSample &= 0x7FFFFFFF; shiftL++; } intSample = (shiftL << (31 - shiftR)) | (intSample >> shiftR); if(sign) intSample = ~intSample | sign; return static_cast(static_cast(intSample)); } } // namespace DMO #else MPT_MSVC_WORKAROUND_LNK4221(Distortion) #endif // !NO_PLUGINS OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/plugins/dmo/DMOUtils.h0000644000175000017500000000071113744301561022147 00000000000000/* * DMOUtils.h * ---------- * Purpose: Utility functions shared by DMO plugins * Notes : none * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ OPENMPT_NAMESPACE_BEGIN #ifndef NO_PLUGINS namespace DMO { // Computes (log2(x) + 1) * 2 ^ (shiftL - shiftR) (x = -2^31...2^31) float logGain(float x, int32 shiftL, int32 shiftR); } #endif // !NO_PLUGINS OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/plugins/dmo/Chorus.cpp0000644000175000017500000001674614155206341022321 00000000000000/* * Chorus.cpp * ---------- * Purpose: Implementation of the DMO Chorus DSP (for non-Windows platforms) * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #ifndef NO_PLUGINS #include "../../Sndfile.h" #include "Chorus.h" #include "mpt/base/numbers.hpp" #endif // !NO_PLUGINS OPENMPT_NAMESPACE_BEGIN #ifndef NO_PLUGINS namespace DMO { IMixPlugin* Chorus::Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct) { return new (std::nothrow) Chorus(factory, sndFile, mixStruct); } Chorus::Chorus(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct, bool isFlanger) : IMixPlugin(factory, sndFile, mixStruct) , m_isFlanger(isFlanger) { m_param[kChorusWetDryMix] = 0.5f; m_param[kChorusDepth] = 0.1f; m_param[kChorusFrequency] = 0.11f; m_param[kChorusWaveShape] = 1.0f; m_param[kChorusPhase] = 0.75f; m_param[kChorusFeedback] = (25.0f + 99.0f) / 198.0f; m_param[kChorusDelay] = 0.8f; m_mixBuffer.Initialize(2, 2); InsertIntoFactoryList(); } // Integer part of buffer position int32 Chorus::GetBufferIntOffset(int32 fpOffset) const { if(fpOffset < 0) fpOffset += m_bufSize * 4096; MPT_ASSERT(fpOffset >= 0); return (fpOffset / 4096) % m_bufSize; } void Chorus::Process(float *pOutL, float *pOutR, uint32 numFrames) { if(!m_bufSize || !m_mixBuffer.Ok()) return; const float *in[2] = { m_mixBuffer.GetInputBuffer(0), m_mixBuffer.GetInputBuffer(1) }; float *out[2] = { m_mixBuffer.GetOutputBuffer(0), m_mixBuffer.GetOutputBuffer(1) }; const bool isTriangle = IsTriangle(); const float feedback = Feedback() / 100.0f; const float wetDryMix = WetDryMix(); const uint32 phase = Phase(); const auto &bufferR = m_isFlanger ? m_bufferR : m_bufferL; for(uint32 i = numFrames; i != 0; i--) { const float leftIn = *(in[0])++; const float rightIn = *(in[1])++; const int32 readOffset = GetBufferIntOffset(m_bufPos + m_delayOffset); const int32 writeOffset = GetBufferIntOffset(m_bufPos); if(m_isFlanger) { m_DryBufferL[m_dryWritePos] = leftIn; m_DryBufferR[m_dryWritePos] = rightIn; m_bufferL[writeOffset] = (m_bufferL[readOffset] * feedback) + leftIn; m_bufferR[writeOffset] = (m_bufferR[readOffset] * feedback) + rightIn; } else { m_bufferL[writeOffset] = (m_bufferL[readOffset] * feedback) + (leftIn + rightIn) * 0.5f; } float waveMin; float waveMax; if(isTriangle) { m_waveShapeMin += m_waveShapeVal; m_waveShapeMax += m_waveShapeVal; if(m_waveShapeMin > 1) m_waveShapeMin -= 2; if(m_waveShapeMax > 1) m_waveShapeMax -= 2; waveMin = std::abs(m_waveShapeMin) * 2 - 1; waveMax = std::abs(m_waveShapeMax) * 2 - 1; } else { m_waveShapeMin = m_waveShapeMax * m_waveShapeVal + m_waveShapeMin; m_waveShapeMax = m_waveShapeMax - m_waveShapeMin * m_waveShapeVal; waveMin = m_waveShapeMin; waveMax = m_waveShapeMax; } const float leftDelayIn = m_isFlanger ? m_DryBufferL[(m_dryWritePos + 2) % 3] : leftIn; const float rightDelayIn = m_isFlanger ? m_DryBufferR[(m_dryWritePos + 2) % 3] : rightIn; float left1 = m_bufferL[GetBufferIntOffset(m_bufPos + m_delayL)]; float left2 = m_bufferL[GetBufferIntOffset(m_bufPos + m_delayL + 4096)]; float fracPos = (m_delayL & 0xFFF) * (1.0f / 4096.0f); float leftOut = (left2 - left1) * fracPos + left1; *(out[0])++ = leftDelayIn + (leftOut - leftDelayIn) * wetDryMix; float right1 = bufferR[GetBufferIntOffset(m_bufPos + m_delayR)]; float right2 = bufferR[GetBufferIntOffset(m_bufPos + m_delayR + 4096)]; fracPos = (m_delayR & 0xFFF) * (1.0f / 4096.0f); float rightOut = (right2 - right1) * fracPos + right1; *(out[1])++ = rightDelayIn + (rightOut - rightDelayIn) * wetDryMix; // Increment delay positions if(m_dryWritePos <= 0) m_dryWritePos += 3; m_dryWritePos--; m_delayL = m_delayOffset + (phase < 4 ? 1 : -1) * static_cast(waveMin * m_depthDelay); m_delayR = m_delayOffset + (phase < 2 ? -1 : 1) * static_cast(((phase % 2u) ? waveMax : waveMin) * m_depthDelay); if(m_bufPos <= 0) m_bufPos += m_bufSize * 4096; m_bufPos -= 4096; } ProcessMixOps(pOutL, pOutR, m_mixBuffer.GetOutputBuffer(0), m_mixBuffer.GetOutputBuffer(1), numFrames); } PlugParamValue Chorus::GetParameter(PlugParamIndex index) { if(index < kChorusNumParameters) { return m_param[index]; } return 0; } void Chorus::SetParameter(PlugParamIndex index, PlugParamValue value) { if(index < kChorusNumParameters) { value = mpt::safe_clamp(value, 0.0f, 1.0f); if(index == kChorusWaveShape) { value = mpt::round(value); if(m_param[index] != value) { m_waveShapeMin = 0.0f; m_waveShapeMax = 0.5f + value * 0.5f; } } else if(index == kChorusPhase) { value = mpt::round(value * 4.0f) / 4.0f; } m_param[index] = value; RecalculateChorusParams(); } } void Chorus::Resume() { PositionChanged(); RecalculateChorusParams(); m_isResumed = true; m_waveShapeMin = 0.0f; m_waveShapeMax = IsTriangle() ? 0.5f : 1.0f; m_delayL = m_delayR = m_delayOffset; m_bufPos = 0; m_dryWritePos = 0; } void Chorus::PositionChanged() { m_bufSize = Util::muldiv(m_SndFile.GetSampleRate(), 3840, 1000); try { m_bufferL.assign(m_bufSize, 0.0f); if(m_isFlanger) m_bufferR.assign(m_bufSize, 0.0f); m_DryBufferL.fill(0.0f); m_DryBufferR.fill(0.0f); } catch(mpt::out_of_memory e) { mpt::delete_out_of_memory(e); m_bufSize = 0; } } #ifdef MODPLUG_TRACKER CString Chorus::GetParamName(PlugParamIndex param) { switch(param) { case kChorusWetDryMix: return _T("WetDryMix"); case kChorusDepth: return _T("Depth"); case kChorusFrequency: return _T("Frequency"); case kChorusWaveShape: return _T("WaveShape"); case kChorusPhase: return _T("Phase"); case kChorusFeedback: return _T("Feedback"); case kChorusDelay: return _T("Delay"); } return CString(); } CString Chorus::GetParamLabel(PlugParamIndex param) { switch(param) { case kChorusWetDryMix: case kChorusDepth: case kChorusFeedback: return _T("%"); case kChorusFrequency: return _T("Hz"); case kChorusPhase: return mpt::ToCString(MPT_UTF8("\xC2\xB0")); // U+00B0 DEGREE SIGN case kChorusDelay: return _T("ms"); } return CString(); } CString Chorus::GetParamDisplay(PlugParamIndex param) { CString s; float value = m_param[param]; switch(param) { case kChorusWetDryMix: case kChorusDepth: value *= 100.0f; break; case kChorusFrequency: value = FrequencyInHertz(); break; case kChorusWaveShape: return (value < 1) ? _T("Triangle") : _T("Sine"); break; case kChorusPhase: switch(Phase()) { case 0: return _T("-180"); case 1: return _T("-90"); case 2: return _T("0"); case 3: return _T("90"); case 4: return _T("180"); } break; case kChorusFeedback: value = Feedback(); break; case kChorusDelay: value = Delay(); } s.Format(_T("%.2f"), value); return s; } #endif // MODPLUG_TRACKER void Chorus::RecalculateChorusParams() { const float sampleRate = static_cast(m_SndFile.GetSampleRate()); float delaySamples = Delay() * sampleRate / 1000.0f; m_depthDelay = Depth() * delaySamples * 2048.0f; m_delayOffset = mpt::saturate_round(4096.0f * (delaySamples + 2.0f)); m_frequency = FrequencyInHertz(); const float frequencySamples = m_frequency / sampleRate; if(IsTriangle()) m_waveShapeVal = frequencySamples * 2.0f; else m_waveShapeVal = std::sin(frequencySamples * mpt::numbers::pi_v) * 2.0f; } } // namespace DMO #else MPT_MSVC_WORKAROUND_LNK4221(Chorus) #endif // !NO_PLUGINS OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/plugins/dmo/Chorus.h0000644000175000017500000000716614064177730021772 00000000000000/* * Chorus.h * -------- * Purpose: Implementation of the DMO Chorus DSP (for non-Windows platforms) * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #ifndef NO_PLUGINS #include "../PlugInterface.h" OPENMPT_NAMESPACE_BEGIN namespace DMO { class Chorus : public IMixPlugin { protected: enum Parameters { kChorusWetDryMix = 0, kChorusDepth, kChorusFrequency, kChorusWaveShape, kChorusPhase, kChorusFeedback, kChorusDelay, kChorusNumParameters }; float m_param[kChorusNumParameters]; // Calculated parameters float m_waveShapeMin, m_waveShapeMax, m_waveShapeVal; float m_depthDelay; float m_frequency; int32 m_delayOffset; const bool m_isFlanger = false; // State std::vector m_bufferL, m_bufferR; // Only m_bufferL is used in case of !m_isFlanger std::array m_DryBufferL, m_DryBufferR; int32 m_bufPos = 0, m_bufSize = 0; int32 m_delayL = 0, m_delayR = 0; int32 m_dryWritePos = 0; public: static IMixPlugin* Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct); Chorus(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct, bool stereoBuffers = false); void Release() override { delete this; } int32 GetUID() const override { return 0xEFE6629C; } int32 GetVersion() const override { return 0; } void Idle() override { } uint32 GetLatency() const override { return 0; } void Process(float *pOutL, float *pOutR, uint32 numFrames) override; float RenderSilence(uint32) override { return 0.0f; } int32 GetNumPrograms() const override { return 0; } int32 GetCurrentProgram() override { return 0; } void SetCurrentProgram(int32) override { } PlugParamIndex GetNumParameters() const override { return kChorusNumParameters; } PlugParamValue GetParameter(PlugParamIndex index) override; void SetParameter(PlugParamIndex index, PlugParamValue value) override; void Resume() override; void Suspend() override { m_isResumed = false; } void PositionChanged() override; bool IsInstrument() const override { return false; } bool CanRecieveMidiEvents() override { return false; } bool ShouldProcessSilence() override { return true; } #ifdef MODPLUG_TRACKER CString GetDefaultEffectName() override { return _T("Chorus"); } CString GetParamName(PlugParamIndex param) override; CString GetParamLabel(PlugParamIndex) override; CString GetParamDisplay(PlugParamIndex param) override; CString GetCurrentProgramName() override { return CString(); } void SetCurrentProgramName(const CString &) override { } CString GetProgramName(int32) override { return CString(); } bool HasEditor() const override { return false; } #endif void BeginSetProgram(int32) override { } void EndSetProgram() override { } int GetNumInputChannels() const override { return 2; } int GetNumOutputChannels() const override { return 2; } protected: int32 GetBufferIntOffset(int32 fpOffset) const; virtual float WetDryMix() const { return m_param[kChorusWetDryMix]; } virtual bool IsTriangle() const { return m_param[kChorusWaveShape] < 1; } virtual float Depth() const { return m_param[kChorusDepth]; } virtual float Feedback() const { return -99.0f + m_param[kChorusFeedback] * 198.0f; } virtual float Delay() const { return m_param[kChorusDelay] * 20.0f; } virtual float FrequencyInHertz() const { return m_param[kChorusFrequency] * 10.0f; } virtual int Phase() const { return mpt::saturate_round(m_param[kChorusPhase] * 4.0f); } void RecalculateChorusParams(); }; } // namespace DMO OPENMPT_NAMESPACE_END #endif // !NO_PLUGINS libopenmpt-0.6.1+release.autotools/soundlib/plugins/dmo/Compressor.cpp0000644000175000017500000001301314155206341023172 00000000000000/* * Compressor.cpp * --------------- * Purpose: Implementation of the DMO Compressor DSP (for non-Windows platforms) * Notes : The original plugin's integer and floating point code paths only * behave identically when feeding floating point numbers in range * [-32768, +32768] rather than the usual [-1, +1] into the plugin. * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #ifndef NO_PLUGINS #include "../../Sndfile.h" #include "Compressor.h" #include "DMOUtils.h" #include "mpt/base/numbers.hpp" #endif // !NO_PLUGINS OPENMPT_NAMESPACE_BEGIN #ifndef NO_PLUGINS namespace DMO { IMixPlugin* Compressor::Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct) { return new (std::nothrow) Compressor(factory, sndFile, mixStruct); } Compressor::Compressor(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct) : IMixPlugin(factory, sndFile, mixStruct) { m_param[kCompGain] = 0.5f; m_param[kCompAttack] = 0.02f; m_param[kCompRelease] = 150.0f / 2950.0f; m_param[kCompThreshold] = 2.0f / 3.0f; m_param[kCompRatio] = 0.02f; m_param[kCompPredelay] = 1.0f; m_mixBuffer.Initialize(2, 2); InsertIntoFactoryList(); } void Compressor::Process(float *pOutL, float *pOutR, uint32 numFrames) { if(!m_bufSize || !m_mixBuffer.Ok()) return; const float *in[2] = { m_mixBuffer.GetInputBuffer(0), m_mixBuffer.GetInputBuffer(1) }; float *out[2] = { m_mixBuffer.GetOutputBuffer(0), m_mixBuffer.GetOutputBuffer(1) }; for(uint32 i = numFrames; i != 0; i--) { float leftIn = *(in[0])++; float rightIn = *(in[1])++; m_buffer[m_bufPos * 2] = leftIn; m_buffer[m_bufPos * 2 + 1] = rightIn; leftIn = std::abs(leftIn); rightIn = std::abs(rightIn); float mono = (leftIn + rightIn) * (0.5f * 32768.0f * 32768.0f); float monoLog = std::abs(logGain(mono, 31, 5)) * (1.0f / float(1u << 31)); float newPeak = monoLog + (m_peak - monoLog) * ((m_peak <= monoLog) ? m_attack : m_release); m_peak = newPeak; if(newPeak < m_threshold) newPeak = m_threshold; float compGain = (m_threshold - newPeak) * m_ratio + 0.9999999f; // Computes 2 ^ (2 ^ (log2(x) - 26) - 1) (x = 0...2^31) uint32 compGainInt = static_cast(compGain * 2147483648.0f); uint32 compGainPow = compGainInt << 5; compGainInt >>= 26; if(compGainInt) // compGainInt >= 2^26 { compGainPow |= 0x80000000u; compGainInt--; } compGainPow >>= (31 - compGainInt); int32 readOffset = m_predelay + m_bufPos * 4096 + m_bufSize - 1; readOffset /= 4096; readOffset %= m_bufSize; float outGain = (compGainPow * (1.0f / 2147483648.0f)) * m_gain; *(out[0])++ = m_buffer[readOffset * 2] * outGain; *(out[1])++ = m_buffer[readOffset * 2 + 1] * outGain; if(m_bufPos-- == 0) m_bufPos += m_bufSize; } ProcessMixOps(pOutL, pOutR, m_mixBuffer.GetOutputBuffer(0), m_mixBuffer.GetOutputBuffer(1), numFrames); } PlugParamValue Compressor::GetParameter(PlugParamIndex index) { if(index < kCompNumParameters) { return m_param[index]; } return 0; } void Compressor::SetParameter(PlugParamIndex index, PlugParamValue value) { if(index < kCompNumParameters) { value = mpt::safe_clamp(value, 0.0f, 1.0f); m_param[index] = value; RecalculateCompressorParams(); } } void Compressor::Resume() { m_isResumed = true; PositionChanged(); RecalculateCompressorParams(); } void Compressor::PositionChanged() { m_bufSize = Util::muldiv(m_SndFile.GetSampleRate(), 200, 1000); try { m_buffer.assign(m_bufSize * 2, 0.0f); } catch(mpt::out_of_memory e) { mpt::delete_out_of_memory(e); m_bufSize = 0; } m_bufPos = 0; m_peak = 0.0f; } #ifdef MODPLUG_TRACKER CString Compressor::GetParamName(PlugParamIndex param) { switch(param) { case kCompGain: return _T("Gain"); case kCompAttack: return _T("Attack"); case kCompRelease: return _T("Release"); case kCompThreshold: return _T("Threshold"); case kCompRatio: return _T("Ratio"); case kCompPredelay: return _T("Predelay"); } return CString(); } CString Compressor::GetParamLabel(PlugParamIndex param) { switch(param) { case kCompGain: case kCompThreshold: return _T("dB"); case kCompAttack: case kCompRelease: case kCompPredelay: return _T("ms"); } return CString(); } CString Compressor::GetParamDisplay(PlugParamIndex param) { float value = m_param[param]; switch(param) { case kCompGain: value = GainInDecibel(); break; case kCompAttack: value = AttackTime(); break; case kCompRelease: value = ReleaseTime(); break; case kCompThreshold: value = ThresholdInDecibel(); break; case kCompRatio: value = CompressorRatio(); break; case kCompPredelay: value = PreDelay(); break; } CString s; s.Format(_T("%.2f"), value); return s; } #endif // MODPLUG_TRACKER void Compressor::RecalculateCompressorParams() { const float sampleRate = m_SndFile.GetSampleRate() / 1000.0f; m_gain = std::pow(10.0f, GainInDecibel() / 20.0f); m_attack = std::pow(10.0f, -1.0f / (AttackTime() * sampleRate)); m_release = std::pow(10.0f, -1.0f / (ReleaseTime() * sampleRate)); const float _2e31 = float(1u << 31); const float _2e26 = float(1u << 26); m_threshold = std::min((_2e31 - 1.0f), (std::log(std::pow(10.0f, ThresholdInDecibel() / 20.0f) * _2e31) * _2e26) / mpt::numbers::ln2_v + _2e26) * (1.0f / _2e31); m_ratio = 1.0f - (1.0f / CompressorRatio()); m_predelay = static_cast((PreDelay() * sampleRate) + 2.0f); } } // namespace DMO #else MPT_MSVC_WORKAROUND_LNK4221(Compressor) #endif // !NO_PLUGINS OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/plugins/dmo/Compressor.h0000644000175000017500000000620313647400540022644 00000000000000/* * Compressor.h * ------------- * Purpose: Implementation of the DMO Compressor DSP (for non-Windows platforms) * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #ifndef NO_PLUGINS #include "../PlugInterface.h" OPENMPT_NAMESPACE_BEGIN namespace DMO { class Compressor final : public IMixPlugin { protected: enum Parameters { kCompGain = 0, kCompAttack, kCompRelease, kCompThreshold, kCompRatio, kCompPredelay, kCompNumParameters }; float m_param[kCompNumParameters]; // Calculated parameters and coefficients float m_gain; float m_attack; float m_release; float m_threshold; float m_ratio; int32 m_predelay; // State std::vector m_buffer; int32 m_bufPos, m_bufSize; float m_peak; public: static IMixPlugin* Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct); Compressor(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct); void Release() override { delete this; } int32 GetUID() const override { return 0xEF011F79; } int32 GetVersion() const override { return 0; } void Idle() override { } uint32 GetLatency() const override { return 0; } void Process(float *pOutL, float *pOutR, uint32 numFrames) override; float RenderSilence(uint32) override { return 0.0f; } int32 GetNumPrograms() const override { return 0; } int32 GetCurrentProgram() override { return 0; } void SetCurrentProgram(int32) override { } PlugParamIndex GetNumParameters() const override { return kCompNumParameters; } PlugParamValue GetParameter(PlugParamIndex index) override; void SetParameter(PlugParamIndex index, PlugParamValue value) override; void Resume() override; void Suspend() override { m_isResumed = false; } void PositionChanged() override; bool IsInstrument() const override { return false; } bool CanRecieveMidiEvents() override { return false; } bool ShouldProcessSilence() override { return true; } #ifdef MODPLUG_TRACKER CString GetDefaultEffectName() override { return _T("Compressor"); } CString GetParamName(PlugParamIndex param) override; CString GetParamLabel(PlugParamIndex) override; CString GetParamDisplay(PlugParamIndex param) override; CString GetCurrentProgramName() override { return CString(); } void SetCurrentProgramName(const CString &) override { } CString GetProgramName(int32) override { return CString(); } bool HasEditor() const override { return false; } #endif int GetNumInputChannels() const override { return 2; } int GetNumOutputChannels() const override { return 2; } protected: float GainInDecibel() const { return -60.0f + m_param[kCompGain] * 120.0f; } float AttackTime() const { return 0.01f + m_param[kCompAttack] * 499.99f; } float ReleaseTime() const { return 50.0f + m_param[kCompRelease] * 2950.0f; } float ThresholdInDecibel() const { return -60.0f + m_param[kCompThreshold] * 60.0f; } float CompressorRatio() const { return 1.0f + m_param[kCompRatio] * 99.0f; } float PreDelay() const { return m_param[kCompPredelay] * 4.0f; } void RecalculateCompressorParams(); }; } // namespace DMO OPENMPT_NAMESPACE_END #endif // !NO_PLUGINS libopenmpt-0.6.1+release.autotools/soundlib/plugins/dmo/Distortion.cpp0000644000175000017500000001275314155206341023206 00000000000000/* * Distortion.cpp * -------------- * Purpose: Implementation of the DMO Distortion DSP (for non-Windows platforms) * Notes : The original plugin's integer and floating point code paths only * behave identically when feeding floating point numbers in range * [-32768, +32768] rather than the usual [-1, +1] into the plugin. * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #ifndef NO_PLUGINS #include "../../Sndfile.h" #include "Distortion.h" #include "DMOUtils.h" #include "mpt/base/numbers.hpp" #endif // !NO_PLUGINS OPENMPT_NAMESPACE_BEGIN #ifndef NO_PLUGINS namespace DMO { IMixPlugin* Distortion::Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct) { return new (std::nothrow) Distortion(factory, sndFile, mixStruct); } Distortion::Distortion(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct) : IMixPlugin(factory, sndFile, mixStruct) { m_param[kDistGain] = 0.7f; m_param[kDistEdge] = 0.15f; m_param[kDistPreLowpassCutoff] = 1.0f; m_param[kDistPostEQCenterFrequency] = 0.291f; m_param[kDistPostEQBandwidth] = 0.291f; m_mixBuffer.Initialize(2, 2); InsertIntoFactoryList(); } void Distortion::Process(float *pOutL, float *pOutR, uint32 numFrames) { if(!m_mixBuffer.Ok()) return; const float *in[2] = { m_mixBuffer.GetInputBuffer(0), m_mixBuffer.GetInputBuffer(1) }; float *out[2] = { m_mixBuffer.GetOutputBuffer(0), m_mixBuffer.GetOutputBuffer(1) }; for(uint32 i = numFrames; i != 0; i--) { for(uint8 channel = 0; channel < 2; channel++) { float x = *(in[channel])++; // Pre EQ float z = x * m_preEQa0 + m_preEQz1[channel] * m_preEQb1; m_preEQz1[channel] = z; z *= 1073741824.0f; // 32768^2 // The actual distortion z = logGain(z, m_edge, m_shift); // Post EQ / Gain z = (z * m_postEQa0) - m_postEQz1[channel] * m_postEQb1 - m_postEQz2[channel] * m_postEQb0; m_postEQz1[channel] = z * m_postEQb0 + m_postEQz2[channel]; m_postEQz2[channel] = z; z *= (1.0f / 1073741824.0f); // 32768^2 *(out[channel])++ = z; } } ProcessMixOps(pOutL, pOutR, m_mixBuffer.GetOutputBuffer(0), m_mixBuffer.GetOutputBuffer(1), numFrames); } PlugParamValue Distortion::GetParameter(PlugParamIndex index) { if(index < kDistNumParameters) { return m_param[index]; } return 0; } void Distortion::SetParameter(PlugParamIndex index, PlugParamValue value) { if(index < kDistNumParameters) { value = mpt::safe_clamp(value, 0.0f, 1.0f); m_param[index] = value; RecalculateDistortionParams(); } } void Distortion::Resume() { m_isResumed = true; RecalculateDistortionParams(); PositionChanged(); } void Distortion::PositionChanged() { // Reset filter state m_preEQz1[0] = m_preEQz1[1] = 0; m_postEQz1[0] = m_postEQz2[0] = 0; m_postEQz1[1] = m_postEQz2[1] = 0; } #ifdef MODPLUG_TRACKER CString Distortion::GetParamName(PlugParamIndex param) { switch(param) { case kDistGain: return _T("Gain"); case kDistEdge: return _T("Edge"); case kDistPreLowpassCutoff: return _T("PreLowpassCutoff"); case kDistPostEQCenterFrequency: return _T("PostEQCenterFrequency"); case kDistPostEQBandwidth: return _T("PostEQBandwidth"); } return CString(); } CString Distortion::GetParamLabel(PlugParamIndex param) { switch(param) { case kDistGain: return _T("dB"); case kDistPreLowpassCutoff: case kDistPostEQCenterFrequency: return _T("Hz"); } return CString(); } CString Distortion::GetParamDisplay(PlugParamIndex param) { float value = m_param[param]; switch(param) { case kDistGain: value = GainInDecibel(); break; case kDistEdge: value *= 100.0f; break; case kDistPreLowpassCutoff: case kDistPostEQCenterFrequency: case kDistPostEQBandwidth: value = FreqInHertz(value); break; } CString s; s.Format(_T("%.2f"), value); return s; } #endif // MODPLUG_TRACKER void Distortion::RecalculateDistortionParams() { // Pre-EQ m_preEQb1 = std::sqrt((2.0f * std::cos(2.0f * mpt::numbers::pi_v * std::min(FreqInHertz(m_param[kDistPreLowpassCutoff]) / m_SndFile.GetSampleRate(), 0.5f)) + 3.0f) / 5.0f); m_preEQa0 = std::sqrt(1.0f - m_preEQb1 * m_preEQb1); // Distortion float edge = 2.0f + m_param[kDistEdge] * 29.0f; m_edge = static_cast(edge); // 2...31 shifted bits // Work out the magical shift factor (= floor(log2(edge)) + 1 == index of highest bit + 1) uint8 shift; if(m_edge <= 3) shift = 2; else if(m_edge <= 7) shift = 3; else if(m_edge <= 15) shift = 4; else shift = 5; m_shift = shift; static constexpr float LogNorm[32] = { 1.00f, 1.00f, 1.50f, 1.00f, 1.75f, 1.40f, 1.17f, 1.00f, 1.88f, 1.76f, 1.50f, 1.36f, 1.25f, 1.15f, 1.07f, 1.00f, 1.94f, 1.82f, 1.72f, 1.63f, 1.55f, 1.48f, 1.41f, 1.35f, 1.29f, 1.24f, 1.19f, 1.15f, 1.11f, 1.07f, 1.03f, 1.00f, }; // Post-EQ const float gain = std::pow(10.0f, GainInDecibel() / 20.0f); const float postFreq = 2.0f * mpt::numbers::pi_v * std::min(FreqInHertz(m_param[kDistPostEQCenterFrequency]) / m_SndFile.GetSampleRate(), 0.5f); const float postBw = 2.0f * mpt::numbers::pi_v * std::min(FreqInHertz(m_param[kDistPostEQBandwidth]) / m_SndFile.GetSampleRate(), 0.5f); const float t = std::tan(5.0e-1f * postBw); m_postEQb1 = ((1.0f - t) / (1.0f + t)); m_postEQb0 = -std::cos(postFreq); m_postEQa0 = gain * std::sqrt(1.0f - m_postEQb0 * m_postEQb0) * std::sqrt(1.0f - m_postEQb1 * m_postEQb1) * LogNorm[m_edge]; } } // namespace DMO #else MPT_MSVC_WORKAROUND_LNK4221(Distortion) #endif // !NO_PLUGINS OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/plugins/dmo/Distortion.h0000644000175000017500000000545313647400540022654 00000000000000/* * Distortion.h * ------------ * Purpose: Implementation of the DMO Distortion DSP (for non-Windows platforms) * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #ifndef NO_PLUGINS #include "../PlugInterface.h" OPENMPT_NAMESPACE_BEGIN namespace DMO { class Distortion final : public IMixPlugin { protected: enum Parameters { kDistGain = 0, kDistEdge, kDistPreLowpassCutoff, kDistPostEQCenterFrequency, kDistPostEQBandwidth, kDistNumParameters }; float m_param[kDistNumParameters]; // Pre-EQ coefficients float m_preEQz1[2], m_preEQb1, m_preEQa0; // Post-EQ coefficients float m_postEQz1[2], m_postEQz2[2], m_postEQa0, m_postEQb0, m_postEQb1; uint8 m_edge, m_shift; public: static IMixPlugin* Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct); Distortion(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct); void Release() override { delete this; } int32 GetUID() const override { return 0xEF114C90; } int32 GetVersion() const override { return 0; } void Idle() override { } uint32 GetLatency() const override { return 0; } void Process(float *pOutL, float *pOutR, uint32 numFrames) override; float RenderSilence(uint32) override { return 0.0f; } int32 GetNumPrograms() const override { return 0; } int32 GetCurrentProgram() override { return 0; } void SetCurrentProgram(int32) override { } PlugParamIndex GetNumParameters() const override { return kDistNumParameters; } PlugParamValue GetParameter(PlugParamIndex index) override; void SetParameter(PlugParamIndex index, PlugParamValue value) override; void Resume() override; void Suspend() override { m_isResumed = false; } void PositionChanged() override; bool IsInstrument() const override { return false; } bool CanRecieveMidiEvents() override { return false; } bool ShouldProcessSilence() override { return true; } #ifdef MODPLUG_TRACKER CString GetDefaultEffectName() override { return _T("Distortion"); } CString GetParamName(PlugParamIndex param) override; CString GetParamLabel(PlugParamIndex) override; CString GetParamDisplay(PlugParamIndex param) override; CString GetCurrentProgramName() override { return CString(); } void SetCurrentProgramName(const CString &) override { } CString GetProgramName(int32) override { return CString(); } bool HasEditor() const override { return false; } #endif int GetNumInputChannels() const override { return 2; } int GetNumOutputChannels() const override { return 2; } protected: static float FreqInHertz(float param) { return 100.0f + param * 7900.0f; } float GainInDecibel() const { return -60.0f + m_param[kDistGain] * 60.0f; } void RecalculateDistortionParams(); }; } // namespace DMO OPENMPT_NAMESPACE_END #endif // !NO_PLUGINS libopenmpt-0.6.1+release.autotools/soundlib/plugins/dmo/Echo.cpp0000644000175000017500000001052014155206341021714 00000000000000/* * Echo.cpp * -------- * Purpose: Implementation of the DMO Echo DSP (for non-Windows platforms) * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #ifndef NO_PLUGINS #include "../../Sndfile.h" #include "Echo.h" #endif // !NO_PLUGINS OPENMPT_NAMESPACE_BEGIN #ifndef NO_PLUGINS namespace DMO { IMixPlugin* Echo::Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct) { return new (std::nothrow) Echo(factory, sndFile, mixStruct); } Echo::Echo(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct) : IMixPlugin(factory, sndFile, mixStruct) , m_bufferSize(0) , m_writePos(0) , m_sampleRate(sndFile.GetSampleRate()) , m_initialFeedback(0.0f) { m_param[kEchoWetDry] = 0.5f; m_param[kEchoFeedback] = 0.5f; m_param[kEchoLeftDelay] = 0.25f; m_param[kEchoRightDelay] = 0.25f; m_param[kEchoPanDelay] = 0.0f; m_mixBuffer.Initialize(2, 2); InsertIntoFactoryList(); } void Echo::Process(float *pOutL, float *pOutR, uint32 numFrames) { if(!m_bufferSize || !m_mixBuffer.Ok()) return; const float wetMix = m_param[kEchoWetDry], dryMix = 1 - wetMix; const float *in[2] = { m_mixBuffer.GetInputBuffer(0), m_mixBuffer.GetInputBuffer(1) }; float *out[2] = { m_mixBuffer.GetOutputBuffer(0), m_mixBuffer.GetOutputBuffer(1) }; for(uint32 i = numFrames; i != 0; i--) { for(uint8 channel = 0; channel < 2; channel++) { const uint8 readChannel = (m_crossEcho ? (1 - channel) : channel); int readPos = m_writePos - m_delayTime[readChannel]; if(readPos < 0) readPos += m_bufferSize; float chnInput = *(in[channel])++; float chnDelay = m_delayLine[readPos * 2 + readChannel]; // Calculate the delay float chnOutput = chnInput * m_initialFeedback; chnOutput += chnDelay * m_param[kEchoFeedback]; // Prevent denormals if(std::abs(chnOutput) < 1e-24f) chnOutput = 0.0f; m_delayLine[m_writePos * 2 + channel] = chnOutput; // Output samples now *(out[channel])++ = (chnInput * dryMix + chnDelay * wetMix); } m_writePos++; if(m_writePos == m_bufferSize) m_writePos = 0; } ProcessMixOps(pOutL, pOutR, m_mixBuffer.GetOutputBuffer(0), m_mixBuffer.GetOutputBuffer(1), numFrames); } PlugParamValue Echo::GetParameter(PlugParamIndex index) { if(index < kEchoNumParameters) { return m_param[index]; } return 0; } void Echo::SetParameter(PlugParamIndex index, PlugParamValue value) { if(index < kEchoNumParameters) { value = mpt::safe_clamp(value, 0.0f, 1.0f); if(index == kEchoPanDelay) value = mpt::round(value); m_param[index] = value; RecalculateEchoParams(); } } void Echo::Resume() { m_isResumed = true; m_sampleRate = m_SndFile.GetSampleRate(); RecalculateEchoParams(); PositionChanged(); } void Echo::PositionChanged() { m_bufferSize = m_sampleRate * 2u; try { m_delayLine.assign(m_bufferSize * 2, 0); } catch(mpt::out_of_memory e) { mpt::delete_out_of_memory(e); m_bufferSize = 0; } m_writePos = 0; } #ifdef MODPLUG_TRACKER CString Echo::GetParamName(PlugParamIndex param) { switch(param) { case kEchoWetDry: return _T("WetDryMix"); case kEchoFeedback: return _T("Feedback"); case kEchoLeftDelay: return _T("LeftDelay"); case kEchoRightDelay: return _T("RightDelay"); case kEchoPanDelay: return _T("PanDelay"); } return CString(); } CString Echo::GetParamLabel(PlugParamIndex param) { if(param == kEchoLeftDelay || param == kEchoRightDelay) return _T("ms"); return CString(); } CString Echo::GetParamDisplay(PlugParamIndex param) { CString s; switch(param) { case kEchoWetDry: case kEchoFeedback: s.Format(_T("%.2f"), m_param[param] * 100.0f); break; case kEchoLeftDelay: case kEchoRightDelay: s.Format(_T("%.2f"), m_param[param] * 2000.0f); break; case kEchoPanDelay: s = (m_param[param] <= 0.5) ? _T("No") : _T("Yes"); } return s; } #endif // MODPLUG_TRACKER void Echo::RecalculateEchoParams() { m_initialFeedback = std::sqrt(1.0f - (m_param[kEchoFeedback] * m_param[kEchoFeedback])); m_delayTime[0] = static_cast(m_param[kEchoLeftDelay] * (2 * m_sampleRate)); m_delayTime[1] = static_cast(m_param[kEchoRightDelay] * (2 * m_sampleRate)); m_crossEcho = (m_param[kEchoPanDelay]) > 0.5f; } } // namespace DMO #else MPT_MSVC_WORKAROUND_LNK4221(Echo) #endif // !NO_PLUGINS OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/plugins/dmo/Echo.h0000644000175000017500000000532213647400540021367 00000000000000/* * Echo.h * ------ * Purpose: Implementation of the DMO Echo DSP (for non-Windows platforms) * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #ifndef NO_PLUGINS #include "../PlugInterface.h" OPENMPT_NAMESPACE_BEGIN namespace DMO { class Echo final : public IMixPlugin { protected: enum Parameters { kEchoWetDry = 0, kEchoFeedback, kEchoLeftDelay, kEchoRightDelay, kEchoPanDelay, kEchoNumParameters }; std::vector m_delayLine; // Echo delay line float m_param[kEchoNumParameters]; uint32 m_bufferSize; // Delay line length in frames uint32 m_writePos; // Current write position in the delay line uint32 m_delayTime[2]; // In frames uint32 m_sampleRate; // Echo calculation coefficients float m_initialFeedback; bool m_crossEcho; public: static IMixPlugin* Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct); Echo(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct); void Release() override { delete this; } int32 GetUID() const override { return 0xEF3E932C; } int32 GetVersion() const override { return 0; } void Idle() override { } uint32 GetLatency() const override { return 0; } void Process(float *pOutL, float *pOutR, uint32 numFrames)override; float RenderSilence(uint32) override { return 0.0f; } int32 GetNumPrograms() const override { return 0; } int32 GetCurrentProgram() override { return 0; } void SetCurrentProgram(int32) override { } PlugParamIndex GetNumParameters() const override { return kEchoNumParameters; } PlugParamValue GetParameter(PlugParamIndex index) override; void SetParameter(PlugParamIndex index, PlugParamValue value) override; void Resume() override; void Suspend() override { m_isResumed = false; } void PositionChanged() override; bool IsInstrument() const override { return false; } bool CanRecieveMidiEvents() override { return false; } bool ShouldProcessSilence() override { return true; } #ifdef MODPLUG_TRACKER CString GetDefaultEffectName() override { return _T("Echo"); } CString GetParamName(PlugParamIndex param) override; CString GetParamLabel(PlugParamIndex) override; CString GetParamDisplay(PlugParamIndex param) override; CString GetCurrentProgramName() override { return CString(); } void SetCurrentProgramName(const CString &) override { } CString GetProgramName(int32) override { return CString(); } bool HasEditor() const override { return false; } #endif int GetNumInputChannels() const override { return 2; } int GetNumOutputChannels() const override { return 2; } protected: void RecalculateEchoParams(); }; } // namespace DMO OPENMPT_NAMESPACE_END #endif // !NO_PLUGINS libopenmpt-0.6.1+release.autotools/soundlib/plugins/dmo/Flanger.cpp0000644000175000017500000000652314155206341022424 00000000000000/* * Flanger.cpp * ----------- * Purpose: Implementation of the DMO Flanger DSP (for non-Windows platforms) * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #ifndef NO_PLUGINS #include "../../Sndfile.h" #include "Flanger.h" #endif // !NO_PLUGINS OPENMPT_NAMESPACE_BEGIN #ifndef NO_PLUGINS namespace DMO { IMixPlugin* Flanger::Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct) { return new (std::nothrow) Flanger(factory, sndFile, mixStruct, false); } IMixPlugin* Flanger::CreateLegacy(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct) { return new(std::nothrow) Flanger(factory, sndFile, mixStruct, true); } Flanger::Flanger(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct, const bool legacy) : Chorus(factory, sndFile, mixStruct, !legacy) { m_param[kFlangerWetDryMix] = 0.5f; m_param[kFlangerWaveShape] = 1.0f; m_param[kFlangerFrequency] = 0.025f; m_param[kFlangerDepth] = 1.0f; m_param[kFlangerPhase] = 0.5f; m_param[kFlangerFeedback] = (-50.0f + 99.0f) / 198.0f; m_param[kFlangerDelay] = 0.5f; // Already done in Chorus constructor //m_mixBuffer.Initialize(2, 2); //InsertIntoFactoryList(); } void Flanger::SetParameter(PlugParamIndex index, PlugParamValue value) { if(index < kFlangerNumParameters) { value = mpt::safe_clamp(value, 0.0f, 1.0f); if(index == kFlangerWaveShape) { value = mpt::round(value); if(m_param[index] != value) { m_waveShapeMin = 0.0f; m_waveShapeMax = 0.5f + value * 0.5f; } } else if(index == kFlangerPhase) { value = mpt::round(value * 4.0f) / 4.0f; } m_param[index] = value; RecalculateChorusParams(); } } #ifdef MODPLUG_TRACKER CString Flanger::GetParamName(PlugParamIndex param) { switch(param) { case kFlangerWetDryMix: return _T("WetDryMix"); case kFlangerWaveShape: return _T("WaveShape"); case kFlangerFrequency: return _T("Frequency"); case kFlangerDepth: return _T("Depth"); case kFlangerPhase: return _T("Phase"); case kFlangerFeedback: return _T("Feedback"); case kFlangerDelay: return _T("Delay"); } return CString(); } CString Flanger::GetParamLabel(PlugParamIndex param) { switch(param) { case kFlangerWetDryMix: case kFlangerDepth: case kFlangerFeedback: return _T("%"); case kFlangerFrequency: return _T("Hz"); case kFlangerPhase: return mpt::ToCString(MPT_UTF8("\xC2\xB0")); // U+00B0 DEGREE SIGN case kFlangerDelay: return _T("ms"); } return CString(); } CString Flanger::GetParamDisplay(PlugParamIndex param) { CString s; float value = m_param[param]; switch(param) { case kFlangerWetDryMix: case kFlangerDepth: value *= 100.0f; break; case kFlangerFrequency: value = FrequencyInHertz(); break; case kFlangerWaveShape: return (value < 1) ? _T("Triangle") : _T("Sine"); break; case kFlangerPhase: switch(Phase()) { case 0: return _T("-180"); case 1: return _T("-90"); case 2: return _T("0"); case 3: return _T("90"); case 4: return _T("180"); } break; case kFlangerFeedback: value = Feedback(); break; case kFlangerDelay: value = Delay(); } s.Format(_T("%.2f"), value); return s; } #endif // MODPLUG_TRACKER } // namespace DMO #else MPT_MSVC_WORKAROUND_LNK4221(Flanger) #endif // !NO_PLUGINS OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/plugins/dmo/Flanger.h0000644000175000017500000000406314064177730022076 00000000000000/* * Flanger.h * --------- * Purpose: Implementation of the DMO Flanger DSP (for non-Windows platforms) * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #ifndef NO_PLUGINS #include "Chorus.h" OPENMPT_NAMESPACE_BEGIN namespace DMO { class Flanger final : public Chorus { protected: enum Parameters { kFlangerWetDryMix = 0, kFlangerWaveShape, kFlangerFrequency, kFlangerDepth, kFlangerPhase, kFlangerFeedback, kFlangerDelay, kFlangerNumParameters }; public: static IMixPlugin* Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct); static IMixPlugin* CreateLegacy(VSTPluginLib& factory, CSoundFile& sndFile, SNDMIXPLUGIN* mixStruct); Flanger(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct, const bool legacy); void Release() override { delete this; } int32 GetUID() const override { return 0xEFCA3D92; } PlugParamIndex GetNumParameters() const override { return kFlangerNumParameters; } void SetParameter(PlugParamIndex index, PlugParamValue value) override; #ifdef MODPLUG_TRACKER CString GetDefaultEffectName() override { return _T("Flanger"); } CString GetParamName(PlugParamIndex param) override; CString GetParamLabel(PlugParamIndex) override; CString GetParamDisplay(PlugParamIndex param) override; #endif protected: float WetDryMix() const override { return m_param[kFlangerWetDryMix]; } bool IsTriangle() const override { return m_param[kFlangerWaveShape] < 1; } float Depth() const override { return m_param[kFlangerDepth]; } float Feedback() const override { return -99.0f + m_param[kFlangerFeedback] * 198.0f; } float Delay() const override { return m_param[kFlangerDelay] * 4.0f; } float FrequencyInHertz() const override { return m_param[kFlangerFrequency] * 10.0f; } int Phase() const override { return mpt::saturate_round(m_param[kFlangerPhase] * 4.0f); } }; } // namespace DMO OPENMPT_NAMESPACE_END #endif // !NO_PLUGINS libopenmpt-0.6.1+release.autotools/soundlib/plugins/dmo/Gargle.cpp0000644000175000017500000000771714155206341022255 00000000000000/* * Gargle.cpp * ---------- * Purpose: Implementation of the DMO Gargle DSP (for non-Windows platforms) * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #ifndef NO_PLUGINS #include "../../Sndfile.h" #include "Gargle.h" #endif // !NO_PLUGINS OPENMPT_NAMESPACE_BEGIN #ifndef NO_PLUGINS namespace DMO { IMixPlugin* Gargle::Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct) { return new (std::nothrow) Gargle(factory, sndFile, mixStruct); } Gargle::Gargle(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct) : IMixPlugin(factory, sndFile, mixStruct) { m_param[kGargleRate] = 0.02f; m_param[kGargleWaveShape] = 0.0f; m_mixBuffer.Initialize(2, 2); InsertIntoFactoryList(); } void Gargle::Process(float *pOutL, float *pOutR, uint32 numFrames) { if(!m_mixBuffer.Ok()) return; const float *inL = m_mixBuffer.GetInputBuffer(0), *inR = m_mixBuffer.GetInputBuffer(1); float *outL = m_mixBuffer.GetOutputBuffer(0), *outR = m_mixBuffer.GetOutputBuffer(1); const bool triangle = m_param[kGargleWaveShape] < 1.0f; for(uint32 frame = numFrames; frame != 0;) { if(m_counter < m_periodHalf) { // First half of gargle period const uint32 remain = std::min(frame, m_periodHalf - m_counter); if(triangle) { const uint32 stop = m_counter + remain; const float factor = 1.0f / m_periodHalf; for(uint32 i = m_counter; i < stop; i++) { *outL++ = *inL++ * i * factor; *outR++ = *inR++ * i * factor; } } else { for(uint32 i = 0; i < remain; i++) { *outL++ = *inL++; *outR++ = *inR++; } } frame -= remain; m_counter += remain; } else { // Second half of gargle period const uint32 remain = std::min(frame, m_period - m_counter); if(triangle) { const uint32 stop = m_period - m_counter - remain; const float factor = 1.0f / m_periodHalf; for(uint32 i = m_period - m_counter; i > stop; i--) { *outL++ = *inL++ * i * factor; *outR++ = *inR++ * i * factor; } } else { for(uint32 i = 0; i < remain; i++) { *outL++ = 0; *outR++ = 0; } inL += remain; inR += remain; } frame -= remain; m_counter += remain; if(m_counter >= m_period) m_counter = 0; } } ProcessMixOps(pOutL, pOutR, m_mixBuffer.GetOutputBuffer(0), m_mixBuffer.GetOutputBuffer(1), numFrames); } PlugParamValue Gargle::GetParameter(PlugParamIndex index) { if(index < kEqNumParameters) { return m_param[index]; } return 0; } void Gargle::SetParameter(PlugParamIndex index, PlugParamValue value) { if(index < kEqNumParameters) { value = mpt::safe_clamp(value, 0.0f, 1.0f); if(index == kGargleWaveShape) value = mpt::round(value); m_param[index] = value; RecalculateGargleParams(); } } void Gargle::Resume() { RecalculateGargleParams(); m_counter = 0; m_isResumed = true; } #ifdef MODPLUG_TRACKER CString Gargle::GetParamName(PlugParamIndex param) { switch(param) { case kGargleRate: return _T("Rate"); case kGargleWaveShape: return _T("WaveShape"); } return CString(); } CString Gargle::GetParamLabel(PlugParamIndex param) { switch(param) { case kGargleRate: return _T("Hz"); } return CString(); } CString Gargle::GetParamDisplay(PlugParamIndex param) { CString s; switch(param) { case kGargleRate: s.Format(_T("%u"), RateInHertz()); break; case kGargleWaveShape: return (m_param[param] < 0.5) ? _T("Triangle") : _T("Square"); } return s; } #endif // MODPLUG_TRACKER uint32 Gargle::RateInHertz() const { return mpt::saturate_round(m_param[kGargleRate] * 999.0f) + 1; } void Gargle::RecalculateGargleParams() { m_period = m_SndFile.GetSampleRate() / RateInHertz(); if(m_period < 2) m_period = 2; m_periodHalf = m_period / 2; LimitMax(m_counter, m_period); } } // namespace DMO #else MPT_MSVC_WORKAROUND_LNK4221(Gargle) #endif // !NO_PLUGINS OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/plugins/dmo/Gargle.h0000644000175000017500000000472513647400540021720 00000000000000/* * Gargle.h * -------- * Purpose: Implementation of the DMO Gargle DSP (for non-Windows platforms) * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #ifndef NO_PLUGINS #include "../PlugInterface.h" OPENMPT_NAMESPACE_BEGIN namespace DMO { class Gargle final : public IMixPlugin { protected: enum Parameters { kGargleRate = 0, kGargleWaveShape, kEqNumParameters }; float m_param[kEqNumParameters]; uint32 m_period, m_periodHalf, m_counter; // In frames public: static IMixPlugin* Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct); Gargle(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct); void Release() override { delete this; } int32 GetUID() const override { return 0xDAFD8210; } int32 GetVersion() const override { return 0; } void Idle() override { } uint32 GetLatency() const override { return 0; } void Process(float *pOutL, float *pOutR, uint32 numFrames) override; float RenderSilence(uint32) override { return 0.0f; } int32 GetNumPrograms() const override { return 0; } int32 GetCurrentProgram() override { return 0; } void SetCurrentProgram(int32) override { } PlugParamIndex GetNumParameters() const override { return kEqNumParameters; } PlugParamValue GetParameter(PlugParamIndex index) override; void SetParameter(PlugParamIndex index, PlugParamValue value) override; void Resume() override; void Suspend() override { m_isResumed = false; } void PositionChanged() override { m_counter = 0; } bool IsInstrument() const override { return false; } bool CanRecieveMidiEvents() override { return false; } bool ShouldProcessSilence() override { return true; } #ifdef MODPLUG_TRACKER CString GetDefaultEffectName() override { return _T("Gargle"); } CString GetParamName(PlugParamIndex param) override; CString GetParamLabel(PlugParamIndex) override; CString GetParamDisplay(PlugParamIndex param) override; CString GetCurrentProgramName() override { return CString(); } void SetCurrentProgramName(const CString &) override { } CString GetProgramName(int32) override { return CString(); } bool HasEditor() const override { return false; } #endif int GetNumInputChannels() const override { return 2; } int GetNumOutputChannels() const override { return 2; } protected: uint32 RateInHertz() const; void RecalculateGargleParams(); }; } // namespace DMO OPENMPT_NAMESPACE_END #endif // !NO_PLUGINS libopenmpt-0.6.1+release.autotools/soundlib/plugins/dmo/I3DL2Reverb.cpp0000644000175000017500000005001314163700363022764 00000000000000/* * I3DL2Reverb.cpp * --------------- * Purpose: Implementation of the DMO I3DL2Reverb DSP (for non-Windows platforms) * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #ifndef NO_PLUGINS #include "../../Sndfile.h" #include "I3DL2Reverb.h" #ifdef MODPLUG_TRACKER #include "../../../sounddsp/Reverb.h" #endif // MODPLUG_TRACKER #include "mpt/base/numbers.hpp" #endif // !NO_PLUGINS OPENMPT_NAMESPACE_BEGIN #ifndef NO_PLUGINS namespace DMO { void I3DL2Reverb::DelayLine::Init(int32 ms, int32 padding, uint32 sampleRate, int32 delayTap) { m_length = Util::muldiv(sampleRate, ms, 1000) + padding; m_position = 0; SetDelayTap(delayTap); assign(m_length, 0.0f); } void I3DL2Reverb::DelayLine::SetDelayTap(int32 delayTap) { if(m_length > 0) m_delayPosition = (delayTap + m_position + m_length) % m_length; } void I3DL2Reverb::DelayLine::Advance() { if(--m_position < 0) m_position += m_length; if(--m_delayPosition < 0) m_delayPosition += m_length; } MPT_FORCEINLINE void I3DL2Reverb::DelayLine::Set(float value) { at(m_position) = value; } float I3DL2Reverb::DelayLine::Get(int32 offset) const { offset = (offset + m_position) % m_length; if(offset < 0) offset += m_length; return at(offset); } MPT_FORCEINLINE float I3DL2Reverb::DelayLine::Get() const { return at(m_delayPosition); } IMixPlugin* I3DL2Reverb::Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct) { return new (std::nothrow) I3DL2Reverb(factory, sndFile, mixStruct); } I3DL2Reverb::I3DL2Reverb(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct) : IMixPlugin(factory, sndFile, mixStruct) { m_param[kI3DL2ReverbRoom] = 0.9f; m_param[kI3DL2ReverbRoomHF] = 0.99f; m_param[kI3DL2ReverbRoomRolloffFactor] = 0.0f; m_param[kI3DL2ReverbDecayTime] = 0.07f; m_param[kI3DL2ReverbDecayHFRatio] = 0.3842105f; m_param[kI3DL2ReverbReflections] = 0.672545433f; m_param[kI3DL2ReverbReflectionsDelay] = 0.233333333f; m_param[kI3DL2ReverbReverb] = 0.85f; m_param[kI3DL2ReverbReverbDelay] = 0.11f; m_param[kI3DL2ReverbDiffusion] = 1.0f; m_param[kI3DL2ReverbDensity] = 1.0f; m_param[kI3DL2ReverbHFReference] = (5000.0f - 20.0f) / 19980.0f; m_param[kI3DL2ReverbQuality] = 2.0f / 3.0f; SetCurrentProgram(m_program); m_mixBuffer.Initialize(2, 2); InsertIntoFactoryList(); } void I3DL2Reverb::Process(float *pOutL, float *pOutR, uint32 numFrames) { if(m_recalcParams) { auto sampleRate = m_effectiveSampleRate; RecalculateI3DL2ReverbParams(); // Resize and clear delay lines if quality has changed if(sampleRate != m_effectiveSampleRate) PositionChanged(); } if(!m_ok || !m_mixBuffer.Ok()) return; const float *in[2] = { m_mixBuffer.GetInputBuffer(0), m_mixBuffer.GetInputBuffer(1) }; float *out[2] = { m_mixBuffer.GetOutputBuffer(0), m_mixBuffer.GetOutputBuffer(1) }; uint32 frames = numFrames; if(!(m_quality & kFullSampleRate) && m_remain && frames > 0) { // Remaining frame from previous render call frames--; *(out[0]++) = m_prevL; *(out[1]++) = m_prevR; in[0]++; in[1]++; m_remain = false; } while(frames > 0) { // Apply room filter and insert into early reflection delay lines const float inL = *(in[0]++); const float inRoomL = (m_filterHist[12] - inL) * m_roomFilter + inL; m_filterHist[12] = inRoomL; m_delayLines[15].Set(inRoomL); const float inR = *(in[1]++); const float inRoomR = (m_filterHist[13] - inR) * m_roomFilter + inR; m_filterHist[13] = inRoomR; m_delayLines[16].Set(inRoomR); // Early reflections (left) float earlyL = m_delayLines[15].Get(m_earlyTaps[0][1]) * 0.68f - m_delayLines[15].Get(m_earlyTaps[0][2]) * 0.5f - m_delayLines[15].Get(m_earlyTaps[0][3]) * 0.62f - m_delayLines[15].Get(m_earlyTaps[0][4]) * 0.5f - m_delayLines[15].Get(m_earlyTaps[0][5]) * 0.62f; if(m_quality & kMoreDelayLines) { float earlyL2 = earlyL; earlyL = m_delayLines[13].Get() + earlyL * 0.618034f; m_delayLines[13].Set(earlyL2 - earlyL * 0.618034f); } const float earlyRefOutL = earlyL * m_ERLevel; m_filterHist[15] = m_delayLines[15].Get(m_earlyTaps[0][0]) + m_filterHist[15]; m_filterHist[16] = m_delayLines[16].Get(m_earlyTaps[1][0]) + m_filterHist[16]; // Lots of slightly different copy-pasta ahead float reverbL1, reverbL2, reverbL3, reverbR1, reverbR2, reverbR3; reverbL1 = -m_filterHist[15] * 0.707f; reverbL2 = m_filterHist[16] * 0.707f + reverbL1; reverbR2 = reverbL1 - m_filterHist[16] * 0.707f; m_filterHist[5] = (m_filterHist[5] - m_delayLines[5].Get()) * m_delayCoeffs[5][1] + m_delayLines[5].Get(); reverbL1 = m_filterHist[5] * m_delayCoeffs[5][0] + reverbL2 * m_diffusion; m_delayLines[5].Set(reverbL2 - reverbL1 * m_diffusion); reverbL2 = reverbL1; reverbL3 = -0.15f * reverbL1; m_filterHist[4] = (m_filterHist[4] - m_delayLines[4].Get()) * m_delayCoeffs[4][1] + m_delayLines[4].Get(); reverbL1 = m_filterHist[4] * m_delayCoeffs[4][0] + reverbL2 * m_diffusion; m_delayLines[4].Set(reverbL2 - reverbL1 * m_diffusion); reverbL2 = reverbL1; reverbL3 -= reverbL1 * 0.2f; if(m_quality & kMoreDelayLines) { m_filterHist[3] = (m_filterHist[3] - m_delayLines[3].Get()) * m_delayCoeffs[3][1] + m_delayLines[3].Get(); reverbL1 = m_filterHist[3] * m_delayCoeffs[3][0] + reverbL2 * m_diffusion; m_delayLines[3].Set(reverbL2 - reverbL1 * m_diffusion); reverbL2 = reverbL1; reverbL3 += 0.35f * reverbL1; m_filterHist[2] = (m_filterHist[2] - m_delayLines[2].Get()) * m_delayCoeffs[2][1] + m_delayLines[2].Get(); reverbL1 = m_filterHist[2] * m_delayCoeffs[2][0] + reverbL2 * m_diffusion; m_delayLines[2].Set(reverbL2 - reverbL1 * m_diffusion); reverbL2 = reverbL1; reverbL3 -= reverbL1 * 0.38f; } m_delayLines[17].Set(reverbL2); reverbL1 = m_delayLines[17].Get() * m_delayCoeffs[12][0]; m_filterHist[17] = (m_filterHist[17] - reverbL1) * m_delayCoeffs[12][1] + reverbL1; m_filterHist[1] = (m_filterHist[1] - m_delayLines[1].Get()) * m_delayCoeffs[1][1] + m_delayLines[1].Get(); reverbL1 = m_filterHist[17] * m_diffusion + m_filterHist[1] * m_delayCoeffs[1][0]; m_delayLines[1].Set(m_filterHist[17] - reverbL1 * m_diffusion); reverbL2 = reverbL1; float reverbL4 = reverbL1 * 0.38f; m_filterHist[0] = (m_filterHist[0] - m_delayLines[0].Get()) * m_delayCoeffs[0][1] + m_delayLines[0].Get(); reverbL1 = m_filterHist[0] * m_delayCoeffs[0][0] + reverbL2 * m_diffusion; m_delayLines[0].Set(reverbL2 - reverbL1 * m_diffusion); reverbL3 -= reverbL1 * 0.38f; m_filterHist[15] = reverbL1; // Early reflections (right) float earlyR = m_delayLines[16].Get(m_earlyTaps[1][1]) * 0.707f - m_delayLines[16].Get(m_earlyTaps[1][2]) * 0.6f - m_delayLines[16].Get(m_earlyTaps[1][3]) * 0.5f - m_delayLines[16].Get(m_earlyTaps[1][4]) * 0.6f - m_delayLines[16].Get(m_earlyTaps[1][5]) * 0.5f; if(m_quality & kMoreDelayLines) { float earlyR2 = earlyR; earlyR = m_delayLines[14].Get() + earlyR * 0.618034f; m_delayLines[14].Set(earlyR2 - earlyR * 0.618034f); } const float earlyRefOutR = earlyR * m_ERLevel; m_filterHist[11] = (m_filterHist[11] - m_delayLines[11].Get()) * m_delayCoeffs[11][1] + m_delayLines[11].Get(); reverbR1 = m_filterHist[11] * m_delayCoeffs[11][0] + reverbR2 * m_diffusion; m_delayLines[11].Set(reverbR2 - reverbR1 * m_diffusion); reverbR2 = reverbR1; m_filterHist[10] = (m_filterHist[10] - m_delayLines[10].Get()) * m_delayCoeffs[10][1] + m_delayLines[10].Get(); reverbR1 = m_filterHist[10] * m_delayCoeffs[10][0] + reverbR2 * m_diffusion; m_delayLines[10].Set(reverbR2 - reverbR1 * m_diffusion); reverbR3 = reverbL4 - reverbR2 * 0.15f - reverbR1 * 0.2f; reverbR2 = reverbR1; if(m_quality & kMoreDelayLines) { m_filterHist[9] = (m_filterHist[9] - m_delayLines[9].Get()) * m_delayCoeffs[9][1] + m_delayLines[9].Get(); reverbR1 = m_filterHist[9] * m_delayCoeffs[9][0] + reverbR2 * m_diffusion; m_delayLines[9].Set(reverbR2 - reverbR1 * m_diffusion); reverbR2 = reverbR1; reverbR3 += reverbR1 * 0.35f; m_filterHist[8] = (m_filterHist[8] - m_delayLines[8].Get()) * m_delayCoeffs[8][1] + m_delayLines[8].Get(); reverbR1 = m_filterHist[8] * m_delayCoeffs[8][0] + reverbR2 * m_diffusion; m_delayLines[8].Set(reverbR2 - reverbR1 * m_diffusion); reverbR2 = reverbR1; reverbR3 -= reverbR1 * 0.38f; } m_delayLines[18].Set(reverbR2); reverbR1 = m_delayLines[18].Get() * m_delayCoeffs[12][0]; m_filterHist[18] = (m_filterHist[18] - reverbR1) * m_delayCoeffs[12][1] + reverbR1; m_filterHist[7] = (m_filterHist[7] - m_delayLines[7].Get()) * m_delayCoeffs[7][1] + m_delayLines[7].Get(); reverbR1 = m_filterHist[18] * m_diffusion + m_filterHist[7] * m_delayCoeffs[7][0]; m_delayLines[7].Set(m_filterHist[18] - reverbR1 * m_diffusion); reverbR2 = reverbR1; float lateRevOutL = (reverbL3 + reverbR1 * 0.38f) * m_ReverbLevelL; m_filterHist[6] = (m_filterHist[6] - m_delayLines[6].Get()) * m_delayCoeffs[6][1] + m_delayLines[6].Get(); reverbR1 = m_filterHist[6] * m_delayCoeffs[6][0] + reverbR2 * m_diffusion; m_delayLines[6].Set(reverbR2 - reverbR1 * m_diffusion); m_filterHist[16] = reverbR1; float lateRevOutR = (reverbR3 - reverbR1 * 0.38f) * m_ReverbLevelR; float outL = earlyRefOutL + lateRevOutL; float outR = earlyRefOutR + lateRevOutR; for(auto &line : m_delayLines) line.Advance(); if(!(m_quality & kFullSampleRate)) { *(out[0]++) = (outL + m_prevL) * 0.5f; *(out[1]++) = (outR + m_prevR) * 0.5f; m_prevL = outL; m_prevR = outR; in[0]++; in[1]++; if(frames-- == 1) { m_remain = true; break; } } *(out[0]++) = outL; *(out[1]++) = outR; frames--; } ProcessMixOps(pOutL, pOutR, m_mixBuffer.GetOutputBuffer(0), m_mixBuffer.GetOutputBuffer(1), numFrames); } int32 I3DL2Reverb::GetNumPrograms() const { #ifdef MODPLUG_TRACKER return NUM_REVERBTYPES; #else return 0; #endif } void I3DL2Reverb::SetCurrentProgram(int32 program) { #ifdef MODPLUG_TRACKER if(program < NUM_REVERBTYPES) { m_program = program; const auto &preset = *GetReverbPreset(m_program); m_param[kI3DL2ReverbRoom] = (preset.lRoom + 10000) / 10000.0f; m_param[kI3DL2ReverbRoomHF] = (preset.lRoomHF + 10000) / 10000.0f; m_param[kI3DL2ReverbRoomRolloffFactor] = 0.0f; m_param[kI3DL2ReverbDecayTime] = (preset.flDecayTime - 0.1f) / 19.9f; m_param[kI3DL2ReverbDecayHFRatio] = (preset.flDecayHFRatio - 0.1f) / 1.9f; m_param[kI3DL2ReverbReflections] = (preset.lReflections + 10000) / 11000.0f; m_param[kI3DL2ReverbReflectionsDelay] = preset.flReflectionsDelay / 0.3f; m_param[kI3DL2ReverbReverb] = (preset.lReverb + 10000) / 12000.0f; m_param[kI3DL2ReverbReverbDelay] = preset.flReverbDelay / 0.1f; m_param[kI3DL2ReverbDiffusion] = preset.flDiffusion / 100.0f; m_param[kI3DL2ReverbDensity] = preset.flDensity / 100.0f; m_param[kI3DL2ReverbHFReference] = (5000.0f - 20.0f) / 19980.0f; RecalculateI3DL2ReverbParams(); } #else MPT_UNUSED_VARIABLE(program); #endif } PlugParamValue I3DL2Reverb::GetParameter(PlugParamIndex index) { if(index < kI3DL2ReverbNumParameters) { return m_param[index]; } return 0; } void I3DL2Reverb::SetParameter(PlugParamIndex index, PlugParamValue value) { if(index < kI3DL2ReverbNumParameters) { value = mpt::safe_clamp(value, 0.0f, 1.0f); if(index == kI3DL2ReverbQuality) value = mpt::round(value * 3.0f) / 3.0f; m_param[index] = value; m_recalcParams = true; } } void I3DL2Reverb::Resume() { RecalculateI3DL2ReverbParams(); PositionChanged(); m_isResumed = true; } void I3DL2Reverb::PositionChanged() { MemsetZero(m_filterHist); m_prevL = 0; m_prevR = 0; m_remain = false; try { uint32 sampleRate = static_cast(m_effectiveSampleRate); m_delayLines[0].Init(67, 5, sampleRate, m_delayTaps[0]); m_delayLines[1].Init(62, 5, sampleRate, m_delayTaps[1]); m_delayLines[2].Init(53, 5, sampleRate, m_delayTaps[2]); m_delayLines[3].Init(43, 5, sampleRate, m_delayTaps[3]); m_delayLines[4].Init(32, 5, sampleRate, m_delayTaps[4]); m_delayLines[5].Init(22, 5, sampleRate, m_delayTaps[5]); m_delayLines[6].Init(75, 5, sampleRate, m_delayTaps[6]); m_delayLines[7].Init(69, 5, sampleRate, m_delayTaps[7]); m_delayLines[8].Init(60, 5, sampleRate, m_delayTaps[8]); m_delayLines[9].Init(48, 5, sampleRate, m_delayTaps[9]); m_delayLines[10].Init(36, 5, sampleRate, m_delayTaps[10]); m_delayLines[11].Init(25, 5, sampleRate, m_delayTaps[11]); m_delayLines[12].Init(0, 0, 0); // Dummy for array index consistency with both tap and coefficient arrays m_delayLines[13].Init(3, 0, sampleRate, m_delayTaps[13]); m_delayLines[14].Init(3, 0, sampleRate, m_delayTaps[14]); m_delayLines[15].Init(407, 1, sampleRate); m_delayLines[16].Init(400, 1, sampleRate); m_delayLines[17].Init(10, 0, sampleRate, -1); m_delayLines[18].Init(10, 0, sampleRate, -1); m_ok = true; } catch(mpt::out_of_memory e) { m_ok = false; mpt::delete_out_of_memory(e); } } #ifdef MODPLUG_TRACKER CString I3DL2Reverb::GetParamName(PlugParamIndex param) { switch(param) { case kI3DL2ReverbRoom: return _T("Room"); case kI3DL2ReverbRoomHF: return _T("RoomHF"); case kI3DL2ReverbRoomRolloffFactor: return _T("RoomRolloffFactor"); case kI3DL2ReverbDecayTime: return _T("DecayTime"); case kI3DL2ReverbDecayHFRatio: return _T("DecayHFRatio"); case kI3DL2ReverbReflections: return _T("Reflections"); case kI3DL2ReverbReflectionsDelay: return _T("ReflectionsDelay"); case kI3DL2ReverbReverb: return _T("Reverb"); case kI3DL2ReverbReverbDelay: return _T("ReverbDelay"); case kI3DL2ReverbDiffusion: return _T("Diffusion"); case kI3DL2ReverbDensity: return _T("Density"); case kI3DL2ReverbHFReference: return _T("HFRefrence"); case kI3DL2ReverbQuality: return _T("Quality"); } return CString(); } CString I3DL2Reverb::GetParamLabel(PlugParamIndex param) { switch(param) { case kI3DL2ReverbRoom: case kI3DL2ReverbRoomHF: case kI3DL2ReverbReflections: case kI3DL2ReverbReverb: return _T("dB"); case kI3DL2ReverbDecayTime: case kI3DL2ReverbReflectionsDelay: case kI3DL2ReverbReverbDelay: return _T("s"); case kI3DL2ReverbDiffusion: case kI3DL2ReverbDensity: return _T("%"); case kI3DL2ReverbHFReference: return _T("Hz"); } return CString(); } CString I3DL2Reverb::GetParamDisplay(PlugParamIndex param) { static constexpr const TCHAR * const modes[] = { _T("LQ"), _T("LQ+"), _T("HQ"), _T("HQ+") }; float value = m_param[param]; switch(param) { case kI3DL2ReverbRoom: value = Room() * 0.01f; break; case kI3DL2ReverbRoomHF: value = RoomHF() * 0.01f; break; case kI3DL2ReverbRoomRolloffFactor: value = RoomRolloffFactor(); break; case kI3DL2ReverbDecayTime: value = DecayTime(); break; case kI3DL2ReverbDecayHFRatio: value = DecayHFRatio(); break; case kI3DL2ReverbReflections: value = Reflections() * 0.01f; break; case kI3DL2ReverbReflectionsDelay: value = ReflectionsDelay(); break; case kI3DL2ReverbReverb: value = Reverb() * 0.01f; break; case kI3DL2ReverbReverbDelay: value = ReverbDelay(); break; case kI3DL2ReverbDiffusion: value = Diffusion(); break; case kI3DL2ReverbDensity: value = Density(); break; case kI3DL2ReverbHFReference: value = HFReference(); break; case kI3DL2ReverbQuality: return modes[Quality() % 4u]; } CString s; s.Format(_T("%.2f"), value); return s; } CString I3DL2Reverb::GetCurrentProgramName() { return GetProgramName(m_program); } CString I3DL2Reverb::GetProgramName(int32 program) { return mpt::ToCString(GetReverbPresetName(program)); } #endif // MODPLUG_TRACKER void I3DL2Reverb::RecalculateI3DL2ReverbParams() { m_quality = Quality(); m_effectiveSampleRate = static_cast(m_SndFile.GetSampleRate() / ((m_quality & kFullSampleRate) ? 1u : 2u)); // Diffusion m_diffusion = Diffusion() * (0.618034f / 100.0f); // Early Reflection Level m_ERLevel = std::min(std::pow(10.0f, (Room() + Reflections()) / (100.0f * 20.0f)), 1.0f) * 0.761f; // Room Filter float roomHF = std::pow(10.0f, RoomHF() / 100.0f / 10.0f); if(roomHF == 1.0f) { m_roomFilter = 0.0f; } else { float freq = std::cos(HFReference() * (2.0f * mpt::numbers::pi_v) / m_effectiveSampleRate); float roomFilter = (freq * (roomHF + roomHF) - 2.0f + std::sqrt(freq * (roomHF * roomHF * freq * 4.0f) + roomHF * 8.0f - roomHF * roomHF * 4.0f - roomHF * freq * 8.0f)) / (roomHF + roomHF - 2.0f); m_roomFilter = Clamp(roomFilter, 0.0f, 1.0f); } SetDelayTaps(); SetDecayCoeffs(); m_recalcParams = false; } void I3DL2Reverb::SetDelayTaps() { // Early reflections static constexpr float delays[] = { 1.0000f, 1.0000f, 0.0000f, 0.1078f, 0.1768f, 0.2727f, 0.3953f, 0.5386f, 0.6899f, 0.8306f, 0.9400f, 0.9800f, }; const float sampleRate = m_effectiveSampleRate; const float reflectionsDelay = ReflectionsDelay(); const float reverbDelay = std::max(ReverbDelay(), 5.0f / 1000.0f); m_earlyTaps[0][0] = static_cast((reverbDelay + reflectionsDelay + 7.0f / 1000.0f) * sampleRate); for(uint32 i = 1; i < 12; i++) { m_earlyTaps[i % 2u][i / 2u] = static_cast((reverbDelay * delays[i] + reflectionsDelay) * sampleRate); } // Late reflections float density = std::min((Density() / 100.0f + 0.1f) * 0.9091f, 1.0f); float delayL = density * 67.0f / 1000.0f * sampleRate; float delayR = density * 75.0f / 1000.0f * sampleRate; for(int i = 0, power = 0; i < 6; i++) { power += i; float factor = std::pow(0.93f, static_cast(power)); m_delayTaps[i + 0] = static_cast(delayL * factor); m_delayTaps[i + 6] = static_cast(delayR * factor); } m_delayTaps[12] = static_cast(10.0f / 1000.0f * sampleRate); // Early reflections (extra delay lines) m_delayTaps[13] = static_cast(3.25f / 1000.0f * sampleRate); m_delayTaps[14] = static_cast(3.53f / 1000.0f * sampleRate); for(std::size_t d = 0; d < std::size(m_delayTaps); d++) m_delayLines[d].SetDelayTap(m_delayTaps[d]); } void I3DL2Reverb::SetDecayCoeffs() { float levelLtmp = 1.0f, levelRtmp = 1.0f; float levelL = 0.0f, levelR = 0.0f; levelLtmp *= CalcDecayCoeffs(5); levelRtmp *= CalcDecayCoeffs(11); levelL += levelLtmp * 0.0225f; levelR += levelRtmp * 0.0225f; levelLtmp *= CalcDecayCoeffs(4); levelRtmp *= CalcDecayCoeffs(10); levelL += levelLtmp * 0.04f; levelR += levelRtmp * 0.04f; if(m_quality & kMoreDelayLines) { levelLtmp *= CalcDecayCoeffs(3); levelRtmp *= CalcDecayCoeffs(9); levelL += levelLtmp * 0.1225f; levelR += levelRtmp * 0.1225f; levelLtmp *= CalcDecayCoeffs(2); levelRtmp *= CalcDecayCoeffs(8); levelL += levelLtmp * 0.1444f; levelR += levelRtmp * 0.1444f; } CalcDecayCoeffs(12); levelLtmp *= m_delayCoeffs[12][0] * m_delayCoeffs[12][0]; levelRtmp *= m_delayCoeffs[12][0] * m_delayCoeffs[12][0]; levelLtmp *= CalcDecayCoeffs(1); levelRtmp *= CalcDecayCoeffs(7); levelL += levelRtmp * 0.1444f; levelR += levelLtmp * 0.1444f; levelLtmp *= CalcDecayCoeffs(0); levelRtmp *= CalcDecayCoeffs(6); levelL += levelLtmp * 0.1444f; levelR += levelRtmp * 0.1444f; // Final Reverb Level float level = std::min(std::pow(10.0f, (Room() + Reverb()) / (100.0f * 20.0f)), 1.0f); float monoInv = 1.0f - ((levelLtmp + levelRtmp) * 0.5f); m_ReverbLevelL = level * std::sqrt(monoInv / levelL); m_ReverbLevelR = level * std::sqrt(monoInv / levelR); } float I3DL2Reverb::CalcDecayCoeffs(int32 index) { float hfRef = (2.0f * mpt::numbers::pi_v) / m_effectiveSampleRate * HFReference(); float decayHFRatio = DecayHFRatio(); if(decayHFRatio > 1.0f) hfRef = mpt::numbers::pi_v; float c1 = std::pow(10.0f, ((m_delayTaps[index] / m_effectiveSampleRate) * -60.0f / DecayTime()) / 20.0f); float c2 = 0.0f; float c21 = (std::pow(c1, 2.0f - 2.0f / decayHFRatio) - 1.0f) / (1.0f - std::cos(hfRef)); if(c21 != 0 && std::isfinite(c21)) { float c22 = -2.0f * c21 - 2.0f; float c23sq = c22 * c22 - c21 * c21 * 4.0f; float c23 = c23sq > 0.0f ? std::sqrt(c23sq) : 0.0f; c2 = (c23 - c22) / (c21 + c21); if(std::abs(c2) > 1.0f) c2 = (-c22 - c23) / (c21 + c21); c2 = mpt::sanitize_nan(c2); } m_delayCoeffs[index][0] = c1; m_delayCoeffs[index][1] = c2; c1 *= c1; float diff2 = m_diffusion * m_diffusion; return diff2 + c1 / (1.0f - diff2 * c1) * (1.0f - diff2) * (1.0f - diff2); } } // namespace DMO #else MPT_MSVC_WORKAROUND_LNK4221(I3DL2Reverb) #endif // !NO_PLUGINS OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/plugins/dmo/I3DL2Reverb.h0000644000175000017500000001175114052666041022440 00000000000000/* * I3DL2Reverb.h * ------------- * Purpose: Implementation of the DMO I3DL2Reverb DSP (for non-Windows platforms) * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #ifndef NO_PLUGINS #include "../PlugInterface.h" OPENMPT_NAMESPACE_BEGIN namespace DMO { class I3DL2Reverb final : public IMixPlugin { protected: enum Parameters { kI3DL2ReverbRoom = 0, kI3DL2ReverbRoomHF, kI3DL2ReverbRoomRolloffFactor, // Doesn't actually do anything :) kI3DL2ReverbDecayTime, kI3DL2ReverbDecayHFRatio, kI3DL2ReverbReflections, kI3DL2ReverbReflectionsDelay, kI3DL2ReverbReverb, kI3DL2ReverbReverbDelay, kI3DL2ReverbDiffusion, kI3DL2ReverbDensity, kI3DL2ReverbHFReference, kI3DL2ReverbQuality, kI3DL2ReverbNumParameters }; enum QualityFlags { kMoreDelayLines = 0x01, kFullSampleRate = 0x02, }; class DelayLine : private std::vector { int32 m_length; int32 m_position; int32 m_delayPosition; public: void Init(int32 ms, int32 padding, uint32 sampleRate, int32 delayTap = 0); void SetDelayTap(int32 delayTap); void Advance(); void Set(float value); float Get(int32 offset) const; float Get() const; }; float m_param[kI3DL2ReverbNumParameters]; int32 m_program = 0; // Calculated parameters uint32 m_quality; float m_effectiveSampleRate; float m_diffusion; float m_roomFilter; float m_ERLevel; float m_ReverbLevelL; float m_ReverbLevelR; int32 m_delayTaps[15]; // 6*L + 6*R + LR + Early L + Early R int32 m_earlyTaps[2][6]; float m_delayCoeffs[13][2]; // State DelayLine m_delayLines[19]; float m_filterHist[19]; // Remaining frame for downsampled reverb float m_prevL; float m_prevR; bool m_remain = false; bool m_ok = false, m_recalcParams = true; public: static IMixPlugin* Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct); I3DL2Reverb(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct); void Release() override { delete this; } int32 GetUID() const override { return 0xEF985E71; } int32 GetVersion() const override { return 0; } void Idle() override { } uint32 GetLatency() const override { return 0; } void Process(float *pOutL, float *pOutR, uint32 numFrames) override; float RenderSilence(uint32) override { return 0.0f; } int32 GetNumPrograms() const override; int32 GetCurrentProgram() override { return m_program; } // cppcheck-suppress virtualCallInConstructor void SetCurrentProgram(int32) override; PlugParamIndex GetNumParameters() const override { return kI3DL2ReverbNumParameters; } PlugParamValue GetParameter(PlugParamIndex index) override; void SetParameter(PlugParamIndex index, PlugParamValue value) override; void Resume() override; void Suspend() override { m_isResumed = false; } void PositionChanged() override; bool IsInstrument() const override { return false; } bool CanRecieveMidiEvents() override { return false; } bool ShouldProcessSilence() override { return true; } #ifdef MODPLUG_TRACKER CString GetDefaultEffectName() override { return _T("I3DL2Reverb"); } CString GetParamName(PlugParamIndex param) override; CString GetParamLabel(PlugParamIndex) override; CString GetParamDisplay(PlugParamIndex param) override; CString GetCurrentProgramName() override; void SetCurrentProgramName(const CString &) override { } CString GetProgramName(int32 program) override; bool HasEditor() const override { return false; } #endif void BeginSetProgram(int32) override { } void EndSetProgram() override { } int GetNumInputChannels() const override { return 2; } int GetNumOutputChannels() const override { return 2; } protected: float Room() const { return -10000.0f + m_param[kI3DL2ReverbRoom] * 10000.0f; } float RoomHF() const { return -10000.0f + m_param[kI3DL2ReverbRoomHF] * 10000.0f; } float RoomRolloffFactor() const { return m_param[kI3DL2ReverbRoomRolloffFactor] * 10.0f; } float DecayTime() const { return 0.1f + m_param[kI3DL2ReverbDecayTime] * 19.9f; } float DecayHFRatio() const { return 0.1f + m_param[kI3DL2ReverbDecayHFRatio] * 1.9f; } float Reflections() const { return -10000.0f + m_param[kI3DL2ReverbReflections] * 11000.0f; } float ReflectionsDelay() const { return m_param[kI3DL2ReverbReflectionsDelay] * 0.3f; } float Reverb() const { return -10000.0f + m_param[kI3DL2ReverbReverb] * 12000.0f; } float ReverbDelay() const { return m_param[kI3DL2ReverbReverbDelay] * 0.1f; } float Diffusion() const { return m_param[kI3DL2ReverbDiffusion] * 100.0f; } float Density() const { return m_param[kI3DL2ReverbDensity] * 100.0f; } float HFReference() const { return 20.0f + m_param[kI3DL2ReverbHFReference] * 19980.0f; } uint32 Quality() const { return mpt::saturate_round(m_param[kI3DL2ReverbQuality] * 3.0f); } void RecalculateI3DL2ReverbParams(); void SetDelayTaps(); void SetDecayCoeffs(); float CalcDecayCoeffs(int32 index); }; } // namespace DMO OPENMPT_NAMESPACE_END #endif // !NO_PLUGINS libopenmpt-0.6.1+release.autotools/soundlib/plugins/dmo/ParamEq.cpp0000644000175000017500000001034414155206341022370 00000000000000/* * ParamEq.cpp * ----------- * Purpose: Implementation of the DMO Parametric Equalizer DSP (for non-Windows platforms) * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #ifndef NO_PLUGINS #include "../../Sndfile.h" #include "ParamEq.h" #include "mpt/base/numbers.hpp" #endif // !NO_PLUGINS OPENMPT_NAMESPACE_BEGIN #ifndef NO_PLUGINS namespace DMO { IMixPlugin* ParamEq::Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct) { return new (std::nothrow) ParamEq(factory, sndFile, mixStruct); } ParamEq::ParamEq(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct) : IMixPlugin(factory, sndFile, mixStruct) , m_maxFreqParam(1.0f) { m_param[kEqCenter] = (8000.0f - 80.0f) / 15920.0f; m_param[kEqBandwidth] = 0.314286f; m_param[kEqGain] = 0.5f; m_mixBuffer.Initialize(2, 2); InsertIntoFactoryList(); } void ParamEq::Process(float *pOutL, float *pOutR, uint32 numFrames) { if(!m_mixBuffer.Ok()) return; const float *in[2] = { m_mixBuffer.GetInputBuffer(0), m_mixBuffer.GetInputBuffer(1) }; float *out[2] = { m_mixBuffer.GetOutputBuffer(0), m_mixBuffer.GetOutputBuffer(1) }; if(m_param[kEqGain] == 0.5f) { memcpy(out[0], in[0], numFrames * sizeof(float)); memcpy(out[1], in[1], numFrames * sizeof(float)); } else { for(uint32 i = numFrames; i != 0; i--) { for(uint8 channel = 0; channel < 2; channel++) { float x = *(in[channel])++; float y = b0DIVa0 * x + b1DIVa0 * x1[channel] + b2DIVa0 * x2[channel] - a1DIVa0 * y1[channel] - a2DIVa0 * y2[channel]; x2[channel] = x1[channel]; x1[channel] = x; y2[channel] = y1[channel]; y1[channel] = y; *(out[channel])++ = y; } } } ProcessMixOps(pOutL, pOutR, m_mixBuffer.GetOutputBuffer(0), m_mixBuffer.GetOutputBuffer(1), numFrames); } PlugParamValue ParamEq::GetParameter(PlugParamIndex index) { if(index < kEqNumParameters) { return m_param[index]; } return 0; } void ParamEq::SetParameter(PlugParamIndex index, PlugParamValue value) { if(index < kEqNumParameters) { value = mpt::safe_clamp(value, 0.0f, 1.0f); m_param[index] = value; RecalculateEqParams(); } } void ParamEq::Resume() { m_isResumed = true; // Limit center frequency to a third of the sampling rate. m_maxFreqParam = Clamp((m_SndFile.GetSampleRate() / 3.0f - 80.0f) / 15920.0f, 0.0f, 1.0f); RecalculateEqParams(); PositionChanged(); } void ParamEq::PositionChanged() { // Reset filter state x1[0] = x2[0] = 0; x1[1] = x2[1] = 0; y1[0] = y2[0] = 0; y1[1] = y2[1] = 0; } #ifdef MODPLUG_TRACKER CString ParamEq::GetParamName(PlugParamIndex param) { switch(param) { case kEqCenter: return _T("Center"); case kEqBandwidth: return _T("Bandwidth"); case kEqGain: return _T("Gain"); } return CString(); } CString ParamEq::GetParamLabel(PlugParamIndex param) { switch(param) { case kEqCenter: return _T("Hz"); case kEqBandwidth: return _T("Semitones"); case kEqGain: return _T("dB"); } return CString(); } CString ParamEq::GetParamDisplay(PlugParamIndex param) { float value = 0.0f; switch(param) { case kEqCenter: value = FreqInHertz(); break; case kEqBandwidth: value = BandwidthInSemitones(); break; case kEqGain: value = GainInDecibel(); break; } CString s; s.Format(_T("%.2f"), value); return s; } #endif // MODPLUG_TRACKER void ParamEq::RecalculateEqParams() { LimitMax(m_param[kEqCenter], m_maxFreqParam); const float freq = FreqInHertz() / m_SndFile.GetSampleRate(); const float a = std::pow(10.0f, GainInDecibel() / 40.0f); const float w0 = 2.0f * mpt::numbers::pi_v * freq; const float sinW0 = std::sin(w0); const float cosW0 = std::cos(w0); const float alpha = sinW0 * std::sinh((BandwidthInSemitones() * (mpt::numbers::ln2_v / 24.0f)) * w0 / sinW0); const float b0 = 1.0f + alpha * a; const float b1 = -2.0f * cosW0; const float b2 = 1.0f - alpha * a; const float a0 = 1.0f + alpha / a; const float a1 = -2.0f * cosW0; const float a2 = 1.0f - alpha / a; b0DIVa0 = b0 / a0; b1DIVa0 = b1 / a0; b2DIVa0 = b2 / a0; a1DIVa0 = a1 / a0; a2DIVa0 = a2 / a0; } } // namespace DMO #else MPT_MSVC_WORKAROUND_LNK4221(ParamEq) #endif // !NO_PLUGINS OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/plugins/dmo/ParamEq.h0000644000175000017500000000542413647400540022042 00000000000000/* * ParamEq.h * --------- * Purpose: Implementation of the DMO Parametric Equalizer DSP (for non-Windows platforms) * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #ifndef NO_PLUGINS #include "../PlugInterface.h" OPENMPT_NAMESPACE_BEGIN namespace DMO { class ParamEq final : public IMixPlugin { protected: enum Parameters { kEqCenter = 0, kEqBandwidth, kEqGain, kEqNumParameters }; float m_param[kEqNumParameters]; // Equalizer coefficients float b0DIVa0, b1DIVa0, b2DIVa0, a1DIVa0, a2DIVa0; // Equalizer memory float x1[2], x2[2]; float y1[2], y2[2]; float m_maxFreqParam; public: static IMixPlugin* Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct); ParamEq(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct); void Release() override { delete this; } int32 GetUID() const override { return 0x120CED89; } int32 GetVersion() const override { return 0; } void Idle() override { } uint32 GetLatency() const override { return 0; } void Process(float *pOutL, float *pOutR, uint32 numFrames) override; float RenderSilence(uint32) override { return 0.0f; } int32 GetNumPrograms() const override { return 0; } int32 GetCurrentProgram() override { return 0; } void SetCurrentProgram(int32) override { } PlugParamIndex GetNumParameters() const override { return kEqNumParameters; } PlugParamValue GetParameter(PlugParamIndex index) override; void SetParameter(PlugParamIndex index, PlugParamValue value) override; void Resume() override; void Suspend() override { m_isResumed = false; } void PositionChanged() override; bool IsInstrument() const override { return false; } bool CanRecieveMidiEvents() override { return false; } bool ShouldProcessSilence() override { return true; } #ifdef MODPLUG_TRACKER CString GetDefaultEffectName() override { return _T("ParamEq"); } CString GetParamName(PlugParamIndex param) override; CString GetParamLabel(PlugParamIndex) override; CString GetParamDisplay(PlugParamIndex param) override; CString GetCurrentProgramName() override { return CString(); } void SetCurrentProgramName(const CString &) override { } CString GetProgramName(int32) override { return CString(); } bool HasEditor() const override { return false; } #endif int GetNumInputChannels() const override { return 2; } int GetNumOutputChannels() const override { return 2; } protected: float BandwidthInSemitones() const { return 1.0f + m_param[kEqBandwidth] * 35.0f; } float FreqInHertz() const { return 80.0f + m_param[kEqCenter] * 15920.0f; } float GainInDecibel() const { return (m_param[kEqGain] - 0.5f) * 30.0f; } void RecalculateEqParams(); }; } // namespace DMO OPENMPT_NAMESPACE_END #endif // !NO_PLUGINS libopenmpt-0.6.1+release.autotools/soundlib/plugins/dmo/WavesReverb.cpp0000644000175000017500000001660014155206341023276 00000000000000/* * WavesReverb.cpp * --------------- * Purpose: Implementation of the DMO WavesReverb DSP (for non-Windows platforms) * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #ifndef NO_PLUGINS #include "../../Sndfile.h" #include "WavesReverb.h" #endif // !NO_PLUGINS OPENMPT_NAMESPACE_BEGIN #ifndef NO_PLUGINS namespace DMO { IMixPlugin* WavesReverb::Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct) { return new (std::nothrow) WavesReverb(factory, sndFile, mixStruct); } WavesReverb::WavesReverb(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct) : IMixPlugin(factory, sndFile, mixStruct) { m_param[kRvbInGain] = 1.0f; m_param[kRvbReverbMix] = 1.0f; m_param[kRvbReverbTime] = 1.0f / 3.0f; m_param[kRvbHighFreqRTRatio] = 0.0f; m_mixBuffer.Initialize(2, 2); InsertIntoFactoryList(); } void WavesReverb::Process(float *pOutL, float *pOutR, uint32 numFrames) { if(!m_mixBuffer.Ok()) return; const float *in[2] = { m_mixBuffer.GetInputBuffer(0), m_mixBuffer.GetInputBuffer(1) }; float *out[2] = { m_mixBuffer.GetOutputBuffer(0), m_mixBuffer.GetOutputBuffer(1) }; uint32 combPos = m_state.combPos, allpassPos = m_state.allpassPos; uint32 delay0 = (m_delay[0] + combPos + 1) & 0xFFF; uint32 delay1 = (m_delay[1] + combPos + 1) & 0xFFF; uint32 delay2 = (m_delay[2] + combPos + 1) & 0xFFF; uint32 delay3 = (m_delay[3] + combPos + 1) & 0xFFF; uint32 delay4 = (m_delay[4] + allpassPos) & 0x3FF; uint32 delay5 = (m_delay[5] + allpassPos) & 0x3FF; float delay0old = m_state.comb[delay0][0]; float delay1old = m_state.comb[delay1][1]; float delay2old = m_state.comb[delay2][2]; float delay3old = m_state.comb[delay3][3]; for(uint32 i = numFrames; i != 0; i--) { const float leftIn = *(in[0])++ + 1e-30f; // Prevent denormals const float rightIn = *(in[1])++ + 1e-30f; // Prevent denormals // Advance buffer index for the four comb filters delay0 = (delay0 - 1) & 0xFFF; delay1 = (delay1 - 1) & 0xFFF; delay2 = (delay2 - 1) & 0xFFF; delay3 = (delay3 - 1) & 0xFFF; float &delay0new = m_state.comb[delay0][0]; float &delay1new = m_state.comb[delay1][1]; float &delay2new = m_state.comb[delay2][2]; float &delay3new = m_state.comb[delay3][3]; float r1, r2; r1 = delay1new * 0.61803401f + m_state.allpass1[delay4][0] * m_coeffs[0]; r2 = m_state.allpass1[delay4][1] * m_coeffs[0] - delay0new * 0.61803401f; m_state.allpass1[allpassPos][0] = r2 * 0.61803401f + delay0new; m_state.allpass1[allpassPos][1] = delay1new - r1 * 0.61803401f; delay0new = r1; delay1new = r2; r1 = delay3new * 0.61803401f + m_state.allpass2[delay5][0] * m_coeffs[1]; r2 = m_state.allpass2[delay5][1] * m_coeffs[1] - delay2new * 0.61803401f; m_state.allpass2[allpassPos][0] = r2 * 0.61803401f + delay2new; m_state.allpass2[allpassPos][1] = delay3new - r1 * 0.61803401f; delay2new = r1; delay3new = r2; *(out[0])++ = (leftIn * m_dryFactor) + delay0new + delay2new; *(out[1])++ = (rightIn * m_dryFactor) + delay1new + delay3new; const float leftWet = leftIn * m_wetFactor; const float rightWet = rightIn * m_wetFactor; m_state.comb[combPos][0] = (delay0new * m_coeffs[2]) + (delay0old * m_coeffs[3]) + leftWet; m_state.comb[combPos][1] = (delay1new * m_coeffs[4]) + (delay1old * m_coeffs[5]) + rightWet; m_state.comb[combPos][2] = (delay2new * m_coeffs[6]) + (delay2old * m_coeffs[7]) - rightWet; m_state.comb[combPos][3] = (delay3new * m_coeffs[8]) + (delay3old * m_coeffs[9]) + leftWet; delay0old = delay0new; delay1old = delay1new; delay2old = delay2new; delay3old = delay3new; // Advance buffer index combPos = (combPos - 1) & 0xFFF; allpassPos = (allpassPos - 1) & 0x3FF; delay4 = (delay4 - 1) & 0x3FF; delay5 = (delay5 - 1) & 0x3FF; } m_state.combPos = combPos; m_state.allpassPos = allpassPos; ProcessMixOps(pOutL, pOutR, m_mixBuffer.GetOutputBuffer(0), m_mixBuffer.GetOutputBuffer(1), numFrames); } PlugParamValue WavesReverb::GetParameter(PlugParamIndex index) { if(index < kDistNumParameters) { return m_param[index]; } return 0; } void WavesReverb::SetParameter(PlugParamIndex index, PlugParamValue value) { if(index < kDistNumParameters) { value = mpt::safe_clamp(value, 0.0f, 1.0f); m_param[index] = value; RecalculateWavesReverbParams(); } } void WavesReverb::Resume() { m_isResumed = true; // Recalculate delays uint32 delay0 = mpt::saturate_round(m_SndFile.GetSampleRate() * 0.045f); uint32 delay1 = mpt::saturate_round(delay0 * 1.18920707f); // 2^0.25 uint32 delay2 = mpt::saturate_round(delay1 * 1.18920707f); uint32 delay3 = mpt::saturate_round(delay2 * 1.18920707f); uint32 delay4 = mpt::saturate_round((delay0 + delay2) * 0.11546667f); uint32 delay5 = mpt::saturate_round((delay1 + delay3) * 0.11546667f); // Comb delays m_delay[0] = delay0 - delay4; m_delay[1] = delay2 - delay4; m_delay[2] = delay1 - delay5; m_delay[3] = delay3 - delay5; // Allpass delays m_delay[4] = delay4; m_delay[5] = delay5; RecalculateWavesReverbParams(); PositionChanged(); } void WavesReverb::PositionChanged() { MemsetZero(m_state); } #ifdef MODPLUG_TRACKER CString WavesReverb::GetParamName(PlugParamIndex param) { switch(param) { case kRvbInGain: return _T("InGain"); case kRvbReverbMix: return _T("ReverbMix"); case kRvbReverbTime: return _T("ReverbTime"); case kRvbHighFreqRTRatio: return _T("HighFreqRTRatio"); } return CString(); } CString WavesReverb::GetParamLabel(PlugParamIndex param) { switch(param) { case kRvbInGain: case kRvbReverbMix: return _T("dB"); case kRvbReverbTime: return _T("ms"); } return CString(); } CString WavesReverb::GetParamDisplay(PlugParamIndex param) { float value = m_param[param]; switch(param) { case kRvbInGain: case kRvbReverbMix: value = GainInDecibel(value); break; case kRvbReverbTime: value = ReverbTime(); break; case kRvbHighFreqRTRatio: value = HighFreqRTRatio(); break; } CString s; s.Format(_T("%.2f"), value); return s; } #endif // MODPLUG_TRACKER void WavesReverb::RecalculateWavesReverbParams() { // Recalculate filters const double ReverbTimeSmp = -3000.0 / (m_SndFile.GetSampleRate() * ReverbTime()); const double ReverbTimeSmpHF = ReverbTimeSmp * (1.0 / HighFreqRTRatio() - 1.0); m_coeffs[0] = static_cast(std::pow(10.0, m_delay[4] * ReverbTimeSmp)); m_coeffs[1] = static_cast(std::pow(10.0, m_delay[5] * ReverbTimeSmp)); double sum = 0.0; for(uint32 pair = 0; pair < 4; pair++) { double gain1 = std::pow(10.0, m_delay[pair] * ReverbTimeSmp); double gain2 = (1.0 - std::pow(10.0, (m_delay[pair] + m_delay[4 + pair / 2]) * ReverbTimeSmpHF)) * 0.5; double gain3 = gain1 * m_coeffs[pair / 2]; double gain4 = gain3 * (((gain3 + 1.0) * gain3 + 1.0) * gain3 + 1.0) + 1.0; m_coeffs[2 + pair * 2] = static_cast(gain1 * (1.0 - gain2)); m_coeffs[3 + pair * 2] = static_cast(gain1 * gain2); sum += gain4 * gain4; } double inGain = std::pow(10.0, GainInDecibel(m_param[kRvbInGain]) * 0.05); double reverbMix = std::pow(10.0, GainInDecibel(m_param[kRvbReverbMix]) * 0.1); m_dryFactor = static_cast(std::sqrt(1.0 - reverbMix) * inGain); m_wetFactor = static_cast(std::sqrt(reverbMix) * (4.0 / std::sqrt(sum) * inGain)); } } // namespace DMO #else MPT_MSVC_WORKAROUND_LNK4221(WavesReverb) #endif // !NO_PLUGINS OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/plugins/dmo/WavesReverb.h0000644000175000017500000000567713647400540022761 00000000000000/* * WavesReverb.h * ------------- * Purpose: Implementation of the DMO WavesReverb DSP (for non-Windows platforms) * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #ifndef NO_PLUGINS #include "../PlugInterface.h" OPENMPT_NAMESPACE_BEGIN namespace DMO { class WavesReverb final : public IMixPlugin { protected: enum Parameters { kRvbInGain = 0, kRvbReverbMix, kRvbReverbTime, kRvbHighFreqRTRatio, kDistNumParameters }; float m_param[kDistNumParameters]; // Parameters and coefficients float m_dryFactor; float m_wetFactor; float m_coeffs[10]; uint32 m_delay[6]; // State struct ReverbState { uint32 combPos, allpassPos; float comb[4096][4]; float allpass1[1024][2]; float allpass2[1024][2]; } m_state; public: static IMixPlugin* Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct); WavesReverb(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct); void Release() override { delete this; } int32 GetUID() const override { return 0x87FC0268; } int32 GetVersion() const override { return 0; } void Idle() override { } uint32 GetLatency() const override { return 0; } void Process(float *pOutL, float *pOutR, uint32 numFrames) override; float RenderSilence(uint32) override { return 0.0f; } int32 GetNumPrograms() const override { return 0; } int32 GetCurrentProgram() override { return 0; } void SetCurrentProgram(int32) override { } PlugParamIndex GetNumParameters() const override { return kDistNumParameters; } PlugParamValue GetParameter(PlugParamIndex index) override; void SetParameter(PlugParamIndex index, PlugParamValue value) override; void Resume() override; void Suspend() override { m_isResumed = false; } void PositionChanged() override; bool IsInstrument() const override { return false; } bool CanRecieveMidiEvents() override { return false; } bool ShouldProcessSilence() override { return true; } #ifdef MODPLUG_TRACKER CString GetDefaultEffectName() override { return _T("WavesReverb"); } CString GetParamName(PlugParamIndex param) override; CString GetParamLabel(PlugParamIndex) override; CString GetParamDisplay(PlugParamIndex param) override; CString GetCurrentProgramName() override { return CString(); } void SetCurrentProgramName(const CString &) override { } CString GetProgramName(int32) override { return CString(); } bool HasEditor() const override { return false; } #endif int GetNumInputChannels() const override { return 2; } int GetNumOutputChannels() const override { return 2; } protected: static float GainInDecibel(float param) { return -96.0f + param * 96.0f; } float ReverbTime() const { return 0.001f + m_param[kRvbReverbTime] * 2999.999f; } float HighFreqRTRatio() const { return 0.001f + m_param[kRvbHighFreqRTRatio] * 0.998f; } void RecalculateWavesReverbParams(); }; } // namespace DMO OPENMPT_NAMESPACE_END #endif // !NO_PLUGINS libopenmpt-0.6.1+release.autotools/soundlib/plugins/DigiBoosterEcho.cpp0000644000175000017500000001271414155206341023277 00000000000000/* * DigiBoosterEcho.cpp * ------------------- * Purpose: Implementation of the DigiBooster Pro Echo DSP * Notes : (currently none) * Authors: OpenMPT Devs, based on original code by Grzegorz Kraszewski (BSD 2-clause) * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #ifndef NO_PLUGINS #include "../Sndfile.h" #include "DigiBoosterEcho.h" OPENMPT_NAMESPACE_BEGIN IMixPlugin* DigiBoosterEcho::Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct) { return new (std::nothrow) DigiBoosterEcho(factory, sndFile, mixStruct); } DigiBoosterEcho::DigiBoosterEcho(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct) : IMixPlugin(factory, sndFile, mixStruct) , m_sampleRate(sndFile.GetSampleRate()) , m_chunk(PluginChunk::Default()) { m_mixBuffer.Initialize(2, 2); InsertIntoFactoryList(); } void DigiBoosterEcho::Process(float *pOutL, float *pOutR, uint32 numFrames) { if(!m_bufferSize) return; const float *srcL = m_mixBuffer.GetInputBuffer(0), *srcR = m_mixBuffer.GetInputBuffer(1); float *outL = m_mixBuffer.GetOutputBuffer(0), *outR = m_mixBuffer.GetOutputBuffer(1); for(uint32 i = numFrames; i != 0; i--) { int readPos = m_writePos - m_delayTime; if(readPos < 0) readPos += m_bufferSize; float l = *srcL++, r = *srcR++; float lDelay = m_delayLine[readPos * 2], rDelay = m_delayLine[readPos * 2 + 1]; // Calculate the delay float al = l * m_NCrossNBack; al += r * m_PCrossNBack; al += lDelay * m_NCrossPBack; al += rDelay * m_PCrossPBack; float ar = r * m_NCrossNBack; ar += l * m_PCrossNBack; ar += rDelay * m_NCrossPBack; ar += lDelay * m_PCrossPBack; // Prevent denormals if(std::abs(al) < 1e-24f) al = 0.0f; if(std::abs(ar) < 1e-24f) ar = 0.0f; m_delayLine[m_writePos * 2] = al; m_delayLine[m_writePos * 2 + 1] = ar; m_writePos++; if(m_writePos == m_bufferSize) m_writePos = 0; // Output samples now *outL++ = (l * m_NMix + lDelay * m_PMix); *outR++ = (r * m_NMix + rDelay * m_PMix); } ProcessMixOps(pOutL, pOutR, m_mixBuffer.GetOutputBuffer(0), m_mixBuffer.GetOutputBuffer(1), numFrames); } void DigiBoosterEcho::SaveAllParameters() { m_pMixStruct->defaultProgram = -1; try { m_pMixStruct->pluginData.resize(sizeof(m_chunk)); memcpy(m_pMixStruct->pluginData.data(), &m_chunk, sizeof(m_chunk)); } catch(mpt::out_of_memory e) { mpt::delete_out_of_memory(e); m_pMixStruct->pluginData.clear(); } } void DigiBoosterEcho::RestoreAllParameters(int32 program) { if(m_pMixStruct->pluginData.size() == sizeof(m_chunk) && !memcmp(m_pMixStruct->pluginData.data(), "Echo", 4)) { memcpy(&m_chunk, m_pMixStruct->pluginData.data(), sizeof(m_chunk)); } else { IMixPlugin::RestoreAllParameters(program); } RecalculateEchoParams(); } PlugParamValue DigiBoosterEcho::GetParameter(PlugParamIndex index) { if(index < kEchoNumParameters) { return m_chunk.param[index] / 255.0f; } return 0; } void DigiBoosterEcho::SetParameter(PlugParamIndex index, PlugParamValue value) { if(index < kEchoNumParameters) { m_chunk.param[index] = mpt::saturate_round(mpt::safe_clamp(value, 0.0f, 1.0f) * 255.0f); RecalculateEchoParams(); } } void DigiBoosterEcho::Resume() { m_isResumed = true; m_sampleRate = m_SndFile.GetSampleRate(); RecalculateEchoParams(); PositionChanged(); } void DigiBoosterEcho::PositionChanged() { m_bufferSize = (m_sampleRate >> 1) + (m_sampleRate >> 6); try { m_delayLine.assign(m_bufferSize * 2, 0); } catch(mpt::out_of_memory e) { mpt::delete_out_of_memory(e); m_bufferSize = 0; } m_writePos = 0; } #ifdef MODPLUG_TRACKER CString DigiBoosterEcho::GetParamName(PlugParamIndex param) { switch(param) { case kEchoDelay: return _T("Delay"); case kEchoFeedback: return _T("Feedback"); case kEchoMix: return _T("Wet / Dry Ratio"); case kEchoCross: return _T("Cross Echo"); } return CString(); } CString DigiBoosterEcho::GetParamLabel(PlugParamIndex param) { if(param == kEchoDelay) return _T("ms"); return CString(); } CString DigiBoosterEcho::GetParamDisplay(PlugParamIndex param) { CString s; if(param == kEchoMix) { int wet = (m_chunk.param[kEchoMix] * 100) / 255; s.Format(_T("%d%% / %d%%"), wet, 100 - wet); } else if(param < kEchoNumParameters) { int val = m_chunk.param[param]; if(param == kEchoDelay) val *= 2; s.Format(_T("%d"), val); } return s; } #endif // MODPLUG_TRACKER IMixPlugin::ChunkData DigiBoosterEcho::GetChunk(bool) { auto data = reinterpret_cast(&m_chunk); return ChunkData(data, sizeof(m_chunk)); } void DigiBoosterEcho::SetChunk(const ChunkData &chunk, bool) { auto data = chunk.data(); if(chunk.size() == sizeof(chunk) && !memcmp(data, "Echo", 4)) { memcpy(&m_chunk, data, chunk.size()); RecalculateEchoParams(); } } void DigiBoosterEcho::RecalculateEchoParams() { m_delayTime = (m_chunk.param[kEchoDelay] * m_sampleRate + 250) / 500; m_PMix = (m_chunk.param[kEchoMix]) * (1.0f / 256.0f); m_NMix = (256 - m_chunk.param[kEchoMix]) * (1.0f / 256.0f); m_PCrossPBack = (m_chunk.param[kEchoCross] * m_chunk.param[kEchoFeedback]) * (1.0f / 65536.0f); m_PCrossNBack = (m_chunk.param[kEchoCross] * (256 - m_chunk.param[kEchoFeedback])) * (1.0f / 65536.0f); m_NCrossPBack = ((m_chunk.param[kEchoCross] - 256) * m_chunk.param[kEchoFeedback]) * (1.0f / 65536.0f); m_NCrossNBack = ((m_chunk.param[kEchoCross] - 256) * (m_chunk.param[kEchoFeedback] - 256)) * (1.0f / 65536.0f); } OPENMPT_NAMESPACE_END #endif // NO_PLUGINS libopenmpt-0.6.1+release.autotools/soundlib/plugins/DigiBoosterEcho.h0000644000175000017500000000714714064175115022753 00000000000000/* * DigiBoosterEcho.h * ----------------- * Purpose: Implementation of the DigiBooster Pro Echo DSP * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #ifndef NO_PLUGINS #include "PlugInterface.h" OPENMPT_NAMESPACE_BEGIN class DigiBoosterEcho final : public IMixPlugin { public: enum Parameters { kEchoDelay = 0, kEchoFeedback, kEchoMix, kEchoCross, kEchoNumParameters }; // Our settings chunk for file I/O, as it will be written to files struct PluginChunk { char id[4]; uint8 param[kEchoNumParameters]; static PluginChunk Create(uint8 delay, uint8 feedback, uint8 mix, uint8 cross) { static_assert(sizeof(PluginChunk) == 8); PluginChunk result; memcpy(result.id, "Echo", 4); result.param[kEchoDelay] = delay; result.param[kEchoFeedback] = feedback; result.param[kEchoMix] = mix; result.param[kEchoCross] = cross; return result; } static PluginChunk Default() { return Create(80, 150, 80, 255); } }; protected: std::vector m_delayLine; // Echo delay line uint32 m_bufferSize = 0; // Delay line length in frames uint32 m_writePos = 0; // Current write position in the delay line uint32 m_delayTime = 0; // In frames uint32 m_sampleRate = 0; // Echo calculation coefficients float m_PMix, m_NMix; float m_PCrossPBack, m_PCrossNBack; float m_NCrossPBack, m_NCrossNBack; // Settings chunk for file I/O PluginChunk m_chunk; public: static IMixPlugin* Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct); DigiBoosterEcho(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct); void Release() override { delete this; } void SaveAllParameters() override; void RestoreAllParameters(int32 program) override; int32 GetUID() const override { int32le id; memcpy(&id, "Echo", 4); return id; } int32 GetVersion() const override { return 0; } void Idle() override { } uint32 GetLatency() const override { return 0; } void Process(float *pOutL, float *pOutR, uint32 numFrames) override; float RenderSilence(uint32) override { return 0.0f; } int32 GetNumPrograms() const override { return 0; } int32 GetCurrentProgram() override { return 0; } void SetCurrentProgram(int32) override { } PlugParamIndex GetNumParameters() const override { return kEchoNumParameters; } PlugParamValue GetParameter(PlugParamIndex index) override; void SetParameter(PlugParamIndex index, PlugParamValue value) override; void Resume() override; void Suspend() override { m_isResumed = false; } void PositionChanged() override; bool IsInstrument() const override { return false; } bool CanRecieveMidiEvents() override { return false; } bool ShouldProcessSilence() override { return true; } #ifdef MODPLUG_TRACKER CString GetDefaultEffectName() override { return _T("Echo"); } CString GetParamName(PlugParamIndex param) override; CString GetParamLabel(PlugParamIndex) override; CString GetParamDisplay(PlugParamIndex param) override; CString GetCurrentProgramName() override { return CString(); } void SetCurrentProgramName(const CString &) override { } CString GetProgramName(int32) override { return CString(); } bool HasEditor() const override { return false; } #endif int GetNumInputChannels() const override { return 2; } int GetNumOutputChannels() const override { return 2; } bool ProgramsAreChunks() const override { return true; } ChunkData GetChunk(bool) override; void SetChunk(const ChunkData &chunk, bool) override; protected: void RecalculateEchoParams(); }; OPENMPT_NAMESPACE_END #endif // NO_PLUGINS libopenmpt-0.6.1+release.autotools/soundlib/plugins/LFOPlugin.cpp0000644000175000017500000003005014155206341022056 00000000000000/* * LFOPlugin.cpp * ------------- * Purpose: Plugin for automating other plugins' parameters * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #ifndef NO_PLUGINS #include "LFOPlugin.h" #include "../Sndfile.h" #include "../../common/FileReader.h" #ifdef MODPLUG_TRACKER #include "../../mptrack/plugins/LFOPluginEditor.h" #endif // MODPLUG_TRACKER #include "mpt/base/numbers.hpp" OPENMPT_NAMESPACE_BEGIN IMixPlugin* LFOPlugin::Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct) { return new (std::nothrow) LFOPlugin(factory, sndFile, mixStruct); } LFOPlugin::LFOPlugin(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct) : IMixPlugin(factory, sndFile, mixStruct) , m_PRNG(mpt::make_prng(mpt::global_prng())) { RecalculateFrequency(); RecalculateIncrement(); m_mixBuffer.Initialize(2, 2); InsertIntoFactoryList(); } // Processing (we do not process audio, just send out parameters) void LFOPlugin::Process(float *pOutL, float *pOutR, uint32 numFrames) { if(!m_bypassed) { ResetSilence(); if(m_tempoSync) { double tempo = m_SndFile.GetCurrentBPM(); if(tempo != m_tempo) { m_tempo = tempo; RecalculateIncrement(); } } if(m_oneshot) { LimitMax(m_phase, 1.0); } else { int intPhase = static_cast(m_phase); if(intPhase > 0 && (m_waveForm == kSHNoise || m_waveForm == kSmoothNoise)) { // Phase wrap-around happened NextRandom(); } m_phase -= intPhase; } double value = 0; switch(m_waveForm) { case kSine: value = std::sin(m_phase * (2.0 * mpt::numbers::pi)); break; case kTriangle: value = 1.0 - 4.0 * std::abs(m_phase - 0.5); break; case kSaw: value = 2.0 * m_phase - 1.0; break; case kSquare: value = m_phase < 0.5 ? -1.0 : 1.0; break; case kSHNoise: value = m_random; break; case kSmoothNoise: value = m_phase * m_phase * m_phase * (m_phase * (m_phase * 6 - 15) + 10); // Smootherstep value = m_nextRandom * value + m_random * (1.0 - value); break; default: break; } if(m_polarity) value = -value; // Transform value from -1...+1 to 0...1 range and apply offset/amplitude value = value * m_amplitude + m_offset; Limit(value, 0.0, 1.0); IMixPlugin *plugin = GetOutputPlugin(); if(plugin != nullptr) { if(m_outputToCC) { plugin->MidiSend(MIDIEvents::CC(static_cast(m_outputParam & 0x7F), static_cast((m_outputParam >> 8) & 0x0F), mpt::saturate_round(value * 127.0f))); } else { plugin->SetParameter(m_outputParam, static_cast(value)); } } m_phase += m_increment * numFrames; } ProcessMixOps(pOutL, pOutR, m_mixBuffer.GetInputBuffer(0), m_mixBuffer.GetInputBuffer(1), numFrames); } PlugParamValue LFOPlugin::GetParameter(PlugParamIndex index) { switch(index) { case kAmplitude: return m_amplitude; case kOffset: return m_offset; case kFrequency: return m_frequency; case kTempoSync: return m_tempoSync ? 1.0f : 0.0f; case kWaveform: return WaveformToParam(m_waveForm); case kPolarity: return m_polarity ? 1.0f : 0.0f; case kBypassed: return m_bypassed ? 1.0f : 0.0f; case kLoopMode: return m_oneshot ? 1.0f : 0.0f; default: return 0; } } void LFOPlugin::SetParameter(PlugParamIndex index, PlugParamValue value) { ResetSilence(); value = mpt::safe_clamp(value, 0.0f, 1.0f); switch(index) { case kAmplitude: m_amplitude = value; break; case kOffset: m_offset = value; break; case kFrequency: m_frequency = value; RecalculateFrequency(); break; case kTempoSync: m_tempoSync = (value >= 0.5f); RecalculateFrequency(); break; case kWaveform: m_waveForm = ParamToWaveform(value); break; case kPolarity: m_polarity = (value >= 0.5f); break; case kBypassed: m_bypassed = (value >= 0.5f); break; case kLoopMode: m_oneshot = (value >= 0.5f); break; case kCurrentPhase: if(value == 0) { // Enforce next random value for random LFOs NextRandom(); } m_phase = value; return; default: return; } #ifdef MODPLUG_TRACKER if(GetEditor() != nullptr) { GetEditor()->PostMessage(WM_PARAM_UDPATE, GetSlot(), index); } #endif } void LFOPlugin::Resume() { m_isResumed = true; RecalculateIncrement(); NextRandom(); PositionChanged(); } void LFOPlugin::PositionChanged() { // TODO Changing tempo (with tempo sync enabled), parameter automation over time and setting the LFO phase manually is not considered here. m_phase = m_increment * m_SndFile.GetTotalSampleCount(); m_phase -= static_cast(m_phase); } bool LFOPlugin::MidiSend(uint32 midiCode) { if(IMixPlugin *plugin = GetOutputPlugin()) return plugin->MidiSend(midiCode); else return true; } bool LFOPlugin::MidiSysexSend(mpt::const_byte_span sysex) { if(IMixPlugin *plugin = GetOutputPlugin()) return plugin->MidiSysexSend(sysex); else return true; } void LFOPlugin::MidiCC(MIDIEvents::MidiCC nController, uint8 nParam, CHANNELINDEX trackChannel) { if(IMixPlugin *plugin = GetOutputPlugin()) { plugin->MidiCC(nController, nParam, trackChannel); } } void LFOPlugin::MidiPitchBend(int32 increment, int8 pwd, CHANNELINDEX trackChannel) { if(IMixPlugin *plugin = GetOutputPlugin()) { plugin->MidiPitchBend(increment, pwd, trackChannel); } } void LFOPlugin::MidiVibrato(int32 depth, int8 pwd, CHANNELINDEX trackChannel) { if(IMixPlugin *plugin = GetOutputPlugin()) { plugin->MidiVibrato(depth, pwd, trackChannel); } } void LFOPlugin::MidiCommand(const ModInstrument &instr, uint16 note, uint16 vol, CHANNELINDEX trackChannel) { if(ModCommand::IsNote(static_cast(note)) && vol > 0) { SetParameter(kCurrentPhase, 0); } if(IMixPlugin *plugin = GetOutputPlugin()) { plugin->MidiCommand(instr, note, vol, trackChannel); } } void LFOPlugin::HardAllNotesOff() { if(IMixPlugin *plugin = GetOutputPlugin()) { plugin->HardAllNotesOff(); } } bool LFOPlugin::IsNotePlaying(uint8 note, CHANNELINDEX trackerChn) { if(IMixPlugin *plugin = GetOutputPlugin()) return plugin->IsNotePlaying(note, trackerChn); else return false; } void LFOPlugin::SaveAllParameters() { auto chunk = GetChunk(false); if(chunk.empty()) return; m_pMixStruct->defaultProgram = -1; m_pMixStruct->pluginData.assign(chunk.begin(), chunk.end()); } void LFOPlugin::RestoreAllParameters(int32 /*program*/) { SetChunk(mpt::as_span(m_pMixStruct->pluginData), false); } struct PluginData { char magic[4]; uint32le version; uint32le amplitude; // float uint32le offset; // float uint32le frequency; // float uint32le waveForm; uint32le outputParam; uint8le tempoSync; uint8le polarity; uint8le bypassed; uint8le outputToCC; uint8le loopMode; }; MPT_BINARY_STRUCT(PluginData, 33) IMixPlugin::ChunkData LFOPlugin::GetChunk(bool) { PluginData chunk; memcpy(chunk.magic, "LFO ", 4); chunk.version = 0; chunk.amplitude = IEEE754binary32LE(m_amplitude).GetInt32(); chunk.offset = IEEE754binary32LE(m_offset).GetInt32(); chunk.frequency = IEEE754binary32LE(m_frequency).GetInt32(); chunk.waveForm = m_waveForm; chunk.outputParam = m_outputParam; chunk.tempoSync = m_tempoSync ? 1 : 0; chunk.polarity = m_polarity ? 1 : 0; chunk.bypassed = m_bypassed ? 1 : 0; chunk.outputToCC = m_outputToCC ? 1 : 0; chunk.loopMode = m_oneshot ? 1 : 0; m_chunkData.resize(sizeof(chunk)); memcpy(m_chunkData.data(), &chunk, sizeof(chunk)); return mpt::as_span(m_chunkData); } void LFOPlugin::SetChunk(const ChunkData &chunk, bool) { FileReader file(chunk); PluginData data; if(file.ReadStructPartial(data, file.BytesLeft()) && !memcmp(data.magic, "LFO ", 4) && data.version == 0) { const float amplitude = IEEE754binary32LE().SetInt32(data.amplitude); m_amplitude = mpt::safe_clamp(amplitude, 0.0f, 1.0f); const float offset = IEEE754binary32LE().SetInt32(data.offset); m_offset = mpt::safe_clamp(offset, 0.0f, 1.0f); const float frequency = IEEE754binary32LE().SetInt32(data.frequency); m_frequency = mpt::safe_clamp(frequency, 0.0f, 1.0f); if(data.waveForm < kNumWaveforms) m_waveForm = static_cast(data.waveForm.get()); m_outputParam = data.outputParam; m_tempoSync = data.tempoSync != 0; m_polarity = data.polarity != 0; m_bypassed = data.bypassed != 0; m_outputToCC = data.outputToCC != 0; m_oneshot = data.loopMode != 0; RecalculateFrequency(); } } #ifdef MODPLUG_TRACKER std::pair LFOPlugin::GetParamUIRange(PlugParamIndex param) { if(param == kWaveform) return {0.0f, WaveformToParam(static_cast(kNumWaveforms - 1))}; else return {0.0f, 1.0f}; } CString LFOPlugin::GetParamName(PlugParamIndex param) { switch(param) { case kAmplitude: return _T("Amplitude"); case kOffset: return _T("Offset"); case kFrequency: return _T("Frequency"); case kTempoSync: return _T("Tempo Sync"); case kWaveform: return _T("Waveform"); case kPolarity: return _T("Polarity"); case kBypassed: return _T("Bypassed"); case kLoopMode: return _T("Loop Mode"); case kCurrentPhase: return _T("Set LFO Phase"); } return CString(); } CString LFOPlugin::GetParamLabel(PlugParamIndex param) { if(param == kFrequency) { if(m_tempoSync && m_computedFrequency > 0.0 && m_computedFrequency < 1.0) return _T("Beats Per Cycle"); else if(m_tempoSync) return _T("Cycles Per Beat"); else return _T("Hz"); } return CString(); } CString LFOPlugin::GetParamDisplay(PlugParamIndex param) { CString s; if(param == kPolarity) { return m_polarity ? _T("Inverted") : _T("Normal"); } else if(param == kTempoSync) { return m_tempoSync ? _T("Yes") : _T("No"); } else if(param == kBypassed) { return m_bypassed ? _T("Yes") : _T("No"); } else if(param == kWaveform) { static constexpr const TCHAR * const waveforms[] = { _T("Sine"), _T("Triangle"), _T("Saw"), _T("Square"), _T("Noise"), _T("Smoothed Noise") }; if(m_waveForm < static_cast(std::size(waveforms))) return waveforms[m_waveForm]; } else if(param == kLoopMode) { return m_oneshot ? _T("One-Shot") : _T("Looped"); } else if(param == kCurrentPhase) { return _T("Write-Only"); } else if(param < kLFONumParameters) { auto val = GetParameter(param); if(param == kOffset) val = 2.0f * val - 1.0f; if(param == kFrequency) { val = static_cast(m_computedFrequency); if(m_tempoSync && val > 0.0f && val < 1.0f) val = static_cast(1.0 / m_computedFrequency); } s.Format(_T("%.3f"), val); } return s; } CAbstractVstEditor *LFOPlugin::OpenEditor() { try { return new LFOPluginEditor(*this); } catch(mpt::out_of_memory e) { mpt::delete_out_of_memory(e); return nullptr; } } #endif // MODPLUG_TRACKER void LFOPlugin::NextRandom() { m_random = m_nextRandom; m_nextRandom = mpt::random(m_PRNG) / static_cast(int32_min); } void LFOPlugin::RecalculateFrequency() { m_computedFrequency = 0.25 * std::pow(2.0, m_frequency * 8.0) - 0.25; if(m_tempoSync) { if(m_computedFrequency > 0.00045) { double freqLog = std::log(m_computedFrequency) / mpt::numbers::ln2; double freqFrac = freqLog - std::floor(freqLog); freqLog -= freqFrac; // Lock to powers of two and 1.5 times or 1.333333... times the powers of two if(freqFrac < 0.20751874963942190927313052802609) freqFrac = 0.0; else if(freqFrac < 0.5) freqFrac = 0.41503749927884381854626105605218; else if(freqFrac < 0.79248125036057809072686947197391) freqFrac = 0.58496250072115618145373894394782; else freqFrac = 1.0; m_computedFrequency = std::pow(2.0, freqLog + freqFrac) * 0.5; } else { m_computedFrequency = 0; } } RecalculateIncrement(); } void LFOPlugin::RecalculateIncrement() { m_increment = m_computedFrequency / m_SndFile.GetSampleRate(); if(m_tempoSync) { m_increment *= m_tempo / 60.0; } } IMixPlugin *LFOPlugin::GetOutputPlugin() const { PLUGINDEX outPlug = m_pMixStruct->GetOutputPlugin(); if(outPlug > m_nSlot && outPlug < MAX_MIXPLUGINS) return m_SndFile.m_MixPlugins[outPlug].pMixPlugin; else return nullptr; } OPENMPT_NAMESPACE_END #else MPT_MSVC_WORKAROUND_LNK4221(LFOPlugin) #endif // !NO_PLUGINS libopenmpt-0.6.1+release.autotools/soundlib/plugins/LFOPlugin.h0000644000175000017500000001121314114455374021532 00000000000000/* * LFOPlugin.h * ----------- * Purpose: Plugin for automating other plugins' parameters * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #ifndef NO_PLUGINS #include "PlugInterface.h" #include "../../common/mptRandom.h" OPENMPT_NAMESPACE_BEGIN class LFOPlugin final : public IMixPlugin { friend class LFOPluginEditor; protected: enum Parameters { kAmplitude = 0, kOffset, kFrequency, kTempoSync, kWaveform, kPolarity, kBypassed, kLoopMode, kCurrentPhase, kLFONumParameters }; enum LFOWaveform { kSine = 0, kTriangle, kSaw, kSquare, kSHNoise, kSmoothNoise, kNumWaveforms }; std::vector m_chunkData; // LFO parameters float m_amplitude = 0.5f, m_offset = 0.5f, m_frequency = 0.290241f; LFOWaveform m_waveForm = kSine; PlugParamIndex m_outputParam = int32_max; bool m_tempoSync = false, m_polarity = false, m_bypassed = false, m_outputToCC = false, m_oneshot = false; // LFO state double m_computedFrequency = 0.0; double m_phase = 0.0, m_increment = 0.0; double m_random = 0.0, m_nextRandom = 0.0; double m_tempo = 0.0; mpt::fast_prng m_PRNG; #ifdef MODPLUG_TRACKER static constexpr int WM_PARAM_UDPATE = WM_USER + 500; #endif public: static IMixPlugin* Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct); LFOPlugin(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct); void Release() override { delete this; } int32 GetUID() const override { int32 id; memcpy(&id, "LFO ", 4); return id; } int32 GetVersion() const override { return 0; } void Idle() override { } uint32 GetLatency() const override { return 0; } void Process(float *pOutL, float *pOutR, uint32 numFrames) override; float RenderSilence(uint32) override { return 0.0f; } // MIDI event handling (mostly passing it through to the follow-up plugin) bool MidiSend(uint32 midiCode) override; bool MidiSysexSend(mpt::const_byte_span sysex) override; void MidiCC(MIDIEvents::MidiCC nController, uint8 nParam, CHANNELINDEX trackChannel) override; void MidiPitchBend(int32 increment, int8 pwd, CHANNELINDEX trackChannel) override; void MidiVibrato(int32 depth, int8 pwd, CHANNELINDEX trackChannel) override; void MidiCommand(const ModInstrument &instr, uint16 note, uint16 vol, CHANNELINDEX trackChannel) override; void HardAllNotesOff() override; bool IsNotePlaying(uint8 note, CHANNELINDEX trackerChn) override; int32 GetNumPrograms() const override { return 0; } int32 GetCurrentProgram() override { return 0; } void SetCurrentProgram(int32) override { } PlugParamIndex GetNumParameters() const override { return kLFONumParameters; } PlugParamValue GetParameter(PlugParamIndex index) override; void SetParameter(PlugParamIndex index, PlugParamValue value) override; void Resume() override; void Suspend() override { m_isResumed = false; } void PositionChanged() override; bool IsInstrument() const override { return false; } bool CanRecieveMidiEvents() override { return false; } bool ShouldProcessSilence() override { return true; } #ifdef MODPLUG_TRACKER CString GetDefaultEffectName() override { return _T("LFO"); } std::pair GetParamUIRange(PlugParamIndex param) override; CString GetParamName(PlugParamIndex param) override; CString GetParamLabel(PlugParamIndex) override; CString GetParamDisplay(PlugParamIndex param) override; CString GetCurrentProgramName() override { return CString(); } void SetCurrentProgramName(const CString &) override { } CString GetProgramName(int32) override { return CString(); } bool HasEditor() const override { return true; } protected: CAbstractVstEditor *OpenEditor() override; #endif public: int GetNumInputChannels() const override { return 2; } int GetNumOutputChannels() const override { return 2; } bool ProgramsAreChunks() const override { return true; } // Save parameters for storing them in a module file void SaveAllParameters() override; // Restore parameters from module file void RestoreAllParameters(int32 program) override; ChunkData GetChunk(bool) override; void SetChunk(const ChunkData &chunk, bool) override; protected: void NextRandom(); void RecalculateFrequency(); void RecalculateIncrement(); IMixPlugin *GetOutputPlugin() const; public: static LFOWaveform ParamToWaveform(float param) { return static_cast(std::clamp(mpt::saturate_round(param * 32.0f), 0, kNumWaveforms - 1)); } static float WaveformToParam(LFOWaveform waveform) { return static_cast(waveform) / 32.0f; } }; OPENMPT_NAMESPACE_END #endif // NO_PLUGINS libopenmpt-0.6.1+release.autotools/soundlib/plugins/PluginManager.cpp0000644000175000017500000005556214152402727023032 00000000000000/* * PluginManager.cpp * ----------------- * Purpose: Implementation of the plugin manager, which keeps a list of known plugins and instantiates them. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #ifndef NO_PLUGINS #include "../../common/version.h" #include "PluginManager.h" #include "PlugInterface.h" #include "mpt/uuid/guid.hpp" #include "mpt/uuid/uuid.hpp" // Built-in plugins #include "DigiBoosterEcho.h" #include "LFOPlugin.h" #include "SymMODEcho.h" #include "dmo/DMOPlugin.h" #include "dmo/Chorus.h" #include "dmo/Compressor.h" #include "dmo/Distortion.h" #include "dmo/Echo.h" #include "dmo/Flanger.h" #include "dmo/Gargle.h" #include "dmo/I3DL2Reverb.h" #include "dmo/ParamEq.h" #include "dmo/WavesReverb.h" #ifdef MODPLUG_TRACKER #include "../../mptrack/plugins/MidiInOut.h" #endif // MODPLUG_TRACKER #include "../../common/mptStringBuffer.h" #include "../Sndfile.h" #include "../Loaders.h" #ifdef MPT_WITH_VST #include "../../mptrack/Vstplug.h" #include "../../pluginBridge/BridgeWrapper.h" #endif // MPT_WITH_VST #if defined(MPT_WITH_DMO) #include #include #include #endif // MPT_WITH_DMO #ifdef MODPLUG_TRACKER #include "../../mptrack/Mptrack.h" #include "../../mptrack/TrackerSettings.h" #include "../../mptrack/AbstractVstEditor.h" #include "../../soundlib/AudioCriticalSection.h" #include "../mptrack/ExceptionHandler.h" #include "mpt/crc/crc.hpp" #endif // MODPLUG_TRACKER OPENMPT_NAMESPACE_BEGIN using namespace mpt::uuid_literals; #ifdef MPT_ALL_LOGGING #define VST_LOG #define DMO_LOG #endif #ifdef MODPLUG_TRACKER static constexpr const mpt::uchar *cacheSection = UL_("PluginCache"); #endif // MODPLUG_TRACKER #ifdef MPT_WITH_VST uint8 VSTPluginLib::GetNativePluginArch() { uint8 result = 0; switch(mpt::OS::Windows::GetProcessArchitecture()) { case mpt::OS::Windows::Architecture::x86: result = PluginArch_x86; break; case mpt::OS::Windows::Architecture::amd64: result = PluginArch_amd64; break; case mpt::OS::Windows::Architecture::arm: result = PluginArch_arm; break; case mpt::OS::Windows::Architecture::arm64: result = PluginArch_arm64; break; default: result = 0; break; } return result; } mpt::ustring VSTPluginLib::GetPluginArchName(uint8 arch) { mpt::ustring result; switch(arch) { case PluginArch_x86: result = U_("x86"); break; case PluginArch_amd64: result = U_("amd64"); break; case PluginArch_arm: result = U_("arm"); break; case PluginArch_arm64: result = U_("arm64"); break; default: result = U_(""); break; } return result; } mpt::ustring VSTPluginLib::GetPluginArchNameUser(uint8 arch) { mpt::ustring result; #if defined(MPT_WITH_WINDOWS10) switch(arch) { case PluginArch_x86: result = U_("x86 (32bit)"); break; case PluginArch_amd64: result = U_("amd64 (64bit)"); break; case PluginArch_arm: result = U_("arm (32bit)"); break; case PluginArch_arm64: result = U_("arm64 (64bit)"); break; default: result = U_(""); break; } #else // !MPT_WITH_WINDOWS10 switch(arch) { case PluginArch_x86: result = U_("32-Bit"); break; case PluginArch_amd64: result = U_("64-Bit"); break; case PluginArch_arm: result = U_("32-Bit"); break; case PluginArch_arm64: result = U_("64-Bit"); break; default: result = U_(""); break; } #endif // MPT_WITH_WINDOWS10 return result; } uint8 VSTPluginLib::GetDllArch(bool fromCache) const { // Built-in plugins are always native. if(dllPath.empty()) return GetNativePluginArch(); #ifdef MPT_WITH_VST if(!dllArch || !fromCache) { dllArch = static_cast(BridgeWrapper::GetPluginBinaryType(dllPath)); } #else // !MPT_WITH_VST MPT_UNREFERENCED_PARAMETER(fromCache); #endif // MPT_WITH_VST return dllArch; } mpt::ustring VSTPluginLib::GetDllArchName(bool fromCache) const { return GetPluginArchName(GetDllArch(fromCache)); } mpt::ustring VSTPluginLib::GetDllArchNameUser(bool fromCache) const { return GetPluginArchNameUser(GetDllArch(fromCache)); } bool VSTPluginLib::IsNative(bool fromCache) const { return GetDllArch(fromCache) == GetNativePluginArch(); } bool VSTPluginLib::IsNativeFromCache() const { return dllArch == GetNativePluginArch() || dllArch == 0; } #endif // MPT_WITH_VST // PluginCache format: // FullDllPath = (hex-encoded) // .Flags = Plugin Flags (see VSTPluginLib::DecodeCacheFlags). // .Vendor = Plugin Vendor String. #ifdef MODPLUG_TRACKER void VSTPluginLib::WriteToCache() const { SettingsContainer &cacheFile = theApp.GetPluginCache(); const std::string crcName = dllPath.ToUTF8(); const mpt::crc32 crc(crcName); const mpt::ustring IDs = mpt::ufmt::HEX0<8>(pluginId1) + mpt::ufmt::HEX0<8>(pluginId2) + mpt::ufmt::HEX0<8>(crc.result()); mpt::PathString writePath = dllPath; if(theApp.IsPortableMode()) { writePath = theApp.PathAbsoluteToInstallRelative(writePath); } cacheFile.Write(cacheSection, writePath.ToUnicode(), IDs); cacheFile.Write(cacheSection, IDs + U_(".Vendor"), vendor); cacheFile.Write(cacheSection, IDs + U_(".Flags"), EncodeCacheFlags()); } #endif // MODPLUG_TRACKER bool CreateMixPluginProc(SNDMIXPLUGIN &mixPlugin, CSoundFile &sndFile) { #ifdef MODPLUG_TRACKER CVstPluginManager *that = theApp.GetPluginManager(); if(that) { return that->CreateMixPlugin(mixPlugin, sndFile); } return false; #else if(!sndFile.m_PluginManager) { sndFile.m_PluginManager = std::make_unique(); } return sndFile.m_PluginManager->CreateMixPlugin(mixPlugin, sndFile); #endif // MODPLUG_TRACKER } CVstPluginManager::CVstPluginManager() { #if defined(MPT_WITH_DMO) HRESULT COMinit = CoInitializeEx(NULL, COINIT_MULTITHREADED); if(COMinit == S_OK || COMinit == S_FALSE) { MustUnInitilizeCOM = true; } #endif // Hard-coded "plugins" static constexpr struct { VSTPluginLib::CreateProc createProc; const char *filename, *name; uint32 pluginId1, pluginId2; VSTPluginLib::PluginCategory category; bool isInstrument, isOurs; } BuiltInPlugins[] = { // DirectX Media Objects Emulation { DMO::Chorus::Create, "{EFE6629C-81F7-4281-BD91-C9D604A95AF6}", "Chorus", kDmoMagic, 0xEFE6629C, VSTPluginLib::catDMO, false, false }, { DMO::Compressor::Create, "{EF011F79-4000-406D-87AF-BFFB3FC39D57}", "Compressor", kDmoMagic, 0xEF011F79, VSTPluginLib::catDMO, false, false }, { DMO::Distortion::Create, "{EF114C90-CD1D-484E-96E5-09CFAF912A21}", "Distortion", kDmoMagic, 0xEF114C90, VSTPluginLib::catDMO, false, false }, { DMO::Echo::Create, "{EF3E932C-D40B-4F51-8CCF-3F98F1B29D5D}", "Echo", kDmoMagic, 0xEF3E932C, VSTPluginLib::catDMO, false, false }, { DMO::Flanger::Create, "{EFCA3D92-DFD8-4672-A603-7420894BAD98}", "Flanger", kDmoMagic, 0xEFCA3D92, VSTPluginLib::catDMO, false, false }, { DMO::Gargle::Create, "{DAFD8210-5711-4B91-9FE3-F75B7AE279BF}", "Gargle", kDmoMagic, 0xDAFD8210, VSTPluginLib::catDMO, false, false }, { DMO::I3DL2Reverb::Create, "{EF985E71-D5C7-42D4-BA4D-2D073E2E96F4}", "I3DL2Reverb", kDmoMagic, 0xEF985E71, VSTPluginLib::catDMO, false, false }, { DMO::ParamEq::Create, "{120CED89-3BF4-4173-A132-3CB406CF3231}", "ParamEq", kDmoMagic, 0x120CED89, VSTPluginLib::catDMO, false, false }, { DMO::WavesReverb::Create, "{87FC0268-9A55-4360-95AA-004A1D9DE26C}", "WavesReverb", kDmoMagic, 0x87FC0268, VSTPluginLib::catDMO, false, false }, // First (inaccurate) Flanger implementation (will be chosen based on library name, shares ID1 and ID2 with regular Flanger) { DMO::Flanger::CreateLegacy, "{EFCA3D92-DFD8-4672-A603-7420894BAD98}", "Flanger (Legacy)", kDmoMagic, 0xEFCA3D92, VSTPluginLib::catHidden, false, false }, // DigiBooster Pro Echo DSP { DigiBoosterEcho::Create, "", "DigiBooster Pro Echo", MagicLE("DBM0"), MagicLE("Echo"), VSTPluginLib::catRoomFx, false, true }, // LFO { LFOPlugin::Create, "", "LFO", MagicLE("OMPT"), MagicLE("LFO "), VSTPluginLib::catGenerator, false, true }, // SymMOD Echo { SymMODEcho::Create, "", "SymMOD Echo", MagicLE("SymM"), MagicLE("Echo"), VSTPluginLib::catRoomFx, false, true }, #ifdef MODPLUG_TRACKER { MidiInOut::Create, "", "MIDI Input Output", PLUGMAGIC('V','s','t','P'), PLUGMAGIC('M','M','I','D'), VSTPluginLib::catSynth, true, true }, #endif // MODPLUG_TRACKER }; pluginList.reserve(std::size(BuiltInPlugins)); for(const auto &plugin : BuiltInPlugins) { VSTPluginLib *plug = new (std::nothrow) VSTPluginLib(plugin.createProc, true, mpt::PathString::FromUTF8(plugin.filename), mpt::PathString::FromUTF8(plugin.name)); if(plug != nullptr) { pluginList.push_back(plug); plug->pluginId1 = plugin.pluginId1; plug->pluginId2 = plugin.pluginId2; plug->category = plugin.category; plug->isInstrument = plugin.isInstrument; #ifdef MODPLUG_TRACKER if(plugin.isOurs) plug->vendor = _T("OpenMPT Project"); #endif // MODPLUG_TRACKER } } #ifdef MODPLUG_TRACKER // For security reasons, we do not load untrusted DMO plugins in libopenmpt. EnumerateDirectXDMOs(); #endif } CVstPluginManager::~CVstPluginManager() { for(auto &plug : pluginList) { while(plug->pPluginsList != nullptr) { plug->pPluginsList->Release(); } delete plug; } #if defined(MPT_WITH_DMO) if(MustUnInitilizeCOM) { CoUninitialize(); MustUnInitilizeCOM = false; } #endif } bool CVstPluginManager::IsValidPlugin(const VSTPluginLib *pLib) const { return mpt::contains(pluginList, pLib); } void CVstPluginManager::EnumerateDirectXDMOs() { #if defined(MPT_WITH_DMO) static constexpr mpt::UUID knownDMOs[] = { "745057C7-F353-4F2D-A7EE-58434477730E"_uuid, // AEC (Acoustic echo cancellation, not usable) "EFE6629C-81F7-4281-BD91-C9D604A95AF6"_uuid, // Chorus "EF011F79-4000-406D-87AF-BFFB3FC39D57"_uuid, // Compressor "EF114C90-CD1D-484E-96E5-09CFAF912A21"_uuid, // Distortion "EF3E932C-D40B-4F51-8CCF-3F98F1B29D5D"_uuid, // Echo "EFCA3D92-DFD8-4672-A603-7420894BAD98"_uuid, // Flanger "DAFD8210-5711-4B91-9FE3-F75B7AE279BF"_uuid, // Gargle "EF985E71-D5C7-42D4-BA4D-2D073E2E96F4"_uuid, // I3DL2Reverb "120CED89-3BF4-4173-A132-3CB406CF3231"_uuid, // ParamEq "87FC0268-9A55-4360-95AA-004A1D9DE26C"_uuid, // WavesReverb "F447B69E-1884-4A7E-8055-346F74D6EDB3"_uuid, // Resampler DMO (not usable) }; HKEY hkEnum; TCHAR keyname[128]; LONG cr = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("software\\classes\\DirectShow\\MediaObjects\\Categories\\f3602b3f-0592-48df-a4cd-674721e7ebeb"), 0, KEY_READ, &hkEnum); DWORD index = 0; while (cr == ERROR_SUCCESS) { if ((cr = RegEnumKey(hkEnum, index, keyname, mpt::saturate_cast(std::size(keyname)))) == ERROR_SUCCESS) { CLSID clsid; mpt::winstring formattedKey = mpt::winstring(_T("{")) + mpt::winstring(keyname) + mpt::winstring(_T("}")); if(mpt::VerifyStringToCLSID(formattedKey, clsid)) { if(!mpt::contains(knownDMOs, clsid)) { HKEY hksub; formattedKey = mpt::winstring(_T("software\\classes\\DirectShow\\MediaObjects\\")) + mpt::winstring(keyname); if (RegOpenKey(HKEY_LOCAL_MACHINE, formattedKey.c_str(), &hksub) == ERROR_SUCCESS) { TCHAR name[64]; DWORD datatype = REG_SZ; DWORD datasize = sizeof(name); if(ERROR_SUCCESS == RegQueryValueEx(hksub, nullptr, 0, &datatype, (LPBYTE)name, &datasize)) { VSTPluginLib *plug = new (std::nothrow) VSTPluginLib(DMOPlugin::Create, true, mpt::PathString::FromNative(mpt::GUIDToString(clsid)), mpt::PathString::FromNative(ParseMaybeNullTerminatedStringFromBufferWithSizeInBytes(name, datasize))); if(plug != nullptr) { try { pluginList.push_back(plug); plug->pluginId1 = kDmoMagic; plug->pluginId2 = clsid.Data1; plug->category = VSTPluginLib::catDMO; } catch(mpt::out_of_memory e) { mpt::delete_out_of_memory(e); delete plug; } #ifdef DMO_LOG MPT_LOG_GLOBAL(LogDebug, "DMO", MPT_UFORMAT("Found \"{}\" clsid={}\n")(plug->libraryName, plug->dllPath)); #endif } } RegCloseKey(hksub); } } } } index++; } if (hkEnum) RegCloseKey(hkEnum); #endif // MPT_WITH_DMO } // Extract instrument and category information from plugin. #ifdef MPT_WITH_VST static void GetPluginInformation(bool maskCrashes, Vst::AEffect *effect, VSTPluginLib &library) { unsigned long exception = 0; library.category = static_cast(CVstPlugin::DispatchSEH(maskCrashes, effect, Vst::effGetPlugCategory, 0, 0, nullptr, 0, exception)); library.isInstrument = ((effect->flags & Vst::effFlagsIsSynth) || !effect->numInputs); if(library.isInstrument) { library.category = VSTPluginLib::catSynth; } else if(library.category >= VSTPluginLib::numCategories) { library.category = VSTPluginLib::catUnknown; } #ifdef MODPLUG_TRACKER std::vector s(256, 0); CVstPlugin::DispatchSEH(maskCrashes, effect, Vst::effGetVendorString, 0, 0, s.data(), 0, exception); library.vendor = mpt::ToCString(mpt::Charset::Locale, s.data()); #endif // MODPLUG_TRACKER } #endif // MPT_WITH_VST #ifdef MPT_WITH_VST static bool TryLoadPlugin(bool maskCrashes, VSTPluginLib *plug, HINSTANCE hLib, unsigned long &exception) { Vst::AEffect *pEffect = CVstPlugin::LoadPlugin(maskCrashes, *plug, hLib, CVstPlugin::BridgeMode::DetectRequiredBridgeMode); if(!pEffect || pEffect->magic != Vst::kEffectMagic || !pEffect->dispatcher) { return false; } CVstPlugin::DispatchSEH(maskCrashes, pEffect, Vst::effOpen, 0, 0, 0, 0, exception); plug->pluginId1 = pEffect->magic; plug->pluginId2 = pEffect->uniqueID; GetPluginInformation(maskCrashes, pEffect, *plug); #ifdef VST_LOG intptr_t nver = CVstPlugin::DispatchSEH(maskCrashes, pEffect, Vst::effGetVstVersion, 0,0, nullptr, 0, exception); if (!nver) nver = pEffect->version; MPT_LOG_GLOBAL(LogDebug, "VST", MPT_UFORMAT("{}: v{}.0, {} in, {} out, {} programs, {} params, flags=0x{} realQ={} offQ={}")( plug->libraryName, nver, pEffect->numInputs, pEffect->numOutputs, mpt::ufmt::dec0<2>(pEffect->numPrograms), mpt::ufmt::dec0<2>(pEffect->numParams), mpt::ufmt::HEX0<4>(static_cast(pEffect->flags)), pEffect->realQualities, pEffect->offQualities)); #endif // VST_LOG CVstPlugin::DispatchSEH(maskCrashes, pEffect, Vst::effClose, 0, 0, 0, 0, exception); return true; } #endif // !NO_NVST #ifdef MODPLUG_TRACKER // Add a plugin to the list of known plugins. VSTPluginLib *CVstPluginManager::AddPlugin(const mpt::PathString &dllPath, bool maskCrashes, const mpt::ustring &tags, bool fromCache, bool *fileFound) { const mpt::PathString fileName = dllPath.GetFileName(); // Check if this is already a known plugin. for(const auto &dupePlug : pluginList) { if(!dllPath.CompareNoCase(dllPath, dupePlug->dllPath)) return dupePlug; } if(fileFound != nullptr) { *fileFound = dllPath.IsFile(); } // Look if the plugin info is stored in the PluginCache if(fromCache) { SettingsContainer & cacheFile = theApp.GetPluginCache(); // First try finding the full path mpt::ustring IDs = cacheFile.Read(cacheSection, dllPath.ToUnicode(), U_("")); if(IDs.length() < 16) { // If that didn't work out, find relative path mpt::PathString relPath = theApp.PathAbsoluteToInstallRelative(dllPath); IDs = cacheFile.Read(cacheSection, relPath.ToUnicode(), U_("")); } if(IDs.length() >= 16) { VSTPluginLib *plug = new (std::nothrow) VSTPluginLib(nullptr, false, dllPath, fileName, tags); if(plug == nullptr) { return nullptr; } pluginList.push_back(plug); // Extract plugin IDs for (int i = 0; i < 16; i++) { int32 n = IDs[i] - '0'; if (n > 9) n = IDs[i] + 10 - 'A'; n &= 0x0f; if (i < 8) { plug->pluginId1 = (plug->pluginId1 << 4) | n; } else { plug->pluginId2 = (plug->pluginId2 << 4) | n; } } const mpt::ustring flagKey = IDs + U_(".Flags"); plug->DecodeCacheFlags(cacheFile.Read(cacheSection, flagKey, 0)); plug->vendor = cacheFile.Read(cacheSection, IDs + U_(".Vendor"), CString()); #ifdef VST_LOG MPT_LOG_GLOBAL(LogDebug, "VST", MPT_UFORMAT("Plugin \"{}\" found in PluginCache")(plug->libraryName)); #endif // VST_LOG return plug; } else { #ifdef VST_LOG MPT_LOG_GLOBAL(LogDebug, "VST", MPT_UFORMAT("Plugin mismatch in PluginCache: \"{}\" [{}]")(dllPath, IDs)); #endif // VST_LOG } } // If this key contains a file name on program launch, a plugin previously crashed OpenMPT. theApp.GetSettings().Write(U_("VST Plugins"), U_("FailedPlugin"), dllPath, SettingWriteThrough); bool validPlug = false; VSTPluginLib *plug = new (std::nothrow) VSTPluginLib(nullptr, false, dllPath, fileName, tags); if(plug == nullptr) { return nullptr; } #ifdef MPT_WITH_VST unsigned long exception = 0; // Always scan plugins in a separate process HINSTANCE hLib = NULL; { #ifdef MODPLUG_TRACKER ExceptionHandler::Context ectx{ MPT_UFORMAT("VST Plugin: {}")(plug->dllPath.ToUnicode()) }; ExceptionHandler::ContextSetter ectxguard{&ectx}; #endif // MODPLUG_TRACKER validPlug = TryLoadPlugin(maskCrashes, plug, hLib, exception); } if(hLib) { FreeLibrary(hLib); } if(exception != 0) { CVstPluginManager::ReportPlugException(MPT_UFORMAT("Exception {} while trying to load plugin \"{}\"!\n")(mpt::ufmt::HEX0<8>(exception), plug->libraryName)); } #endif // MPT_WITH_VST // Now it should be safe to assume that this plugin loaded properly. :) theApp.GetSettings().Remove(U_("VST Plugins"), U_("FailedPlugin")); // If OK, write the information in PluginCache if(validPlug) { pluginList.push_back(plug); plug->WriteToCache(); } else { delete plug; } return (validPlug ? plug : nullptr); } // Remove a plugin from the list of known plugins and release any remaining instances of it. bool CVstPluginManager::RemovePlugin(VSTPluginLib *pFactory) { for(const_iterator p = begin(); p != end(); p++) { VSTPluginLib *plug = *p; if(plug == pFactory) { // Kill all instances of this plugin CriticalSection cs; while(plug->pPluginsList != nullptr) { plug->pPluginsList->Release(); } pluginList.erase(p); delete plug; return true; } } return false; } #endif // MODPLUG_TRACKER // Create an instance of a plugin. bool CVstPluginManager::CreateMixPlugin(SNDMIXPLUGIN &mixPlugin, CSoundFile &sndFile) { VSTPluginLib *pFound = nullptr; #ifdef MODPLUG_TRACKER mixPlugin.SetAutoSuspend(TrackerSettings::Instance().enableAutoSuspend); #endif // MODPLUG_TRACKER // Find plugin in library enum PlugMatchQuality { kNoMatch, kMatchName, kMatchId, kMatchNameAndId, }; PlugMatchQuality match = kNoMatch; // "Match quality" of found plugin. Higher value = better match. #if MPT_OS_WINDOWS && !MPT_OS_WINDOWS_WINRT const mpt::PathString libraryName = mpt::PathString::FromUnicode(mixPlugin.GetLibraryName()); #else const std::string libraryName = mpt::ToCharset(mpt::Charset::UTF8, mixPlugin.GetLibraryName()); #endif for(const auto &plug : pluginList) { const bool matchID = (plug->pluginId1 == mixPlugin.Info.dwPluginId1) && (plug->pluginId2 == mixPlugin.Info.dwPluginId2); #if MPT_OS_WINDOWS && !MPT_OS_WINDOWS_WINRT const bool matchName = !mpt::PathString::CompareNoCase(plug->libraryName, libraryName); #else const bool matchName = !mpt::CompareNoCaseAscii(plug->libraryName.ToUTF8(), libraryName); #endif if(matchID && matchName) { pFound = plug; #ifdef MPT_WITH_VST if(plug->IsNative(false)) { break; } #endif // MPT_WITH_VST // If the plugin isn't native, first check if a native version can be found. match = kMatchNameAndId; } else if(matchID && match < kMatchId) { pFound = plug; match = kMatchId; } else if(matchName && match < kMatchName) { pFound = plug; match = kMatchName; } } if(pFound != nullptr && pFound->Create != nullptr) { IMixPlugin *plugin = pFound->Create(*pFound, sndFile, &mixPlugin); return plugin != nullptr; } #ifdef MODPLUG_TRACKER bool maskCrashes = TrackerSettings::Instance().BrokenPluginsWorkaroundVSTMaskAllCrashes; if(!pFound && (mixPlugin.GetLibraryName() != U_(""))) { // Try finding the plugin DLL in the plugin directory or plugin cache instead. mpt::PathString fullPath = TrackerSettings::Instance().PathPlugins.GetDefaultDir(); if(fullPath.empty()) { fullPath = theApp.GetInstallPath() + P_("Plugins\\"); } fullPath += mpt::PathString::FromUnicode(mixPlugin.GetLibraryName()) + P_(".dll"); pFound = AddPlugin(fullPath, maskCrashes); if(!pFound) { // Try plugin cache (search for library name) SettingsContainer &cacheFile = theApp.GetPluginCache(); mpt::ustring IDs = cacheFile.Read(cacheSection, mixPlugin.GetLibraryName(), U_("")); if(IDs.length() >= 16) { fullPath = cacheFile.Read(cacheSection, IDs, P_("")); if(!fullPath.empty()) { fullPath = theApp.PathInstallRelativeToAbsolute(fullPath); if(fullPath.IsFile()) { pFound = AddPlugin(fullPath, maskCrashes); } } } } } #ifdef MPT_WITH_VST if(pFound && mixPlugin.Info.dwPluginId1 == Vst::kEffectMagic) { Vst::AEffect *pEffect = nullptr; HINSTANCE hLibrary = nullptr; bool validPlugin = false; pEffect = CVstPlugin::LoadPlugin(maskCrashes, *pFound, hLibrary, TrackerSettings::Instance().bridgeAllPlugins ? CVstPlugin::BridgeMode::ForceBridgeWithFallback : CVstPlugin::BridgeMode::Automatic); if(pEffect != nullptr && pEffect->dispatcher != nullptr && pEffect->magic == Vst::kEffectMagic) { validPlugin = true; GetPluginInformation(maskCrashes, pEffect, *pFound); // Update cached information pFound->WriteToCache(); CVstPlugin *pVstPlug = new (std::nothrow) CVstPlugin(maskCrashes, hLibrary, *pFound, mixPlugin, *pEffect, sndFile); if(pVstPlug == nullptr) { validPlugin = false; } } if(!validPlugin) { FreeLibrary(hLibrary); CVstPluginManager::ReportPlugException(MPT_UFORMAT("Unable to create plugin \"{}\"!\n")(pFound->libraryName)); } return validPlugin; } else { // "plug not found" notification code MOVED to CSoundFile::Create #ifdef VST_LOG MPT_LOG_GLOBAL(LogDebug, "VST", U_("Unknown plugin")); #endif } #endif // MPT_WITH_VST #endif // MODPLUG_TRACKER return false; } #ifdef MODPLUG_TRACKER void CVstPluginManager::OnIdle() { for(auto &factory : pluginList) { // Note: bridged plugins won't receive these messages and generate their own idle messages. IMixPlugin *p = factory->pPluginsList; while (p) { //rewbs. VSTCompliance: A specific plug has requested indefinite periodic processing time. p->Idle(); //We need to update all open editors CAbstractVstEditor *editor = p->GetEditor(); if (editor && editor->m_hWnd) { editor->UpdateParamDisplays(); } //end rewbs. VSTCompliance: p = p->GetNextInstance(); } } } void CVstPluginManager::ReportPlugException(const mpt::ustring &msg) { Reporting::Notification(msg); #ifdef VST_LOG MPT_LOG_GLOBAL(LogDebug, "VST", mpt::ToUnicode(msg)); #endif } #endif // MODPLUG_TRACKER OPENMPT_NAMESPACE_END #endif // NO_PLUGINS libopenmpt-0.6.1+release.autotools/soundlib/plugins/PluginManager.h0000644000175000017500000001342514151417047022467 00000000000000/* * PluginManager.h * --------------- * Purpose: Plugin management * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" OPENMPT_NAMESPACE_BEGIN constexpr int32 PLUGMAGIC(char a, char b, char c, char d) noexcept { return static_cast((static_cast(a) << 24) | (static_cast(b) << 16) | (static_cast(c) << 8) | (static_cast(d) << 0)); } //#define kBuzzMagic PLUGMAGIC('B', 'u', 'z', 'z') inline constexpr int32 kDmoMagic = PLUGMAGIC('D', 'X', 'M', 'O'); class CSoundFile; class IMixPlugin; struct SNDMIXPLUGIN; enum PluginArch : int; struct VSTPluginLib { public: enum PluginCategory : uint8 { // Same plugin categories as defined in VST SDK catUnknown = 0, catEffect, // Simple Effect catSynth, // VST Instrument (Synths, samplers,...) catAnalysis, // Scope, Tuner, ... catMastering, // Dynamics, ... catSpacializer, // Panners, ... catRoomFx, // Delays and Reverbs catSurroundFx, // Dedicated surround processor catRestoration, // Denoiser, ... catOfflineProcess, // Offline Process catShell, // Plug-in is container of other plug-ins catGenerator, // Tone Generator, ... // Custom categories catDMO, // DirectX media object plugin catHidden, // For internal plugins that should not be visible to the user (e.g. because they only exist for legacy reasons) numCategories }; public: using CreateProc = IMixPlugin *(*)(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct); IMixPlugin *pPluginsList = nullptr; // Pointer to first plugin instance (this instance carries pointers to other instances) CreateProc Create; // Factory to call for this plugin mpt::PathString libraryName; // Display name mpt::PathString dllPath; // Full path name #ifdef MODPLUG_TRACKER mpt::ustring tags; // User tags CString vendor; #endif // MODPLUG_TRACKER int32 pluginId1 = 0; // Plugin type (kEffectMagic, kDmoMagic, ...) int32 pluginId2 = 0; // Plugin unique ID PluginCategory category = catUnknown; const bool isBuiltIn : 1; bool isInstrument : 1; bool useBridge : 1, shareBridgeInstance : 1, modernBridge : 1; protected: mutable uint8 dllArch = 0; public: VSTPluginLib(CreateProc factoryProc, bool isBuiltIn, const mpt::PathString &dllPath, const mpt::PathString &libraryName #ifdef MODPLUG_TRACKER , const mpt::ustring &tags = mpt::ustring(), const CString &vendor = CString() #endif // MODPLUG_TRACKER ) : Create(factoryProc) , libraryName(libraryName), dllPath(dllPath) #ifdef MODPLUG_TRACKER , tags(tags) , vendor(vendor) #endif // MODPLUG_TRACKER , category(catUnknown) , isBuiltIn(isBuiltIn), isInstrument(false) , useBridge(false), shareBridgeInstance(true), modernBridge(true) { } #ifdef MPT_WITH_VST // Get native phost process arch encoded as plugin arch static uint8 GetNativePluginArch(); static mpt::ustring GetPluginArchName(uint8 arch); static mpt::ustring GetPluginArchNameUser(uint8 arch); // Check whether a plugin can be hosted inside OpenMPT or requires bridging uint8 GetDllArch(bool fromCache = true) const; mpt::ustring GetDllArchName(bool fromCache = true) const; mpt::ustring GetDllArchNameUser(bool fromCache = true) const; bool IsNative(bool fromCache = true) const; // Check if a plugin is native, and if it is currently unknown, assume that it is native. Use this function only for performance reasons // (e.g. if tons of unscanned plugins would slow down generation of the plugin selection dialog) bool IsNativeFromCache() const; #endif // MPT_WITH_VST void WriteToCache() const; uint32 EncodeCacheFlags() const { // Format: 00000000.0000000M.AAAAAASB.CCCCCCCI return (isInstrument ? 1 : 0) | (category << 1) | (useBridge ? 0x100 : 0) | (shareBridgeInstance ? 0x200 : 0) | ((dllArch / 8) << 10) | (modernBridge ? 0x10000 : 0) ; } void DecodeCacheFlags(uint32 flags) { category = static_cast((flags & 0xFF) >> 1); if(category >= numCategories) { category = catUnknown; } if(flags & 1) { isInstrument = true; category = catSynth; } useBridge = (flags & 0x100) != 0; shareBridgeInstance = (flags & 0x200) != 0; dllArch = ((flags >> 10) & 0x3F) * 8; modernBridge = (flags & 0x10000) != 0; } }; class CVstPluginManager { #ifndef NO_PLUGINS protected: #if defined(MPT_WITH_DMO) bool MustUnInitilizeCOM = false; #endif std::vector pluginList; public: CVstPluginManager(); ~CVstPluginManager(); using iterator = std::vector::iterator; using const_iterator = std::vector::const_iterator; iterator begin() { return pluginList.begin(); } const_iterator begin() const { return pluginList.begin(); } iterator end() { return pluginList.end(); } const_iterator end() const { return pluginList.end(); } void reserve(size_t num) { pluginList.reserve(num); } size_t size() const { return pluginList.size(); } bool IsValidPlugin(const VSTPluginLib *pLib) const; VSTPluginLib *AddPlugin(const mpt::PathString &dllPath, bool maskCrashes, const mpt::ustring &tags = mpt::ustring(), bool fromCache = true, bool *fileFound = nullptr); bool RemovePlugin(VSTPluginLib *); bool CreateMixPlugin(SNDMIXPLUGIN &, CSoundFile &); void OnIdle(); static void ReportPlugException(const mpt::ustring &msg); protected: void EnumerateDirectXDMOs(); #else // NO_PLUGINS public: const VSTPluginLib **begin() const { return nullptr; } const VSTPluginLib **end() const { return nullptr; } void reserve(size_t) { } size_t size() const { return 0; } void OnIdle() {} #endif // NO_PLUGINS }; OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/plugins/PluginMixBuffer.h0000644000175000017500000000747514155422765023023 00000000000000/* * PluginMixBuffer.h * ----------------- * Purpose: Helper class for managing plugin audio input and output buffers. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include #include #if defined(MPT_ENABLE_ARCH_INTRINSICS) || defined(MPT_WITH_VST) #include "mpt/base/aligned_array.hpp" #endif // MPT_ENABLE_ARCH_INTRINSICS || MPT_WITH_VST OPENMPT_NAMESPACE_BEGIN // At least this part of the code is ready for double-precision rendering... :> // buffer_t: Sample buffer type (float, double, ...) // bufferSize: Buffer size in samples template class PluginMixBuffer { private: #if defined(MPT_ENABLE_ARCH_INTRINSICS) || defined(MPT_WITH_VST) static constexpr std::align_val_t alignment = std::align_val_t{16}; static_assert(sizeof(mpt::aligned_array) == sizeof(std::array)); static_assert(alignof(mpt::aligned_array) == static_cast(alignment)); #endif // MPT_ENABLE_ARCH_INTRINSICS || MPT_WITH_VST protected: #if defined(MPT_ENABLE_ARCH_INTRINSICS) || defined(MPT_WITH_VST) std::vector> inputs; std::vector> outputs; #else // !(MPT_ENABLE_ARCH_INTRINSICS || MPT_WITH_VST) std::vector> inputs; std::vector> outputs; #endif // MPT_ENABLE_ARCH_INTRINSICS || MPT_WITH_VST std::vector inputsarray; std::vector outputsarray; public: // Allocate input and output buffers bool Initialize(uint32 numInputs, uint32 numOutputs) { // Short cut - we do not need to recreate the buffers. if(inputs.size() == numInputs && outputs.size() == numOutputs) { return true; } try { inputs.resize(numInputs); outputs.resize(numOutputs); inputsarray.resize(numInputs); outputsarray.resize(numOutputs); } catch(mpt::out_of_memory e) { mpt::delete_out_of_memory(e); inputs.clear(); inputs.shrink_to_fit(); outputs.clear(); outputs.shrink_to_fit(); inputsarray.clear(); inputsarray.shrink_to_fit(); outputsarray.clear(); outputsarray.shrink_to_fit(); return false; } for(uint32 i = 0; i < numInputs; i++) { inputsarray[i] = inputs[i].data(); } for(uint32 i = 0; i < numOutputs; i++) { outputsarray[i] = outputs[i].data(); } return true; } // Silence all input buffers. void ClearInputBuffers(uint32 numSamples) { MPT_ASSERT(numSamples <= bufferSize); for(size_t i = 0; i < inputs.size(); i++) { std::fill(inputs[i].data(), inputs[i].data() + numSamples, buffer_t{0}); } } // Silence all output buffers. void ClearOutputBuffers(uint32 numSamples) { MPT_ASSERT(numSamples <= bufferSize); for(size_t i = 0; i < outputs.size(); i++) { std::fill(outputs[i].data(), outputs[i].data() + numSamples, buffer_t{0}); } } PluginMixBuffer() { Initialize(2, 0); } // Return pointer to a given input or output buffer const buffer_t *GetInputBuffer(uint32 index) const { return inputs[index].data(); } const buffer_t *GetOutputBuffer(uint32 index) const { return outputs[index].data(); } buffer_t *GetInputBuffer(uint32 index) { return inputs[index].data(); } buffer_t *GetOutputBuffer(uint32 index) { return outputs[index].data(); } // Return pointer array to all input or output buffers buffer_t **GetInputBufferArray() { return inputs.empty() ? nullptr : inputsarray.data(); } buffer_t **GetOutputBufferArray() { return outputs.empty() ? nullptr : outputsarray.data(); } bool Ok() const { return (inputs.size() + outputs.size()) > 0; } }; OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/plugins/PluginStructs.h0000644000175000017500000001126214175042045022557 00000000000000/* * PluginStructs.h * --------------- * Purpose: Basic plugin structs for CSoundFile. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "../Snd_defs.h" #ifndef NO_PLUGINS #include "openmpt/base/Endian.hpp" #endif // NO_PLUGINS OPENMPT_NAMESPACE_BEGIN //////////////////////////////////////////////////////////////////// // Mix Plugins using PlugParamIndex = int32; using PlugParamValue = float; struct SNDMIXPLUGINSTATE; struct SNDMIXPLUGIN; class IMixPlugin; class CSoundFile; #ifndef NO_PLUGINS struct SNDMIXPLUGININFO { // dwInputRouting flags enum RoutingFlags { irApplyToMaster = 0x01, // Apply to master mix irBypass = 0x02, // Bypass effect irWetMix = 0x04, // Wet Mix (dry added) irExpandMix = 0x08, // [0%,100%] -> [-200%,200%] irAutoSuspend = 0x10, // Plugin will automatically suspend on silence }; int32le dwPluginId1; // Plugin type (kEffectMagic, kDmoMagic, kBuzzMagic) int32le dwPluginId2; // Plugin unique ID uint8le routingFlags; // See RoutingFlags uint8le mixMode; uint8le gain; // Divide by 10 to get real gain uint8le reserved; uint32le dwOutputRouting; // 0 = send to master 0x80 + x = send to plugin x uint32le dwReserved[4]; // Reserved for routing info mpt::modecharbuf<32, mpt::String::nullTerminated> szName; // User-chosen plugin display name - this is locale ANSI! mpt::modecharbuf<64, mpt::String::nullTerminated> szLibraryName; // original DLL name - this is UTF-8! // Should only be called from SNDMIXPLUGIN::SetBypass() and IMixPlugin::Bypass() void SetBypass(bool bypass = true) { if(bypass) routingFlags |= irBypass; else routingFlags &= uint8(~irBypass); } }; MPT_BINARY_STRUCT(SNDMIXPLUGININFO, 128) // this is directly written to files, so the size must be correct! struct SNDMIXPLUGIN { IMixPlugin *pMixPlugin = nullptr; std::vector pluginData; SNDMIXPLUGININFO Info = {}; float fDryRatio = 0; int32 defaultProgram = 0; int32 editorX = 0, editorY = 0; #if defined(MPT_ENABLE_CHARSET_LOCALE) const char * GetNameLocale() const { return Info.szName.buf; } mpt::ustring GetName() const { return mpt::ToUnicode(mpt::Charset::Locale, Info.szName); } #endif // MPT_ENABLE_CHARSET_LOCALE mpt::ustring GetLibraryName() const { return mpt::ToUnicode(mpt::Charset::UTF8, Info.szLibraryName); } // Check if a plugin is loaded into this slot (also returns true if the plugin in this slot has not been found) bool IsValidPlugin() const { return (Info.dwPluginId1 | Info.dwPluginId2) != 0; } // Input routing getters uint8 GetGain() const { return Info.gain; } uint8 GetMixMode() const { return Info.mixMode; } bool IsMasterEffect() const { return (Info.routingFlags & SNDMIXPLUGININFO::irApplyToMaster) != 0; } bool IsWetMix() const { return (Info.routingFlags & SNDMIXPLUGININFO::irWetMix) != 0; } bool IsExpandedMix() const { return (Info.routingFlags & SNDMIXPLUGININFO::irExpandMix) != 0; } bool IsBypassed() const { return (Info.routingFlags & SNDMIXPLUGININFO::irBypass) != 0; } bool IsAutoSuspendable() const { return (Info.routingFlags & SNDMIXPLUGININFO::irAutoSuspend) != 0; } // Input routing setters void SetGain(uint8 gain); void SetMixMode(uint8 mixMode) { Info.mixMode = mixMode; } void SetMasterEffect(bool master = true) { if(master) Info.routingFlags |= SNDMIXPLUGININFO::irApplyToMaster; else Info.routingFlags &= uint8(~SNDMIXPLUGININFO::irApplyToMaster); } void SetWetMix(bool wetMix = true) { if(wetMix) Info.routingFlags |= SNDMIXPLUGININFO::irWetMix; else Info.routingFlags &= uint8(~SNDMIXPLUGININFO::irWetMix); } void SetExpandedMix(bool expanded = true) { if(expanded) Info.routingFlags |= SNDMIXPLUGININFO::irExpandMix; else Info.routingFlags &= uint8(~SNDMIXPLUGININFO::irExpandMix); } void SetBypass(bool bypass = true); void SetAutoSuspend(bool suspend = true) { if(suspend) Info.routingFlags |= SNDMIXPLUGININFO::irAutoSuspend; else Info.routingFlags &= uint8(~SNDMIXPLUGININFO::irAutoSuspend); } // Output routing getters bool IsOutputToMaster() const { return Info.dwOutputRouting == 0; } PLUGINDEX GetOutputPlugin() const { return Info.dwOutputRouting >= 0x80 ? static_cast(Info.dwOutputRouting - 0x80) : PLUGINDEX_INVALID; } // Output routing setters void SetOutputToMaster() { Info.dwOutputRouting = 0; } void SetOutputPlugin(PLUGINDEX plugin) { if(plugin < MAX_MIXPLUGINS) Info.dwOutputRouting = plugin + 0x80; else Info.dwOutputRouting = 0; } void Destroy(); }; bool CreateMixPluginProc(SNDMIXPLUGIN &mixPlugin, CSoundFile &sndFile); #endif // NO_PLUGINS OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/plugins/PlugInterface.cpp0000644000175000017500000007056614175042045023030 00000000000000/* * PlugInterface.cpp * ----------------- * Purpose: Default plugin interface implementation * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "../Sndfile.h" #include "PlugInterface.h" #include "PluginManager.h" #include "../../common/FileReader.h" #ifdef MODPLUG_TRACKER #include "../../mptrack/Moddoc.h" #include "../../mptrack/Mainfrm.h" #include "../../mptrack/InputHandler.h" #include "../../mptrack/AbstractVstEditor.h" #include "../../mptrack/DefaultVstEditor.h" // LoadProgram/SaveProgram #include "../../mptrack/FileDialog.h" #include "../../mptrack/VstPresets.h" #include "../../common/mptFileIO.h" #include "../mod_specifications.h" #endif // MODPLUG_TRACKER #include "mpt/base/aligned_array.hpp" #include "mpt/io/base.hpp" #include "mpt/io/io.hpp" #include "mpt/io/io_span.hpp" #include #ifndef NO_PLUGINS OPENMPT_NAMESPACE_BEGIN #ifdef MODPLUG_TRACKER CModDoc *IMixPlugin::GetModDoc() { return m_SndFile.GetpModDoc(); } const CModDoc *IMixPlugin::GetModDoc() const { return m_SndFile.GetpModDoc(); } #endif // MODPLUG_TRACKER IMixPlugin::IMixPlugin(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct) : m_Factory(factory) , m_SndFile(sndFile) , m_pMixStruct(mixStruct) { m_SndFile.m_loadedPlugins++; m_MixState.pMixBuffer = mpt::align_bytes<8, MIXBUFFERSIZE * 2>(m_MixBuffer); while(m_pMixStruct != &(m_SndFile.m_MixPlugins[m_nSlot]) && m_nSlot < MAX_MIXPLUGINS - 1) { m_nSlot++; } } IMixPlugin::~IMixPlugin() { #ifdef MODPLUG_TRACKER CloseEditor(); CriticalSection cs; #endif // MODPLUG_TRACKER // First thing to do, if we don't want to hang in a loop if (m_Factory.pPluginsList == this) m_Factory.pPluginsList = m_pNext; if (m_pMixStruct) { m_pMixStruct->pMixPlugin = nullptr; m_pMixStruct = nullptr; } if (m_pNext) m_pNext->m_pPrev = m_pPrev; if (m_pPrev) m_pPrev->m_pNext = m_pNext; m_pPrev = nullptr; m_pNext = nullptr; m_SndFile.m_loadedPlugins--; } void IMixPlugin::InsertIntoFactoryList() { m_pMixStruct->pMixPlugin = this; m_pNext = m_Factory.pPluginsList; if(m_Factory.pPluginsList) { m_Factory.pPluginsList->m_pPrev = this; } m_Factory.pPluginsList = this; } #ifdef MODPLUG_TRACKER void IMixPlugin::SetSlot(PLUGINDEX slot) { m_nSlot = slot; m_pMixStruct = &m_SndFile.m_MixPlugins[slot]; } PlugParamValue IMixPlugin::GetScaledUIParam(PlugParamIndex param) { const auto [paramMin, paramMax] = GetParamUIRange(param); return (std::clamp(GetParameter(param), paramMin, paramMax) - paramMin) / (paramMax - paramMin); } void IMixPlugin::SetScaledUIParam(PlugParamIndex param, PlugParamValue value) { const auto [paramMin, paramMax] = GetParamUIRange(param); const auto scaledVal = paramMin + std::clamp(value, 0.0f, 1.0f) * (paramMax - paramMin); SetParameter(param, scaledVal); } CString IMixPlugin::GetFormattedParamName(PlugParamIndex param) { CString paramName = GetParamName(param); CString name; if(paramName.IsEmpty()) { name = MPT_CFORMAT("{}: Parameter {}")(mpt::cfmt::dec0<2>(param), mpt::cfmt::dec0<2>(param)); } else { name = MPT_CFORMAT("{}: {}")(mpt::cfmt::dec0<2>(param), paramName); } return name; } // Get a parameter's current value, represented by the plugin. CString IMixPlugin::GetFormattedParamValue(PlugParamIndex param) { CString paramDisplay = GetParamDisplay(param); CString paramUnits = GetParamLabel(param); paramDisplay.Trim(); paramUnits.Trim(); paramDisplay += _T(" ") + paramUnits; return paramDisplay; } CString IMixPlugin::GetFormattedProgramName(int32 index) { CString rawname = GetProgramName(index); // Let's start counting at 1 for the program name (as most MIDI hardware / software does) index++; CString formattedName; if(rawname[0] >= 0 && rawname[0] < _T(' ')) formattedName = MPT_CFORMAT("{} - Program {}")(mpt::cfmt::dec0<2>(index), index); else formattedName = MPT_CFORMAT("{} - {}")(mpt::cfmt::dec0<2>(index), rawname); return formattedName; } void IMixPlugin::SetEditorPos(int32 x, int32 y) { m_pMixStruct->editorX = x; m_pMixStruct->editorY = y; } void IMixPlugin::GetEditorPos(int32 &x, int32 &y) const { x = m_pMixStruct->editorX; y = m_pMixStruct->editorY; } #endif // MODPLUG_TRACKER bool IMixPlugin::IsBypassed() const { return m_pMixStruct != nullptr && m_pMixStruct->IsBypassed(); } void IMixPlugin::RecalculateGain() { float gain = 0.1f * static_cast(m_pMixStruct ? m_pMixStruct->GetGain() : 10); if(gain < 0.1f) gain = 1.0f; if(IsInstrument()) { gain /= m_SndFile.GetPlayConfig().getVSTiAttenuation(); gain = static_cast(gain * (m_SndFile.m_nVSTiVolume / m_SndFile.GetPlayConfig().getNormalVSTiVol())); } m_fGain = gain; } void IMixPlugin::SetDryRatio(uint32 param) { param = std::min(param, uint32(127)); m_pMixStruct->fDryRatio = 1.0f - (param / 127.0f); } void IMixPlugin::Bypass(bool bypass) { m_pMixStruct->Info.SetBypass(bypass); #ifdef MODPLUG_TRACKER if(m_SndFile.GetpModDoc()) m_SndFile.GetpModDoc()->UpdateAllViews(nullptr, PluginHint(m_nSlot + 1).Info(), nullptr); #endif // MODPLUG_TRACKER } double IMixPlugin::GetOutputLatency() const { if(GetSoundFile().IsRenderingToDisc()) return 0; else return GetSoundFile().m_TimingInfo.OutputLatency; } void IMixPlugin::ProcessMixOps(float * MPT_RESTRICT pOutL, float * MPT_RESTRICT pOutR, float * MPT_RESTRICT leftPlugOutput, float * MPT_RESTRICT rightPlugOutput, uint32 numFrames) { /* float *leftPlugOutput; float *rightPlugOutput; if(m_Effect.numOutputs == 1) { // If there was just the one plugin output we copy it into our 2 outputs leftPlugOutput = rightPlugOutput = mixBuffer.GetOutputBuffer(0); } else if(m_Effect.numOutputs > 1) { // Otherwise we actually only cater for two outputs max (outputs > 2 have been mixed together already). leftPlugOutput = mixBuffer.GetOutputBuffer(0); rightPlugOutput = mixBuffer.GetOutputBuffer(1); } else { return; }*/ // -> mixop == 0 : normal processing // -> mixop == 1 : MIX += DRY - WET * wetRatio // -> mixop == 2 : MIX += WET - DRY * dryRatio // -> mixop == 3 : MIX -= WET - DRY * wetRatio // -> mixop == 4 : MIX -= middle - WET * wetRatio + middle - DRY // -> mixop == 5 : MIX_L += wetRatio * (WET_L - DRY_L) + dryRatio * (DRY_R - WET_R) // MIX_R += dryRatio * (WET_L - DRY_L) + wetRatio * (DRY_R - WET_R) MPT_ASSERT(m_pMixStruct != nullptr); int mixop; if(IsInstrument()) { // Force normal mix mode for instruments mixop = 0; } else { mixop = m_pMixStruct->GetMixMode(); } float wetRatio = 1 - m_pMixStruct->fDryRatio; float dryRatio = IsInstrument() ? 1 : m_pMixStruct->fDryRatio; // Always mix full dry if this is an instrument // Wet / Dry range expansion [0,1] -> [-1,1] if(GetNumInputChannels() > 0 && m_pMixStruct->IsExpandedMix()) { wetRatio = 2.0f * wetRatio - 1.0f; dryRatio = -wetRatio; } wetRatio *= m_fGain; dryRatio *= m_fGain; float * MPT_RESTRICT plugInputL = m_mixBuffer.GetInputBuffer(0); float * MPT_RESTRICT plugInputR = m_mixBuffer.GetInputBuffer(1); // Mix operation switch(mixop) { // Default mix case 0: for(uint32 i = 0; i < numFrames; i++) { //rewbs.wetratio - added the factors. [20040123] pOutL[i] += leftPlugOutput[i] * wetRatio + plugInputL[i] * dryRatio; pOutR[i] += rightPlugOutput[i] * wetRatio + plugInputR[i] * dryRatio; } break; // Wet subtract case 1: for(uint32 i = 0; i < numFrames; i++) { pOutL[i] += plugInputL[i] - leftPlugOutput[i] * wetRatio; pOutR[i] += plugInputR[i] - rightPlugOutput[i] * wetRatio; } break; // Dry subtract case 2: for(uint32 i = 0; i < numFrames; i++) { pOutL[i] += leftPlugOutput[i] - plugInputL[i] * dryRatio; pOutR[i] += rightPlugOutput[i] - plugInputR[i] * dryRatio; } break; // Mix subtract case 3: for(uint32 i = 0; i < numFrames; i++) { pOutL[i] -= leftPlugOutput[i] - plugInputL[i] * wetRatio; pOutR[i] -= rightPlugOutput[i] - plugInputR[i] * wetRatio; } break; // Middle subtract case 4: for(uint32 i = 0; i < numFrames; i++) { float middle = (pOutL[i] + plugInputL[i] + pOutR[i] + plugInputR[i]) / 2.0f; pOutL[i] -= middle - leftPlugOutput[i] * wetRatio + middle - plugInputL[i]; pOutR[i] -= middle - rightPlugOutput[i] * wetRatio + middle - plugInputR[i]; } break; // Left / Right balance case 5: if(m_pMixStruct->IsExpandedMix()) { wetRatio /= 2.0f; dryRatio /= 2.0f; } for(uint32 i = 0; i < numFrames; i++) { pOutL[i] += wetRatio * (leftPlugOutput[i] - plugInputL[i]) + dryRatio * (plugInputR[i] - rightPlugOutput[i]); pOutR[i] += dryRatio * (leftPlugOutput[i] - plugInputL[i]) + wetRatio * (plugInputR[i] - rightPlugOutput[i]); } break; } // If dry mix is ticked, we add the unprocessed buffer, // except if this is an instrument since then it has already been done: if(m_pMixStruct->IsWetMix() && !IsInstrument()) { for(uint32 i = 0; i < numFrames; i++) { pOutL[i] += plugInputL[i]; pOutR[i] += plugInputR[i]; } } } // Render some silence and return maximum level returned by the plugin. float IMixPlugin::RenderSilence(uint32 numFrames) { // The JUCE framework doesn't like processing while being suspended. const bool wasSuspended = !IsResumed(); if(wasSuspended) { Resume(); } float out[2][MIXBUFFERSIZE]; // scratch buffers float maxVal = 0.0f; m_mixBuffer.ClearInputBuffers(MIXBUFFERSIZE); while(numFrames > 0) { uint32 renderSamples = numFrames; LimitMax(renderSamples, mpt::saturate_cast(std::size(out[0]))); MemsetZero(out); Process(out[0], out[1], renderSamples); for(size_t i = 0; i < renderSamples; i++) { maxVal = std::max(maxVal, std::fabs(out[0][i])); maxVal = std::max(maxVal, std::fabs(out[1][i])); } numFrames -= renderSamples; } if(wasSuspended) { Suspend(); } return maxVal; } // Get list of plugins to which output is sent. A nullptr indicates master output. size_t IMixPlugin::GetOutputPlugList(std::vector &list) { // At the moment we know there will only be 1 output. // Returning nullptr means plugin outputs directly to master. list.clear(); IMixPlugin *outputPlug = nullptr; if(!m_pMixStruct->IsOutputToMaster()) { PLUGINDEX nOutput = m_pMixStruct->GetOutputPlugin(); if(nOutput > m_nSlot && nOutput != PLUGINDEX_INVALID) { outputPlug = m_SndFile.m_MixPlugins[nOutput].pMixPlugin; } } list.push_back(outputPlug); return 1; } // Get a list of plugins that send data to this plugin. size_t IMixPlugin::GetInputPlugList(std::vector &list) { std::vector candidatePlugOutputs; list.clear(); for(PLUGINDEX plug = 0; plug < MAX_MIXPLUGINS; plug++) { IMixPlugin *candidatePlug = m_SndFile.m_MixPlugins[plug].pMixPlugin; if(candidatePlug) { candidatePlug->GetOutputPlugList(candidatePlugOutputs); for(auto &outPlug : candidatePlugOutputs) { if(outPlug == this) { list.push_back(candidatePlug); break; } } } } return list.size(); } // Get a list of instruments that send data to this plugin. size_t IMixPlugin::GetInputInstrumentList(std::vector &list) { list.clear(); const PLUGINDEX nThisMixPlug = m_nSlot + 1; //m_nSlot is position in mixplug array. for(INSTRUMENTINDEX ins = 0; ins <= m_SndFile.GetNumInstruments(); ins++) { if(m_SndFile.Instruments[ins] != nullptr && m_SndFile.Instruments[ins]->nMixPlug == nThisMixPlug) { list.push_back(ins); } } return list.size(); } size_t IMixPlugin::GetInputChannelList(std::vector &list) { list.clear(); PLUGINDEX nThisMixPlug = m_nSlot + 1; //m_nSlot is position in mixplug array. const CHANNELINDEX chnCount = m_SndFile.GetNumChannels(); for(CHANNELINDEX nChn=0; nChndefaultProgram = -1; // Default implementation: Save all parameter values PlugParamIndex numParams = std::min(GetNumParameters(), static_cast((std::numeric_limits::max() - sizeof(uint32)) / sizeof(IEEE754binary32LE))); uint32 nLen = numParams * sizeof(IEEE754binary32LE); if (!nLen) return; nLen += sizeof(uint32); try { m_pMixStruct->pluginData.resize(nLen); auto memFile = std::make_pair(mpt::as_span(m_pMixStruct->pluginData), mpt::IO::Offset(0)); mpt::IO::WriteIntLE(memFile, 0); // Plugin data type BeginGetProgram(); for(PlugParamIndex i = 0; i < numParams; i++) { mpt::IO::Write(memFile, IEEE754binary32LE(GetParameter(i))); } EndGetProgram(); } catch(mpt::out_of_memory e) { m_pMixStruct->pluginData.clear(); mpt::delete_out_of_memory(e); } } void IMixPlugin::RestoreAllParameters(int32 /*program*/) { if(m_pMixStruct != nullptr && m_pMixStruct->pluginData.size() >= sizeof(uint32)) { FileReader memFile(mpt::as_span(m_pMixStruct->pluginData)); uint32 type = memFile.ReadUint32LE(); if(type == 0) { const uint32 numParams = GetNumParameters(); if((m_pMixStruct->pluginData.size() - sizeof(uint32)) >= (numParams * sizeof(IEEE754binary32LE))) { BeginSetProgram(); for(uint32 i = 0; i < numParams; i++) { const auto value = memFile.ReadFloatLE(); SetParameter(i, std::isfinite(value) ? value : 0.0f); } EndSetProgram(); } } } } #ifdef MODPLUG_TRACKER void IMixPlugin::ToggleEditor() { // We only really need this mutex for bridged plugins, as we may be processing window messages (in the same thread) while the editor opens. // The user could press the toggle button while the editor is loading and thus close the editor while still being initialized. // Note that this does not protect against closing the module while the editor is still loading. static bool initializing = false; if(initializing) return; initializing = true; if (m_pEditor) { CloseEditor(); } else { m_pEditor = OpenEditor(); if (m_pEditor) m_pEditor->OpenEditor(CMainFrame::GetMainFrame()); } initializing = false; } // Provide default plugin editor CAbstractVstEditor *IMixPlugin::OpenEditor() { try { return new CDefaultVstEditor(*this); } catch(mpt::out_of_memory e) { mpt::delete_out_of_memory(e); return nullptr; } } void IMixPlugin::CloseEditor() { if(m_pEditor) { if (m_pEditor->m_hWnd) m_pEditor->DoClose(); delete m_pEditor; m_pEditor = nullptr; } } // Automate a parameter from the plugin GUI (both custom and default plugin GUI) void IMixPlugin::AutomateParameter(PlugParamIndex param) { CModDoc *modDoc = GetModDoc(); if(modDoc == nullptr) { return; } // TODO: Check if any params are actually automatable, and if there are but this one isn't, chicken out if(m_recordAutomation) { // Record parameter change modDoc->RecordParamChange(GetSlot(), param); } modDoc->SendNotifyMessageToAllViews(WM_MOD_PLUGPARAMAUTOMATE, m_nSlot, param); if(auto *vstEditor = GetEditor(); vstEditor && vstEditor->m_hWnd) { // Mark track modified if GUI is open and format supports plugins SetModified(); // Do not use InputHandler in case we are coming from a bridged plugin editor if((GetAsyncKeyState(VK_SHIFT) & 0x8000) && TrackerSettings::Instance().midiMappingInPluginEditor) { // Shift pressed -> Open MIDI mapping dialog CMainFrame::GetMainFrame()->PostMessage(WM_MOD_MIDIMAPPING, m_nSlot, param); } // Learn macro int macroToLearn = vstEditor->GetLearnMacro(); if (macroToLearn > -1) { modDoc->LearnMacro(macroToLearn, param); vstEditor->SetLearnMacro(-1); } } } void IMixPlugin::SetModified() { CModDoc *modDoc = GetModDoc(); if(modDoc != nullptr && m_SndFile.GetModSpecifications().supportsPlugins) { modDoc->SetModified(); } } bool IMixPlugin::SaveProgram() { mpt::PathString defaultDir = TrackerSettings::Instance().PathPluginPresets.GetWorkingDir(); const bool useDefaultDir = !defaultDir.empty(); if(!useDefaultDir && m_Factory.dllPath.IsFile()) { defaultDir = m_Factory.dllPath.GetPath(); } CString progName = m_Factory.libraryName.ToCString() + _T(" - ") + GetCurrentProgramName(); SanitizeFilename(progName); FileDialog dlg = SaveFileDialog() .DefaultExtension("fxb") .DefaultFilename(progName) .ExtensionFilter("VST Plugin Programs (*.fxp)|*.fxp|" "VST Plugin Banks (*.fxb)|*.fxb||") .WorkingDirectory(defaultDir); if(!dlg.Show(m_pEditor)) return false; if(useDefaultDir) { TrackerSettings::Instance().PathPluginPresets.SetWorkingDir(dlg.GetWorkingDirectory()); } const bool isBank = (dlg.GetExtension() == P_("fxb")); try { mpt::SafeOutputFile sf(dlg.GetFirstFile(), std::ios::binary, mpt::FlushModeFromBool(TrackerSettings::Instance().MiscFlushFileBuffersOnSave)); mpt::ofstream &f = sf; f.exceptions(f.exceptions() | std::ios::badbit | std::ios::failbit); if(f.good() && VSTPresets::SaveFile(f, *this, isBank)) return true; } catch(const std::exception &) { } Reporting::Error("Error saving preset.", m_pEditor); return false; } bool IMixPlugin::LoadProgram(mpt::PathString fileName) { mpt::PathString defaultDir = TrackerSettings::Instance().PathPluginPresets.GetWorkingDir(); bool useDefaultDir = !defaultDir.empty(); if(!useDefaultDir && m_Factory.dllPath.IsFile()) { defaultDir = m_Factory.dllPath.GetPath(); } if(fileName.empty()) { FileDialog dlg = OpenFileDialog() .DefaultExtension("fxp") .ExtensionFilter("VST Plugin Programs and Banks (*.fxp,*.fxb)|*.fxp;*.fxb|" "VST Plugin Programs (*.fxp)|*.fxp|" "VST Plugin Banks (*.fxb)|*.fxb|" "All Files|*.*||") .WorkingDirectory(defaultDir); if(!dlg.Show(m_pEditor)) return false; if(useDefaultDir) { TrackerSettings::Instance().PathPluginPresets.SetWorkingDir(dlg.GetWorkingDirectory()); } fileName = dlg.GetFirstFile(); } const char *errorStr = nullptr; InputFile f(fileName, SettingCacheCompleteFileBeforeLoading()); if(f.IsValid()) { FileReader file = GetFileReader(f); errorStr = VSTPresets::GetErrorMessage(VSTPresets::LoadFile(file, *this)); } else { errorStr = "Can't open file."; } if(errorStr == nullptr) { if(GetModDoc() != nullptr && GetSoundFile().GetModSpecifications().supportsPlugins) { GetModDoc()->SetModified(); } return true; } else { Reporting::Error(errorStr, m_pEditor); return false; } } #endif // MODPLUG_TRACKER //////////////////////////////////////////////////////////////////// // IMidiPlugin: Default implementation of plugins with MIDI input // //////////////////////////////////////////////////////////////////// IMidiPlugin::IMidiPlugin(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct) : IMixPlugin(factory, sndFile, mixStruct) , m_MidiCh{{}} { for(auto &chn : m_MidiCh) { chn.midiPitchBendPos = EncodePitchBendParam(MIDIEvents::pitchBendCentre); // centre pitch bend on all channels chn.ResetProgram(); } } void IMidiPlugin::ApplyPitchWheelDepth(int32 &value, int8 pwd) { if(pwd != 0) { value = (value * ((MIDIEvents::pitchBendMax - MIDIEvents::pitchBendCentre + 1) / 64)) / pwd; } else { value = 0; } } // Get the MIDI channel currently associated with a given tracker channel uint8 IMidiPlugin::GetMidiChannel(const ModChannel &chn, CHANNELINDEX trackChannel) const { if(auto ins = chn.pModInstrument; ins != nullptr) return ins->GetMIDIChannel(chn, trackChannel); else return 0; } uint8 IMidiPlugin::GetMidiChannel(CHANNELINDEX trackChannel) const { if(trackChannel < std::size(m_SndFile.m_PlayState.Chn)) return GetMidiChannel(m_SndFile.m_PlayState.Chn[trackChannel], trackChannel); else return 0; } void IMidiPlugin::MidiCC(MIDIEvents::MidiCC nController, uint8 nParam, CHANNELINDEX trackChannel) { //Error checking LimitMax(nController, MIDIEvents::MIDICC_end); LimitMax(nParam, uint8(127)); auto midiCh = GetMidiChannel(trackChannel); if(m_SndFile.m_playBehaviour[kMIDICCBugEmulation]) MidiSend(MIDIEvents::Event(MIDIEvents::evControllerChange, midiCh, nParam, static_cast(nController))); // param and controller are swapped (old broken implementation) else MidiSend(MIDIEvents::CC(nController, midiCh, nParam)); } // Set MIDI pitch for given MIDI channel to the specified raw 14-bit position void IMidiPlugin::MidiPitchBendRaw(int32 pitchbend, CHANNELINDEX trackerChn) { SendMidiPitchBend(GetMidiChannel(trackerChn), EncodePitchBendParam(Clamp(pitchbend, MIDIEvents::pitchBendMin, MIDIEvents::pitchBendMax))); } // Bend MIDI pitch for given MIDI channel using fine tracker param (one unit = 1/64th of a note step) void IMidiPlugin::MidiPitchBend(int32 increment, int8 pwd, CHANNELINDEX trackerChn) { auto midiCh = GetMidiChannel(trackerChn); if(m_SndFile.m_playBehaviour[kOldMIDIPitchBends]) { // OpenMPT Legacy: Old pitch slides never were really accurate, but setting the PWD to 13 in plugins would give the closest results. increment = (increment * 0x800 * 13) / (0xFF * pwd); increment = EncodePitchBendParam(increment); } else { increment = EncodePitchBendParam(increment); ApplyPitchWheelDepth(increment, pwd); } int32 newPitchBendPos = (increment + m_MidiCh[midiCh].midiPitchBendPos) & kPitchBendMask; Limit(newPitchBendPos, EncodePitchBendParam(MIDIEvents::pitchBendMin), EncodePitchBendParam(MIDIEvents::pitchBendMax)); SendMidiPitchBend(midiCh, newPitchBendPos); } // Set MIDI pitch for given MIDI channel using fixed point pitch bend value (converted back to 0-16383 MIDI range) void IMidiPlugin::SendMidiPitchBend(uint8 midiCh, int32 newPitchBendPos) { MPT_ASSERT(EncodePitchBendParam(MIDIEvents::pitchBendMin) <= newPitchBendPos && newPitchBendPos <= EncodePitchBendParam(MIDIEvents::pitchBendMax)); m_MidiCh[midiCh].midiPitchBendPos = newPitchBendPos; MidiSend(MIDIEvents::PitchBend(midiCh, DecodePitchBendParam(newPitchBendPos))); } // Apply vibrato effect through pitch wheel commands on a given MIDI channel. void IMidiPlugin::MidiVibrato(int32 depth, int8 pwd, CHANNELINDEX trackerChn) { auto midiCh = GetMidiChannel(trackerChn); depth = EncodePitchBendParam(depth); if(depth != 0 || (m_MidiCh[midiCh].midiPitchBendPos & kVibratoFlag)) { ApplyPitchWheelDepth(depth, pwd); // Temporarily add vibrato offset to current pitch int32 newPitchBendPos = (depth + m_MidiCh[midiCh].midiPitchBendPos) & kPitchBendMask; Limit(newPitchBendPos, EncodePitchBendParam(MIDIEvents::pitchBendMin), EncodePitchBendParam(MIDIEvents::pitchBendMax)); MidiSend(MIDIEvents::PitchBend(midiCh, DecodePitchBendParam(newPitchBendPos))); } // Update vibrato status if(depth != 0) m_MidiCh[midiCh].midiPitchBendPos |= kVibratoFlag; else m_MidiCh[midiCh].midiPitchBendPos &= ~kVibratoFlag; } void IMidiPlugin::MidiCommand(const ModInstrument &instr, uint16 note, uint16 vol, CHANNELINDEX trackChannel) { if(trackChannel >= MAX_CHANNELS) return; auto midiCh = GetMidiChannel(trackChannel); PlugInstrChannel &channel = m_MidiCh[midiCh]; uint16 midiBank = instr.wMidiBank - 1; uint8 midiProg = instr.nMidiProgram - 1; bool bankChanged = (channel.currentBank != midiBank) && (midiBank < 0x4000); bool progChanged = (channel.currentProgram != midiProg) && (midiProg < 0x80); //get vol in [0,128[ uint8 volume = static_cast(std::min(vol / 2u, 127u)); // Bank change if(bankChanged) { uint8 high = static_cast(midiBank >> 7); uint8 low = static_cast(midiBank & 0x7F); //m_SndFile.ProcessMIDIMacro(trackChannel, false, m_SndFile.m_MidiCfg.Global[MIDIOUT_BANKSEL], 0, m_nSlot + 1); MidiSend(MIDIEvents::CC(MIDIEvents::MIDICC_BankSelect_Coarse, midiCh, high)); MidiSend(MIDIEvents::CC(MIDIEvents::MIDICC_BankSelect_Fine, midiCh, low)); channel.currentBank = midiBank; } // Program change // According to the MIDI specs, a bank change alone doesn't have to change the active program - it will only change the bank of subsequent program changes. // Thus we send program changes also if only the bank has changed. if(progChanged || (midiProg < 0x80 && bankChanged)) { channel.currentProgram = midiProg; //m_SndFile.ProcessMIDIMacro(trackChannel, false, m_SndFile.m_MidiCfg.Global[MIDIOUT_PROGRAM], 0, m_nSlot + 1); MidiSend(MIDIEvents::ProgramChange(midiCh, midiProg)); } // Specific Note Off if(note > NOTE_MAX_SPECIAL) { uint8 i = static_cast(note - NOTE_MAX_SPECIAL - NOTE_MIN); if(channel.noteOnMap[i][trackChannel]) { channel.noteOnMap[i][trackChannel]--; MidiSend(MIDIEvents::NoteOff(midiCh, i, 0)); } } // "Hard core" All Sounds Off on this midi and tracker channel // This one doesn't check the note mask - just one note off per note. // Also less likely to cause a VST event buffer overflow. else if(note == NOTE_NOTECUT) // ^^ { MidiSend(MIDIEvents::CC(MIDIEvents::MIDICC_AllNotesOff, midiCh, 0)); MidiSend(MIDIEvents::CC(MIDIEvents::MIDICC_AllSoundOff, midiCh, 0)); // Turn off all notes for(uint8 i = 0; i < std::size(channel.noteOnMap); i++) { channel.noteOnMap[i][trackChannel] = 0; MidiSend(MIDIEvents::NoteOff(midiCh, i, volume)); } } // All "active" notes off on this midi and tracker channel // using note mask. else if(note == NOTE_KEYOFF || note == NOTE_FADE) // ==, ~~ { for(uint8 i = 0; i < std::size(channel.noteOnMap); i++) { // Some VSTis need a note off for each instance of a note on, e.g. fabfilter. while(channel.noteOnMap[i][trackChannel]) { MidiSend(MIDIEvents::NoteOff(midiCh, i, volume)); channel.noteOnMap[i][trackChannel]--; } } } // Note On else if(note >= NOTE_MIN && note < NOTE_MIN + mpt::array_size::size) { note -= NOTE_MIN; // Reset pitch bend on each new note, tracker style. // This is done if the pitch wheel has been moved or there was a vibrato on the previous row (in which case the "vstVibratoFlag" bit of the pitch bend memory is set) auto newPitchBendPos = EncodePitchBendParam(Clamp(m_SndFile.m_PlayState.Chn[trackChannel].GetMIDIPitchBend(), MIDIEvents::pitchBendMin, MIDIEvents::pitchBendMax)); if(m_MidiCh[midiCh].midiPitchBendPos != newPitchBendPos) { SendMidiPitchBend(midiCh, newPitchBendPos); } // count instances of active notes. // This is to send a note off for each instance of a note, for plugs like Fabfilter. // Problem: if a note dies out naturally and we never send a note off, this counter // will block at max until note off. Is this a problem? // Safe to assume we won't need more than 255 note offs max on a given note? if(channel.noteOnMap[note][trackChannel] < uint8_max) { channel.noteOnMap[note][trackChannel]++; } MidiSend(MIDIEvents::NoteOn(midiCh, static_cast(note), volume)); } } bool IMidiPlugin::IsNotePlaying(uint8 note, CHANNELINDEX trackerChn) { if(!ModCommand::IsNote(note) || trackerChn >= std::size(m_MidiCh[GetMidiChannel(trackerChn)].noteOnMap[note])) return false; note -= NOTE_MIN; return (m_MidiCh[GetMidiChannel(trackerChn)].noteOnMap[note][trackerChn] != 0); } void IMidiPlugin::ReceiveMidi(uint32 midiCode) { ResetSilence(); // I think we should only route events to plugins that are explicitely specified as output plugins of the current plugin. // This should probably use GetOutputPlugList here if we ever get to support multiple output plugins. PLUGINDEX receiver; if(m_pMixStruct != nullptr && (receiver = m_pMixStruct->GetOutputPlugin()) != PLUGINDEX_INVALID) { IMixPlugin *plugin = m_SndFile.m_MixPlugins[receiver].pMixPlugin; // Add all events to the plugin's queue. plugin->MidiSend(midiCode); } #ifdef MODPLUG_TRACKER if(m_recordMIDIOut) { // Spam MIDI data to all views ::PostMessage(CMainFrame::GetMainFrame()->GetMidiRecordWnd(), WM_MOD_MIDIMSG, midiCode, reinterpret_cast(this)); } #endif // MODPLUG_TRACKER } void IMidiPlugin::ReceiveSysex(mpt::const_byte_span sysex) { ResetSilence(); // I think we should only route events to plugins that are explicitely specified as output plugins of the current plugin. // This should probably use GetOutputPlugList here if we ever get to support multiple output plugins. PLUGINDEX receiver; if(m_pMixStruct != nullptr && (receiver = m_pMixStruct->GetOutputPlugin()) != PLUGINDEX_INVALID) { IMixPlugin *plugin = m_SndFile.m_MixPlugins[receiver].pMixPlugin; // Add all events to the plugin's queue. plugin->MidiSysexSend(sysex); } } // SNDMIXPLUGIN functions void SNDMIXPLUGIN::SetGain(uint8 gain) { Info.gain = gain; if(pMixPlugin != nullptr) pMixPlugin->RecalculateGain(); } void SNDMIXPLUGIN::SetBypass(bool bypass) { if(pMixPlugin != nullptr) pMixPlugin->Bypass(bypass); else Info.SetBypass(bypass); } void SNDMIXPLUGIN::Destroy() { if(pMixPlugin) { pMixPlugin->Release(); pMixPlugin = nullptr; } pluginData.clear(); pluginData.shrink_to_fit(); } OPENMPT_NAMESPACE_END #endif // NO_PLUGINS libopenmpt-0.6.1+release.autotools/soundlib/plugins/PlugInterface.h0000644000175000017500000002562414175042045022470 00000000000000/* * PlugInterface.h * --------------- * Purpose: Interface class for plugin handling * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #ifndef NO_PLUGINS #include "../../soundlib/Snd_defs.h" #include "../../soundlib/MIDIEvents.h" #include "../../soundlib/Mixer.h" #include "PluginMixBuffer.h" #include "PluginStructs.h" OPENMPT_NAMESPACE_BEGIN struct VSTPluginLib; struct SNDMIXPLUGIN; struct ModInstrument; struct ModChannel; class CSoundFile; class CModDoc; class CAbstractVstEditor; struct SNDMIXPLUGINSTATE { // dwFlags flags enum PluginStateFlags { psfMixReady = 0x01, // Set when cleared psfHasInput = 0x02, // Set when plugin has non-silent input psfSilenceBypass = 0x04, // Bypass because of silence detection }; mixsample_t *pMixBuffer = nullptr; // Stereo effect send buffer uint32 dwFlags = 0; // PluginStateFlags uint32 inputSilenceCount = 0; // How much silence has been processed? (for plugin auto-turnoff) mixsample_t nVolDecayL = 0, nVolDecayR = 0; // End of sample click removal void ResetSilence() { dwFlags |= psfHasInput; dwFlags &= ~psfSilenceBypass; inputSilenceCount = 0; } }; class IMixPlugin { friend class CAbstractVstEditor; protected: IMixPlugin *m_pNext = nullptr, *m_pPrev = nullptr; VSTPluginLib &m_Factory; CSoundFile &m_SndFile; SNDMIXPLUGIN *m_pMixStruct; #ifdef MODPLUG_TRACKER CAbstractVstEditor *m_pEditor = nullptr; #endif // MODPLUG_TRACKER public: SNDMIXPLUGINSTATE m_MixState; PluginMixBuffer m_mixBuffer; // Float buffers (input and output) for plugins protected: mixsample_t m_MixBuffer[MIXBUFFERSIZE * 2 + 2]; // Stereo interleaved input (sample mixer renders here) float m_fGain = 1.0f; PLUGINDEX m_nSlot = 0; bool m_isSongPlaying = false; bool m_isResumed = false; public: bool m_recordAutomation = false; bool m_passKeypressesToPlug = false; bool m_recordMIDIOut = false; protected: virtual ~IMixPlugin(); // Insert plugin into list of loaded plugins. void InsertIntoFactoryList(); public: // Non-virtual part of the interface IMixPlugin(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct); inline CSoundFile &GetSoundFile() { return m_SndFile; } inline const CSoundFile &GetSoundFile() const { return m_SndFile; } #ifdef MODPLUG_TRACKER CModDoc *GetModDoc(); const CModDoc *GetModDoc() const; void SetSlot(PLUGINDEX slot); inline PLUGINDEX GetSlot() const { return m_nSlot; } #endif // MODPLUG_TRACKER inline VSTPluginLib &GetPluginFactory() const { return m_Factory; } // Returns the next instance of the same plugin inline IMixPlugin *GetNextInstance() const { return m_pNext; } void SetDryRatio(uint32 param); bool IsBypassed() const; void RecalculateGain(); // Query output latency from host (in seconds) double GetOutputLatency() const; // Destroy the plugin virtual void Release() = 0; virtual int32 GetUID() const = 0; virtual int32 GetVersion() const = 0; virtual void Idle() = 0; // Plugin latency in samples virtual uint32 GetLatency() const = 0; virtual int32 GetNumPrograms() const = 0; virtual int32 GetCurrentProgram() = 0; virtual void SetCurrentProgram(int32 nIndex) = 0; virtual PlugParamIndex GetNumParameters() const = 0; virtual void SetParameter(PlugParamIndex paramindex, PlugParamValue paramvalue) = 0; virtual PlugParamValue GetParameter(PlugParamIndex nIndex) = 0; // Save parameters for storing them in a module file virtual void SaveAllParameters(); // Restore parameters from module file virtual void RestoreAllParameters(int32 program); virtual void Process(float *pOutL, float *pOutR, uint32 numFrames) = 0; void ProcessMixOps(float *pOutL, float *pOutR, float *leftPlugOutput, float *rightPlugOutput, uint32 numFrames); // Render silence and return the highest resulting output level virtual float RenderSilence(uint32 numSamples); // MIDI event handling virtual bool MidiSend(uint32 /*midiCode*/) { return true; } virtual bool MidiSysexSend(mpt::const_byte_span /*sysex*/) { return true; } virtual void MidiCC(MIDIEvents::MidiCC /*nController*/, uint8 /*nParam*/, CHANNELINDEX /*trackChannel*/) { } virtual void MidiPitchBendRaw(int32 /*pitchbend*/, CHANNELINDEX /*trackChannel*/) {} virtual void MidiPitchBend(int32 /*increment*/, int8 /*pwd*/, CHANNELINDEX /*trackChannel*/) { } virtual void MidiVibrato(int32 /*depth*/, int8 /*pwd*/, CHANNELINDEX /*trackerChn*/) { } virtual void MidiCommand(const ModInstrument &/*instr*/, uint16 /*note*/, uint16 /*vol*/, CHANNELINDEX /*trackChannel*/) { } virtual void HardAllNotesOff() { } virtual bool IsNotePlaying(uint8 /*note*/, CHANNELINDEX /*trackerChn*/) { return false; } // Modify parameter by given amount. Only needs to be re-implemented if plugin architecture allows this to be performed atomically. virtual void ModifyParameter(PlugParamIndex nIndex, PlugParamValue diff); virtual void NotifySongPlaying(bool playing) { m_isSongPlaying = playing; } virtual bool IsSongPlaying() const { return m_isSongPlaying; } virtual bool IsResumed() const { return m_isResumed; } virtual void Resume() = 0; virtual void Suspend() = 0; // Tell the plugin that there is a discontinuity between the previous and next render call (e.g. aftert jumping around in the module) virtual void PositionChanged() = 0; virtual void Bypass(bool = true); bool ToggleBypass() { Bypass(!IsBypassed()); return IsBypassed(); } virtual bool IsInstrument() const = 0; virtual bool CanRecieveMidiEvents() = 0; // If false is returned, mixing this plugin can be skipped if its input are currently completely silent. virtual bool ShouldProcessSilence() = 0; virtual void ResetSilence() { m_MixState.ResetSilence(); } size_t GetOutputPlugList(std::vector &list); size_t GetInputPlugList(std::vector &list); size_t GetInputInstrumentList(std::vector &list); size_t GetInputChannelList(std::vector &list); #ifdef MODPLUG_TRACKER bool SaveProgram(); bool LoadProgram(mpt::PathString fileName = mpt::PathString()); virtual CString GetDefaultEffectName() = 0; // Cache a range of names, in case one-by-one retrieval would be slow (e.g. when using plugin bridge) virtual void CacheProgramNames(int32 /*firstProg*/, int32 /*lastProg*/) { } virtual void CacheParameterNames(int32 /*firstParam*/, int32 /*lastParam*/) { } // Allowed value range for a parameter virtual std::pair GetParamUIRange(PlugParamIndex /*param*/) { return {0.0f, 1.0f}; } // Scale allowed value range of a parameter to/from [0,1] PlugParamValue GetScaledUIParam(PlugParamIndex param); void SetScaledUIParam(PlugParamIndex param, PlugParamValue value); virtual CString GetParamName(PlugParamIndex param) = 0; virtual CString GetParamLabel(PlugParamIndex param) = 0; virtual CString GetParamDisplay(PlugParamIndex param) = 0; CString GetFormattedParamName(PlugParamIndex param); CString GetFormattedParamValue(PlugParamIndex param); virtual CString GetCurrentProgramName() = 0; virtual void SetCurrentProgramName(const CString &name) = 0; virtual CString GetProgramName(int32 program) = 0; CString GetFormattedProgramName(int32 index); virtual bool HasEditor() const = 0; protected: virtual CAbstractVstEditor *OpenEditor(); public: // Get the plugin's editor window CAbstractVstEditor *GetEditor() { return m_pEditor; } const CAbstractVstEditor *GetEditor() const { return m_pEditor; } void ToggleEditor(); void CloseEditor(); void SetEditorPos(int32 x, int32 y); void GetEditorPos(int32 &x, int32 &y) const; // Notify OpenMPT that a plugin parameter has changed and set document as modified void AutomateParameter(PlugParamIndex param); // Plugin state changed, set document as modified. void SetModified(); #endif virtual int GetNumInputChannels() const = 0; virtual int GetNumOutputChannels() const = 0; using ChunkData = mpt::const_byte_span; virtual bool ProgramsAreChunks() const { return false; } virtual ChunkData GetChunk(bool /*isBank*/) { return ChunkData(); } virtual void SetChunk(const ChunkData &/*chunk*/, bool /*isBank*/) { } virtual void BeginSetProgram(int32 /*program*/ = -1) {} virtual void EndSetProgram() {} virtual void BeginGetProgram(int32 /*program*/ = -1) {} virtual void EndGetProgram() {} }; inline void IMixPlugin::ModifyParameter(PlugParamIndex nIndex, PlugParamValue diff) { PlugParamValue val = GetParameter(nIndex) + diff; Limit(val, PlugParamValue(0), PlugParamValue(1)); SetParameter(nIndex, val); } // IMidiPlugin: Default implementation of plugins with MIDI input class IMidiPlugin : public IMixPlugin { protected: enum { // Pitch wheel constants kPitchBendShift = 12, // Use lowest 12 bits for fractional part and vibrato flag => 16.11 fixed point precision kPitchBendMask = (~1), kVibratoFlag = 1, }; struct PlugInstrChannel { int32 midiPitchBendPos = 0; // Current Pitch Wheel position, in 16.11 fixed point format. Lowest bit is used for indicating that vibrato was applied. Vibrato offset itself is not stored in this value. uint16 currentProgram = uint16_max; uint16 currentBank = uint16_max; uint8 noteOnMap[128][MAX_CHANNELS]; void ResetProgram() { currentProgram = uint16_max; currentBank = uint16_max; } }; std::array m_MidiCh; // MIDI channel state public: IMidiPlugin(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct); void MidiCC(MIDIEvents::MidiCC nController, uint8 nParam, CHANNELINDEX trackChannel) override; void MidiPitchBendRaw(int32 pitchbend, CHANNELINDEX trackerChn) override; void MidiPitchBend(int32 increment, int8 pwd, CHANNELINDEX trackerChn) override; void MidiVibrato(int32 depth, int8 pwd, CHANNELINDEX trackerChn) override; void MidiCommand(const ModInstrument &instr, uint16 note, uint16 vol, CHANNELINDEX trackChannel) override; bool IsNotePlaying(uint8 note, CHANNELINDEX trackerChn) override; // Get the MIDI channel currently associated with a given tracker channel virtual uint8 GetMidiChannel(const ModChannel &chn, CHANNELINDEX trackChannel) const; protected: uint8 GetMidiChannel(CHANNELINDEX trackChannel) const; // Plugin wants to send MIDI to OpenMPT virtual void ReceiveMidi(uint32 midiCode); virtual void ReceiveSysex(mpt::const_byte_span sysex); // Converts a 14-bit MIDI pitch bend position to our internal pitch bend position representation static constexpr int32 EncodePitchBendParam(int32 position) { return (position << kPitchBendShift); } // Converts the internal pitch bend position to a 14-bit MIDI pitch bend position static constexpr int16 DecodePitchBendParam(int32 position) { return static_cast(position >> kPitchBendShift); } // Apply Pitch Wheel Depth (PWD) to some MIDI pitch bend value. static inline void ApplyPitchWheelDepth(int32 &value, int8 pwd); void SendMidiPitchBend(uint8 midiCh, int32 newPitchBendPos); }; OPENMPT_NAMESPACE_END #endif // NO_PLUGINS libopenmpt-0.6.1+release.autotools/soundlib/plugins/SymMODEcho.cpp0000644000175000017500000001463314155206341022177 00000000000000/* * SymMODEcho.cpp * -------------- * Purpose: Implementation of the SymMOD Echo DSP * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #ifndef NO_PLUGINS #include "../Sndfile.h" #include "SymMODEcho.h" OPENMPT_NAMESPACE_BEGIN IMixPlugin *SymMODEcho::Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct) { return new (std::nothrow) SymMODEcho(factory, sndFile, mixStruct); } SymMODEcho::SymMODEcho(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct) : IMixPlugin(factory, sndFile, mixStruct) , m_chunk(PluginChunk::Default()) { m_mixBuffer.Initialize(2, 2); InsertIntoFactoryList(); RecalculateEchoParams(); } void SymMODEcho::Process(float* pOutL, float* pOutR, uint32 numFrames) { const float *srcL = m_mixBuffer.GetInputBuffer(0), *srcR = m_mixBuffer.GetInputBuffer(1); float *outL = m_mixBuffer.GetOutputBuffer(0), *outR = m_mixBuffer.GetOutputBuffer(1); const uint32 delayTime = m_SndFile.m_PlayState.m_nSamplesPerTick * m_chunk.param[kEchoDelay]; // SymMODs don't have a variable tempo so the tick duration should never change... but if someone loads a module into an MPTM file we have to account for this. if(m_delayLine.size() < delayTime * 2) m_delayLine.resize(delayTime * 2); const auto dspType = GetDSPType(); if(dspType == DSPType::Off) { // Toggling the echo while it's running keeps its delay line untouched std::copy(srcL, srcL + numFrames, outL); std::copy(srcR, srcR + numFrames, outR); } else { for(uint32 i = 0; i < numFrames; i++) { if(m_writePos >= delayTime) m_writePos = 0; int readPos = m_writePos - delayTime; if(readPos < 0) readPos += delayTime; const float lDry = *srcL++, rDry = *srcR++; const float lDelay = m_delayLine[readPos * 2], rDelay = m_delayLine[readPos * 2 + 1]; // Output samples *outL++ = (lDry + lDelay); *outR++ = (rDry + rDelay); // Compute new delay line values float lOut = 0.0f, rOut = 0.0f; switch(dspType) { case DSPType::Off: break; case DSPType::Normal: // Normal lOut = (lDelay + lDry) * m_feedback; rOut = (rDelay + rDry) * m_feedback; break; case DSPType::Cross: case DSPType::Cross2: lOut = (rDelay + rDry) * m_feedback; rOut = (lDelay + lDry) * m_feedback; break; case DSPType::Center: lOut = (lDelay + (lDry + rDry) * 0.5f) * m_feedback; rOut = lOut; break; case DSPType::NumTypes: break; } // Prevent denormals if(std::abs(lOut) < 1e-24f) lOut = 0.0f; if(std::abs(rOut) < 1e-24f) rOut = 0.0f; m_delayLine[m_writePos * 2 + 0] = lOut; m_delayLine[m_writePos * 2 + 1] = rOut; m_writePos++; } } ProcessMixOps(pOutL, pOutR, m_mixBuffer.GetOutputBuffer(0), m_mixBuffer.GetOutputBuffer(1), numFrames); } void SymMODEcho::SaveAllParameters() { m_pMixStruct->defaultProgram = -1; try { const auto pluginData = mpt::as_raw_memory(m_chunk); m_pMixStruct->pluginData.assign(pluginData.begin(), pluginData.end()); } catch(mpt::out_of_memory e) { mpt::delete_out_of_memory(e); m_pMixStruct->pluginData.clear(); } } void SymMODEcho::RestoreAllParameters(int32 program) { if(m_pMixStruct->pluginData.size() == sizeof(m_chunk) && !memcmp(m_pMixStruct->pluginData.data(), "Echo", 4)) { std::copy(m_pMixStruct->pluginData.begin(), m_pMixStruct->pluginData.end(), mpt::as_raw_memory(m_chunk).begin()); } else { IMixPlugin::RestoreAllParameters(program); } RecalculateEchoParams(); } PlugParamValue SymMODEcho::GetParameter(PlugParamIndex index) { if(index < kEchoNumParameters) { return m_chunk.param[index] / 127.0f; } return 0; } void SymMODEcho::SetParameter(PlugParamIndex index, PlugParamValue value) { if(index < kEchoNumParameters) { m_chunk.param[index] = mpt::saturate_round(mpt::safe_clamp(value, 0.0f, 1.0f) * 127.0f); RecalculateEchoParams(); } } void SymMODEcho::Resume() { m_isResumed = true; PositionChanged(); } void SymMODEcho::PositionChanged() { try { m_delayLine.assign(127 * 2 * m_SndFile.m_PlayState.m_nSamplesPerTick, 0.0f); } catch(mpt::out_of_memory e) { mpt::delete_out_of_memory(e); } m_writePos = 0; } #ifdef MODPLUG_TRACKER std::pair SymMODEcho::GetParamUIRange(PlugParamIndex param) { if(param == kEchoType) return {0.0f, (static_cast(DSPType::NumTypes) - 1) / 127.0f}; else return {0.0f, 1.0f}; } CString SymMODEcho::GetParamName(PlugParamIndex param) { switch (param) { case kEchoType: return _T("Type"); case kEchoDelay: return _T("Delay"); case kEchoFeedback: return _T("Feedback"); case kEchoNumParameters: break; } return {}; } CString SymMODEcho::GetParamLabel(PlugParamIndex param) { if(param == kEchoDelay) return _T("Ticks"); if(param == kEchoFeedback) return _T("%"); return {}; } CString SymMODEcho::GetParamDisplay(PlugParamIndex param) { switch(static_cast(param)) { case kEchoType: switch(GetDSPType()) { case DSPType::Off: return _T("Off"); case DSPType::Normal: return _T("Normal"); case DSPType::Cross: return _T("Cross"); case DSPType::Cross2: return _T("Cross 2"); case DSPType::Center: return _T("Center"); case DSPType::NumTypes: break; } break; case kEchoDelay: return mpt::cfmt::val(m_chunk.param[kEchoDelay]); case kEchoFeedback: return mpt::cfmt::flt(m_feedback * 100.0f, 4); case kEchoNumParameters: break; } return {}; } #endif // MODPLUG_TRACKER IMixPlugin::ChunkData SymMODEcho::GetChunk(bool) { auto data = reinterpret_cast(&m_chunk); return ChunkData(data, sizeof(m_chunk)); } void SymMODEcho::SetChunk(const ChunkData& chunk, bool) { auto data = chunk.data(); if(chunk.size() == sizeof(chunk) && !memcmp(data, "Echo", 4)) { memcpy(&m_chunk, data, chunk.size()); RecalculateEchoParams(); } } void SymMODEcho::RecalculateEchoParams() { if(m_chunk.param[kEchoType] >= static_cast(DSPType::NumTypes)) m_chunk.param[kEchoType] = 0; if(m_chunk.param[kEchoDelay] > 127) m_chunk.param[kEchoDelay] = 127; if(m_chunk.param[kEchoFeedback] > 127) m_chunk.param[kEchoFeedback] = 127; if(GetDSPType() == DSPType::Cross2) m_feedback = 1.0f - std::pow(2.0f, -static_cast(m_chunk.param[kEchoFeedback] + 1)); else m_feedback = std::pow(2.0f, -static_cast(m_chunk.param[kEchoFeedback])); } OPENMPT_NAMESPACE_END #endif // NO_PLUGINS libopenmpt-0.6.1+release.autotools/soundlib/plugins/SymMODEcho.h0000644000175000017500000000705414025403521021636 00000000000000/* * SymMODEcho.h * ------------ * Purpose: Implementation of the SymMOD Echo DSP * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #ifndef NO_PLUGINS #include "PlugInterface.h" OPENMPT_NAMESPACE_BEGIN class SymMODEcho final : public IMixPlugin { public: enum class DSPType : uint8 { Off = 0, Normal, Cross, Cross2, Center, NumTypes }; enum Parameters { kEchoType = 0, kEchoDelay, kEchoFeedback, kEchoNumParameters }; // Our settings chunk for file I/O, as it will be written to files struct PluginChunk { char id[4]; uint8 param[kEchoNumParameters]; static PluginChunk Create(uint8 type, uint8 delay, uint8 feedback) { static_assert(sizeof(PluginChunk) == 7); PluginChunk result; memcpy(result.id, "Echo", 4); result.param[kEchoType] = type; result.param[kEchoDelay] = delay; result.param[kEchoFeedback] = feedback; return result; } static PluginChunk Default() { return Create(0, 4, 1); } }; std::vector m_delayLine; uint32 m_writePos = 0; // Current write position in the delay line float m_feedback = 0.5f; // Settings chunk for file I/O PluginChunk m_chunk; public: static IMixPlugin* Create(VSTPluginLib& factory, CSoundFile& sndFile, SNDMIXPLUGIN* mixStruct); SymMODEcho(VSTPluginLib& factory, CSoundFile& sndFile, SNDMIXPLUGIN* mixStruct); void Release() override { delete this; } void SaveAllParameters() override; void RestoreAllParameters(int32 program) override; int32 GetUID() const override { int32le id; memcpy(&id, "Echo", 4); return id; } int32 GetVersion() const override { return 0; } void Idle() override { } uint32 GetLatency() const override { return 0; } void Process(float* pOutL, float* pOutR, uint32 numFrames) override; float RenderSilence(uint32) override { return 0.0f; } int32 GetNumPrograms() const override { return 0; } int32 GetCurrentProgram() override { return 0; } void SetCurrentProgram(int32) override { } PlugParamIndex GetNumParameters() const override { return kEchoNumParameters; } PlugParamValue GetParameter(PlugParamIndex index) override; void SetParameter(PlugParamIndex index, PlugParamValue value) override; void Resume() override; void Suspend() override { m_isResumed = false; } void PositionChanged() override; bool IsInstrument() const override { return false; } bool CanRecieveMidiEvents() override { return false; } bool ShouldProcessSilence() override { return true; } #ifdef MODPLUG_TRACKER CString GetDefaultEffectName() override { return _T("Echo"); } std::pair GetParamUIRange(PlugParamIndex param) override; CString GetParamName(PlugParamIndex param) override; CString GetParamLabel(PlugParamIndex) override; CString GetParamDisplay(PlugParamIndex param) override; CString GetCurrentProgramName() override { return CString(); } void SetCurrentProgramName(const CString&) override { } CString GetProgramName(int32) override { return CString(); } bool HasEditor() const override { return false; } #endif int GetNumInputChannels() const override { return 2; } int GetNumOutputChannels() const override { return 2; } bool ProgramsAreChunks() const override { return true; } ChunkData GetChunk(bool) override; void SetChunk(const ChunkData& chunk, bool) override; protected: DSPType GetDSPType() const { return static_cast(m_chunk.param[kEchoType]); } void RecalculateEchoParams(); }; MPT_BINARY_STRUCT(SymMODEcho::PluginChunk, 7) OPENMPT_NAMESPACE_END #endif // NO_PLUGINS libopenmpt-0.6.1+release.autotools/soundlib/AudioCriticalSection.cpp0000644000175000017500000000307214047666254022660 00000000000000/* * AudioCriticalSection.cpp * ----------- * Purpose: Implementation of OpenMPT's critical section for access to CSoundFile. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "AudioCriticalSection.h" #if defined(MODPLUG_TRACKER) #include "../misc/mptMutex.h" #endif OPENMPT_NAMESPACE_BEGIN #if defined(MODPLUG_TRACKER) #if MPT_COMPILER_MSVC _Acquires_lock_(m_refGlobalMutex.mutex) #endif // MPT_COMPILER_MSVC CriticalSection::CriticalSection() : m_refGlobalMutex(Tracker::GetGlobalMutexRef()) , inSection(false) { Enter(); } CriticalSection::CriticalSection(CriticalSection &&other) noexcept : m_refGlobalMutex(other.m_refGlobalMutex) , inSection(other.inSection) { other.inSection = false; } CriticalSection::CriticalSection(InitialState state) : m_refGlobalMutex(Tracker::GetGlobalMutexRef()) , inSection(false) { if(state == InitialState::Locked) { Enter(); } } #if MPT_COMPILER_MSVC _Acquires_lock_(m_refGlobalMutex.mutex) #endif // MPT_COMPILER_MSVC void CriticalSection::Enter() { if(!inSection) { inSection = true; m_refGlobalMutex.lock(); } } #if MPT_COMPILER_MSVC _Requires_lock_held_(m_refGlobalMutex.mutex) _Releases_lock_(m_refGlobalMutex.mutex) #endif // MPT_COMPILER_MSVC void CriticalSection::Leave() { if(inSection) { inSection = false; m_refGlobalMutex.unlock(); } } CriticalSection::~CriticalSection() { Leave(); } #else MPT_MSVC_WORKAROUND_LNK4221(AudioCriticalSection) #endif OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/AudioCriticalSection.h0000644000175000017500000000401114052666041022305 00000000000000/* * AudioCriticalSection.h * --------- * Purpose: Implementation of OpenMPT's critical section for access to CSoundFile. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #if defined(MODPLUG_TRACKER) #include "../misc/mptMutex.h" #endif OPENMPT_NAMESPACE_BEGIN #if defined(MODPLUG_TRACKER) namespace mpt { class recursive_mutex_with_lock_count; } // namespace mpt namespace Tracker { // implemented in mptrack/Mptrack.cpp mpt::recursive_mutex_with_lock_count & GetGlobalMutexRef(); } // namespace Tracker // Critical section handling done in (safe) RAII style. // Create a CriticalSection object whenever you need exclusive access to CSoundFile. // One object = one lock / critical section. // The critical section is automatically left when the object is destroyed, but // Enter() and Leave() can also be called manually if needed. class CriticalSection { private: mpt::recursive_mutex_with_lock_count & m_refGlobalMutex; protected: bool inSection; public: enum class InitialState { Locked = 0, Unlocked = 1, }; public: #if MPT_COMPILER_MSVC _Acquires_lock_(m_refGlobalMutex.mutex) #endif // MPT_COMPILER_MSVC CriticalSection(); CriticalSection(CriticalSection &&other) noexcept; explicit CriticalSection(InitialState state); #if MPT_COMPILER_MSVC _Acquires_lock_(m_refGlobalMutex.mutex) #endif // MPT_COMPILER_MSVC void Enter(); #if MPT_COMPILER_MSVC _Requires_lock_held_(m_refGlobalMutex.mutex) _Releases_lock_(m_refGlobalMutex.mutex) #endif // MPT_COMPILER_MSVC void Leave(); ~CriticalSection(); }; #else // !MODPLUG_TRACKER class CriticalSection { public: enum class InitialState { Locked = 0, Unlocked = 1, }; public: CriticalSection() {} CriticalSection(CriticalSection &&) noexcept {} explicit CriticalSection(InitialState) {} void Enter() {} void Leave() {} ~CriticalSection() {} }; #endif // MODPLUG_TRACKER OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/AudioReadTarget.h0000644000175000017500000000740214053107667021264 00000000000000/* * AudioReadTarget.h * ----------------- * Purpose: Callback class implementations for audio data read via CSoundFile::Read. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "Sndfile.h" #include "mpt/audio/span.hpp" #include "openmpt/soundbase/SampleFormat.hpp" #include "openmpt/soundbase/CopyMix.hpp" #include "openmpt/soundbase/Dither.hpp" #include "MixerLoops.h" #include "Mixer.h" #include "../common/Dither.h" #include OPENMPT_NAMESPACE_BEGIN template class AudioTargetBuffer : public IAudioTarget { private: std::size_t countRendered; TDithers &dithers; protected: Taudio_span outputBuffer; public: AudioTargetBuffer(Taudio_span buf, TDithers &dithers_) : countRendered(0) , dithers(dithers_) , outputBuffer(buf) { return; } std::size_t GetRenderedCount() const { return countRendered; } public: void Process(mpt::audio_span_interleaved buffer) override { std::visit( [&](auto &ditherInstance) { ConvertBufferMixInternalFixedToBuffer(mpt::make_audio_span_with_offset(outputBuffer, countRendered), buffer, ditherInstance, buffer.size_channels(), buffer.size_frames()); }, dithers.Variant() ); countRendered += buffer.size_frames(); } void Process(mpt::audio_span_interleaved buffer) override { std::visit( [&](auto &ditherInstance) { ConvertBufferMixInternalToBuffer(mpt::make_audio_span_with_offset(outputBuffer, countRendered), buffer, ditherInstance, buffer.size_channels(), buffer.size_frames()); }, dithers.Variant() ); countRendered += buffer.size_frames(); } }; template class AudioTargetBufferWithGain : public AudioTargetBuffer { private: using Tbase = AudioTargetBuffer; private: const float gainFactor; public: AudioTargetBufferWithGain(Taudio_span buf, TDithers &dithers, float gainFactor_) : Tbase(buf, dithers) , gainFactor(gainFactor_) { return; } public: void Process(mpt::audio_span_interleaved buffer) override { const std::size_t countRendered_ = Tbase::GetRenderedCount(); if constexpr(!std::is_floating_point::value) { int32 gainFactor16_16 = mpt::saturate_round(gainFactor * (1 << 16)); if(gainFactor16_16 != (1<<16)) { // only apply gain when != +/- 0dB // no clipping prevention is done here for(std::size_t frame = 0; frame < buffer.size_frames(); ++frame) { for(std::size_t channel = 0; channel < buffer.size_channels(); ++channel) { buffer(channel, frame) = Util::muldiv(buffer(channel, frame), gainFactor16_16, 1 << 16); } } } } Tbase::Process(buffer); if constexpr(std::is_floating_point::value) { if(gainFactor != 1.0f) { // only apply gain when != +/- 0dB for(std::size_t frame = 0; frame < buffer.size_frames(); ++frame) { for(std::size_t channel = 0; channel < buffer.size_channels(); ++channel) { Tbase::outputBuffer(channel, countRendered_ + frame) *= gainFactor; } } } } } void Process(mpt::audio_span_interleaved buffer) override { if(gainFactor != 1.0f) { // only apply gain when != +/- 0dB for(std::size_t frame = 0; frame < buffer.size_frames(); ++frame) { for(std::size_t channel = 0; channel < buffer.size_channels(); ++channel) { buffer(channel, frame) *= gainFactor; } } } Tbase::Process(buffer); } }; OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/BitReader.h0000644000175000017500000000350714056354456020126 00000000000000/* * BitReader.h * ----------- * Purpose: An extended FileReader to read bit-oriented rather than byte-oriented streams. * Notes : The current implementation can only read bit widths up to 32 bits, and it always * reads bits starting from the least significant bit, as this is all that is * required by the class users at the moment. * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "../common/FileReader.h" #include #include "mpt/io/base.hpp" OPENMPT_NAMESPACE_BEGIN class BitReader : private FileReader { protected: off_t m_bufPos = 0, m_bufSize = 0; uint32 bitBuf = 0; // Current bit buffer int m_bitNum = 0; // Currently available number of bits std::byte buffer[mpt::IO::BUFFERSIZE_TINY]{}; public: class eof : public std::range_error { public: eof() : std::range_error("Truncated bit buffer") { } }; BitReader() : FileReader() { } BitReader(mpt::span bytedata) : FileReader(bytedata) { } BitReader(const FileCursor &other) : FileReader(other) { } BitReader(FileCursor &&other) : FileReader(std::move(other)) { } off_t GetLength() const { return FileReader::GetLength(); } off_t GetPosition() const { return FileReader::GetPosition() - m_bufSize + m_bufPos; } uint32 ReadBits(int numBits) { while(m_bitNum < numBits) { // Fetch more bits if(m_bufPos >= m_bufSize) { m_bufSize = ReadRaw(mpt::as_span(buffer)).size(); m_bufPos = 0; if(!m_bufSize) { throw eof(); } } bitBuf |= (static_cast(buffer[m_bufPos++]) << m_bitNum); m_bitNum += 8; } uint32 v = bitBuf & ((1 << numBits) - 1); bitBuf >>= numBits; m_bitNum -= numBits; return v; } }; OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/ContainerMMCMP.cpp0000644000175000017500000002353414105571131021320 00000000000000/* * ContainerMMCMP.cpp * ------------------ * Purpose: Handling of MMCMP compressed modules * Notes : (currently none) * Authors: Olivier Lapicque * OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "../common/FileReader.h" #include "Container.h" #include "Sndfile.h" #include "BitReader.h" OPENMPT_NAMESPACE_BEGIN #if !defined(MPT_WITH_ANCIENT) #ifdef MPT_ALL_LOGGING #define MMCMP_LOG #endif struct MMCMPFileHeader { char id[8]; // "ziRCONia" uint16le hdrsize; // size of all the remaining header data uint16le version; uint16le nblocks; uint32le filesize; uint32le blktable; uint8le glb_comp; uint8le fmt_comp; bool Validate() const { if(std::memcmp(id, "ziRCONia", 8) != 0) return false; if(hdrsize != 14) return false; if(nblocks == 0) return false; if(filesize == 0) return false; if(filesize >= 0x80000000) return false; if(blktable < sizeof(MMCMPFileHeader)) return false; return true; } }; MPT_BINARY_STRUCT(MMCMPFileHeader, 24) struct MMCMPBlock { uint32le unpk_size; uint32le pk_size; uint32le xor_chk; uint16le sub_blk; uint16le flags; uint16le tt_entries; uint16le num_bits; }; MPT_BINARY_STRUCT(MMCMPBlock, 20) struct MMCMPSubBlock { uint32le position; uint32le size; bool Validate(std::vector &unpackedData, const uint32 unpackedSize) const { if(position >= unpackedSize) return false; if(size > unpackedSize) return false; if(size > unpackedSize - position) return false; if(size == 0) return false; if(unpackedData.size() < position + size) unpackedData.resize(position + size); return true; } }; MPT_BINARY_STRUCT(MMCMPSubBlock, 8) enum MMCMPFlags : uint16 { MMCMP_COMP = 0x0001, MMCMP_DELTA = 0x0002, MMCMP_16BIT = 0x0004, MMCMP_STEREO = 0x0100, MMCMP_ABS16 = 0x0200, MMCMP_ENDIAN = 0x0400, }; static constexpr uint8 MMCMP8BitCommands[8] = { 0x01, 0x03, 0x07, 0x0F, 0x1E, 0x3C, 0x78, 0xF8 }; static constexpr uint8 MMCMP8BitFetch[8] = { 3, 3, 3, 3, 2, 1, 0, 0 }; static constexpr uint16 MMCMP16BitCommands[16] = { 0x01, 0x03, 0x07, 0x0F, 0x1E, 0x3C, 0x78, 0xF0, 0x1F0, 0x3F0, 0x7F0, 0xFF0, 0x1FF0, 0x3FF0, 0x7FF0, 0xFFF0 }; static constexpr uint8 MMCMP16BitFetch[16] = { 4, 4, 4, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderMMCMP(MemoryFileReader file, const uint64 *pfilesize) { MMCMPFileHeader mfh; if(!file.ReadStruct(mfh)) return ProbeWantMoreData; if(!mfh.Validate()) return ProbeFailure; MPT_UNREFERENCED_PARAMETER(pfilesize); return ProbeSuccess; } bool UnpackMMCMP(std::vector &containerItems, FileReader &file, ContainerLoadingFlags loadFlags) { file.Rewind(); containerItems.clear(); MMCMPFileHeader mfh; if(!file.ReadStruct(mfh)) return false; if(!mfh.Validate()) return false; if(loadFlags == ContainerOnlyVerifyHeader) return true; if(!file.LengthIsAtLeast(mfh.blktable)) return false; if(!file.LengthIsAtLeast(mfh.blktable + 4 * mfh.nblocks)) return false; containerItems.emplace_back(); containerItems.back().data_cache = std::make_unique >(); auto &unpackedData = *(containerItems.back().data_cache); // Generally it's not so simple to establish an upper limit for the uncompressed data size (blocks can be reused, etc.), // so we just reserve a realistic amount of memory. const uint32 unpackedSize = mfh.filesize; unpackedData.reserve(std::min(unpackedSize, std::min(mpt::saturate_cast(file.GetLength()), uint32_max / 20u) * 20u)); // 8-bit deltas uint8 ptable[256] = { 0 }; std::vector subblks; for(uint32 nBlock = 0; nBlock < mfh.nblocks; nBlock++) { if(!file.Seek(mfh.blktable + 4 * nBlock)) return false; if(!file.CanRead(4)) return false; uint32 blkPos = file.ReadUint32LE(); if(!file.Seek(blkPos)) return false; MMCMPBlock blk; if(!file.ReadStruct(blk)) return false; if(!file.ReadVector(subblks, blk.sub_blk)) return false; const MMCMPSubBlock *psubblk = blk.sub_blk > 0 ? subblks.data() : nullptr; if(blkPos + sizeof(MMCMPBlock) + blk.sub_blk * sizeof(MMCMPSubBlock) >= file.GetLength()) return false; uint32 memPos = blkPos + sizeof(MMCMPBlock) + blk.sub_blk * sizeof(MMCMPSubBlock); #ifdef MMCMP_LOG MPT_LOG_GLOBAL(LogDebug, "MMCMP", MPT_UFORMAT("block {}: flags={} sub_blocks={}")(nBlock, mpt::ufmt::HEX0<4>(static_cast(blk.flags)), static_cast(blk.sub_blk))); MPT_LOG_GLOBAL(LogDebug, "MMCMP", MPT_UFORMAT(" pksize={} unpksize={}")(static_cast(blk.pk_size), static_cast(blk.unpk_size))); MPT_LOG_GLOBAL(LogDebug, "MMCMP", MPT_UFORMAT(" tt_entries={} num_bits={}")(static_cast(blk.tt_entries), static_cast(blk.num_bits))); #endif if(!(blk.flags & MMCMP_COMP)) { // Data is not packed for(uint32 i = 0; i < blk.sub_blk; i++) { if(!psubblk) return false; if(!psubblk->Validate(unpackedData, unpackedSize)) return false; #ifdef MMCMP_LOG MPT_LOG_GLOBAL(LogDebug, "MMCMP", MPT_UFORMAT(" Unpacked sub-block {}: offset {}, size={}")(i, static_cast(psubblk->position), static_cast(psubblk->size))); #endif if(!file.Seek(memPos)) return false; if(file.ReadRaw(mpt::span(&(unpackedData[psubblk->position]), psubblk->size)).size() != psubblk->size) return false; psubblk++; } } else if(blk.flags & MMCMP_16BIT) { // Data is 16-bit packed uint32 subblk = 0; if(!psubblk) return false; if(!psubblk[subblk].Validate(unpackedData, unpackedSize)) return false; char *pDest = &(unpackedData[psubblk[subblk].position]); uint32 dwSize = psubblk[subblk].size & ~1u; if(!dwSize) return false; uint32 dwPos = 0; uint32 numbits = blk.num_bits; uint32 oldval = 0; #ifdef MMCMP_LOG MPT_LOG_GLOBAL(LogDebug, "MMCMP", MPT_UFORMAT(" 16-bit block: pos={} size={} {} {}")(psubblk->position, psubblk->size, (blk.flags & MMCMP_DELTA) ? U_("DELTA ") : U_(""), (blk.flags & MMCMP_ABS16) ? U_("ABS16 ") : U_(""))); #endif if(!file.Seek(memPos + blk.tt_entries)) return false; if(!file.CanRead(blk.pk_size - blk.tt_entries)) return false; BitReader bitFile{ file.GetChunk(blk.pk_size - blk.tt_entries) }; try { while (subblk < blk.sub_blk) { uint32 newval = 0x10000; uint32 d = bitFile.ReadBits(numbits + 1); uint32 command = MMCMP16BitCommands[numbits & 0x0F]; if(d >= command) { uint32 nFetch = MMCMP16BitFetch[numbits & 0x0F]; uint32 newbits = bitFile.ReadBits(nFetch) + ((d - command) << nFetch); if(newbits != numbits) { numbits = newbits & 0x0F; } else if((d = bitFile.ReadBits(4)) == 0x0F) { if(bitFile.ReadBits(1)) break; newval = 0xFFFF; } else { newval = 0xFFF0 + d; } } else { newval = d; } if(newval < 0x10000) { newval = (newval & 1) ? (uint32)(-(int32)((newval + 1) >> 1)) : (uint32)(newval >> 1); if(blk.flags & MMCMP_DELTA) { newval += oldval; oldval = newval; } else if(!(blk.flags & MMCMP_ABS16)) { newval ^= 0x8000; } if(blk.flags & MMCMP_ENDIAN) { pDest[dwPos + 0] = static_cast(newval >> 8); pDest[dwPos + 1] = static_cast(newval & 0xFF); } else { pDest[dwPos + 0] = static_cast(newval & 0xFF); pDest[dwPos + 1] = static_cast(newval >> 8); } dwPos += 2; } if(dwPos >= dwSize) { subblk++; dwPos = 0; if(!(subblk < blk.sub_blk)) break; if(!psubblk[subblk].Validate(unpackedData, unpackedSize)) return false; dwSize = psubblk[subblk].size & ~1u; if(!dwSize) return false; pDest = &(unpackedData[psubblk[subblk].position]); } } } catch(const BitReader::eof &) { } } else { // Data is 8-bit packed uint32 subblk = 0; if(!psubblk) return false; if(!psubblk[subblk].Validate(unpackedData, unpackedSize)) return false; char *pDest = &(unpackedData[psubblk[subblk].position]); uint32 dwSize = psubblk[subblk].size; uint32 dwPos = 0; uint32 numbits = blk.num_bits; uint32 oldval = 0; if(blk.tt_entries > sizeof(ptable) || !file.Seek(memPos) || file.ReadRaw(mpt::span(ptable, blk.tt_entries)).size() < blk.tt_entries) return false; if(!file.CanRead(blk.pk_size - blk.tt_entries)) return false; BitReader bitFile{ file.GetChunk(blk.pk_size - blk.tt_entries) }; try { while (subblk < blk.sub_blk) { uint32 newval = 0x100; uint32 d = bitFile.ReadBits(numbits + 1); uint32 command = MMCMP8BitCommands[numbits & 0x07]; if(d >= command) { uint32 nFetch = MMCMP8BitFetch[numbits & 0x07]; uint32 newbits = bitFile.ReadBits(nFetch) + ((d - command) << nFetch); if(newbits != numbits) { numbits = newbits & 0x07; } else if((d = bitFile.ReadBits(3)) == 7) { if(bitFile.ReadBits(1)) break; newval = 0xFF; } else { newval = 0xF8 + d; } } else { newval = d; } if(newval < sizeof(ptable)) { int n = ptable[newval]; if(blk.flags & MMCMP_DELTA) { n += oldval; oldval = n; } pDest[dwPos++] = static_cast(n); } if(dwPos >= dwSize) { subblk++; dwPos = 0; if(!(subblk < blk.sub_blk)) break; if(!psubblk[subblk].Validate(unpackedData, unpackedSize)) return false; dwSize = psubblk[subblk].size; pDest = &(unpackedData[psubblk[subblk].position]); } } } catch(const BitReader::eof &) { } } } containerItems.back().file = FileReader(mpt::byte_cast(mpt::as_span(unpackedData))); return true; } #endif // !MPT_WITH_ANCIENT OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/ContainerPP20.cpp0000644000175000017500000001036414052707103021126 00000000000000/* * ContainerPP20.cpp * ----------------- * Purpose: Handling of PowerPack PP20 compressed modules * Notes : (currently none) * Authors: Olivier Lapicque * OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "../common/FileReader.h" #include "Container.h" #include "Sndfile.h" #include OPENMPT_NAMESPACE_BEGIN #if !defined(MPT_WITH_ANCIENT) struct PPBITBUFFER { uint32 bitcount = 0; uint32 bitbuffer = 0; const uint8 *pStart = nullptr; const uint8 *pSrc = nullptr; uint32 GetBits(uint32 n); }; uint32 PPBITBUFFER::GetBits(uint32 n) { uint32 result = 0; for(uint32 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; } static bool PP20_DoUnpack(const uint8 *pSrc, uint32 srcLen, uint8 *pDst, uint32 dstLen) { const std::array modeTable{pSrc[0], pSrc[1], pSrc[2], pSrc[3]}; PPBITBUFFER BitBuffer; BitBuffer.pStart = pSrc; BitBuffer.pSrc = pSrc + srcLen - 4; BitBuffer.GetBits(pSrc[srcLen - 1]); uint32 bytesLeft = dstLen; while(bytesLeft > 0) { if(!BitBuffer.GetBits(1)) { uint32 count = 1, countAdd; do { countAdd = BitBuffer.GetBits(2); count += countAdd; } while(countAdd == 3); LimitMax(count, bytesLeft); for(uint32 i = 0; i < count; i++) { pDst[--bytesLeft] = (uint8)BitBuffer.GetBits(8); } if(!bytesLeft) break; } { uint32 modeIndex = BitBuffer.GetBits(2); MPT_CHECKER_ASSUME(modeIndex < 4); uint32 count = modeIndex + 2, offset; if(modeIndex == 3) { offset = BitBuffer.GetBits((BitBuffer.GetBits(1)) ? modeTable[modeIndex] : 7); uint32 countAdd = 7; do { countAdd = BitBuffer.GetBits(3); count += countAdd; } while(countAdd == 7); } else { offset = BitBuffer.GetBits(modeTable[modeIndex]); } LimitMax(count, bytesLeft); for(uint32 i = 0; i < count; i++) { pDst[bytesLeft - 1] = (bytesLeft + offset < dstLen) ? pDst[bytesLeft + offset] : 0; --bytesLeft; } } } return true; } struct PP20header { char magic[4]; // "PP20" uint8 efficiency[4]; }; MPT_BINARY_STRUCT(PP20header, 8) static bool ValidateHeader(const PP20header &hdr) { if(std::memcmp(hdr.magic, "PP20", 4) != 0) { return false; } if(hdr.efficiency[0] < 9 || hdr.efficiency[0] > 15 || hdr.efficiency[1] < 9 || hdr.efficiency[1] > 15 || hdr.efficiency[2] < 9 || hdr.efficiency[2] > 15 || hdr.efficiency[3] < 9 || hdr.efficiency[3] > 15) { return false; } return true; } CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderPP20(MemoryFileReader file, const uint64 *pfilesize) { PP20header hdr; if(!file.ReadStruct(hdr)) { return ProbeWantMoreData; } if(!ValidateHeader(hdr)) { return ProbeFailure; } MPT_UNREFERENCED_PARAMETER(pfilesize); return ProbeSuccess; } bool UnpackPP20(std::vector &containerItems, FileReader &file, ContainerLoadingFlags loadFlags) { file.Rewind(); containerItems.clear(); PP20header hdr; if(!file.ReadStruct(hdr)) { return false; } if(!ValidateHeader(hdr)) { return false; } if(loadFlags == ContainerOnlyVerifyHeader) { return true; } if(!file.CanRead(4)) { return false; } containerItems.emplace_back(); containerItems.back().data_cache = std::make_unique >(); std::vector & unpackedData = *(containerItems.back().data_cache); FileReader::off_t length = file.GetLength(); if(!mpt::in_range(length)) return false; // Length word must be aligned if((length % 2u) != 0) return false; file.Seek(length - 4); uint32 dstLen = file.ReadUint24BE(); if(dstLen == 0) return false; try { unpackedData.resize(dstLen); } catch(mpt::out_of_memory e) { mpt::delete_out_of_memory(e); return false; } file.Seek(4); bool result = PP20_DoUnpack(file.GetRawData().data(), static_cast(length - 4), mpt::byte_cast(unpackedData.data()), dstLen); if(result) { containerItems.back().file = FileReader(mpt::byte_cast(mpt::as_span(unpackedData))); } return result; } #endif // !MPT_WITH_ANCIENT OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/ContainerUMX.cpp0000644000175000017500000000436314024476651021132 00000000000000/* * ContainerUMX.cpp * ---------------- * Purpose: UMX (Unreal Music) module ripper * Notes : Obviously, this code only rips modules from older Unreal Engine games, such as Unreal 1, Unreal Tournament 1 and Deus Ex. * Authors: OpenMPT Devs (inspired by code from http://wiki.beyondunreal.com/Legacy:Package_File_Format) * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Loaders.h" #include "UMXTools.h" #include "Container.h" #include "Sndfile.h" OPENMPT_NAMESPACE_BEGIN CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderUMX(MemoryFileReader file, const uint64 *pfilesize) { return UMX::ProbeFileHeader(file, pfilesize, "music"); } bool UnpackUMX(std::vector &containerItems, FileReader &file, ContainerLoadingFlags loadFlags) { file.Rewind(); containerItems.clear(); UMX::FileHeader fileHeader; if(!file.ReadStruct(fileHeader) || !fileHeader.IsValid()) return false; // Note that this can be a false positive, e.g. Unreal maps will have music and sound // in their name table because they usually import such files. However, it spares us // from wildly seeking through the file, as the name table is usually right at the // start of the file, so it is hopefully a good enough heuristic for our purposes. if(!UMX::FindNameTableEntry(file, fileHeader, "music")) return false; else if(!file.CanRead(fileHeader.GetMinimumAdditionalFileSize())) return false; else if(loadFlags == ContainerOnlyVerifyHeader) return true; const std::vector names = UMX::ReadNameTable(file, fileHeader); const std::vector classes = UMX::ReadImportTable(file, fileHeader, names); // Read export table file.Seek(fileHeader.exportOffset); for(uint32 i = 0; i < fileHeader.exportCount && file.CanRead(8); i++) { auto [fileChunk, objName] = UMX::ReadExportTableEntry(file, fileHeader, classes, names, "music"); if(!fileChunk.IsValid()) continue; ContainerItem item; if(objName >= 0 && static_cast(objName) < names.size()) { item.name = mpt::ToUnicode(mpt::Charset::ISO8859_1, names[objName]); } item.file = fileChunk; containerItems.push_back(std::move(item)); } return !containerItems.empty(); } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/ContainerXPK.cpp0000644000175000017500000001671714155425024021122 00000000000000/* * ContainerXPK.cpp * ---------------- * Purpose: Handling of XPK compressed modules * Notes : (currently none) * Authors: Olivier Lapicque * OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "../common/FileReader.h" #include "Container.h" #include "Sndfile.h" #include OPENMPT_NAMESPACE_BEGIN #if !defined(MPT_WITH_ANCIENT) #ifdef MPT_ALL_LOGGING #define MMCMP_LOG #endif struct XPKFILEHEADER { char XPKF[4]; uint32be SrcLen; char SQSH[4]; uint32be DstLen; char Name[16]; uint32be Reserved; }; MPT_BINARY_STRUCT(XPKFILEHEADER, 36) struct XPK_error : public std::range_error { XPK_error() : std::range_error("invalid XPK data") { } }; struct XPK_BufferBounds { const uint8 *pSrcBeg; std::size_t SrcSize; inline uint8 SrcRead(std::size_t index) { if(index >= SrcSize) throw XPK_error(); return pSrcBeg[index]; } }; static int32 bfextu(std::size_t p, int32 bo, int32 bc, XPK_BufferBounds &bufs) { uint32 r; p += bo / 8; r = bufs.SrcRead(p); p++; r <<= 8; r |= bufs.SrcRead(p); p++; r <<= 8; r |= bufs.SrcRead(p); r <<= bo % 8; r &= 0xffffff; r >>= 24 - bc; return r; } static int32 bfexts(std::size_t p, int32 bo, int32 bc, XPK_BufferBounds &bufs) { uint32 r; p += bo / 8; r = bufs.SrcRead(p); p++; r <<= 8; r |= bufs.SrcRead(p); p++; r <<= 8; r |= bufs.SrcRead(p); r <<= (bo % 8) + 8; return mpt::rshift_signed(static_cast(r), 32 - bc); } static uint8 XPK_ReadTable(int32 index) { static constexpr uint8 xpk_table[] = { 2,3,4,5,6,7,8,0,3,2,4,5,6,7,8,0,4,3,5,2,6,7,8,0,5,4,6,2,3,7,8,0,6,5,7,2,3,4,8,0,7,6,8,2,3,4,5,0,8,7,6,2,3,4,5,0 }; if(index < 0) throw XPK_error(); if(static_cast(index) >= std::size(xpk_table)) throw XPK_error(); return xpk_table[index]; } static bool XPK_DoUnpack(const uint8 *src_, uint32 srcLen, std::vector &unpackedData, int32 len) { if(len <= 0) return false; int32 d0,d1,d2,d3,d4,d5,d6,a2,a5; int32 cp, cup1, type; std::size_t c; std::size_t src; std::size_t phist = 0; unpackedData.reserve(std::min(static_cast(len), std::min(srcLen, uint32_max / 20u) * 20u)); XPK_BufferBounds bufs; bufs.pSrcBeg = src_; bufs.SrcSize = srcLen; src = 0; c = src; while(len > 0) { type = bufs.SrcRead(c+0); cp = (bufs.SrcRead(c+4)<<8) | (bufs.SrcRead(c+5)); // packed cup1 = (bufs.SrcRead(c+6)<<8) | (bufs.SrcRead(c+7)); // unpacked //Log(" packed=%6d unpacked=%6d bytes left=%d dst=%08X(%d)\n", cp, cup1, len, dst, dst); c += 8; src = c+2; if (type == 0) { // RAW chunk if(cp < 0 || cp > len) throw XPK_error(); for(int32 i = 0; i < cp; ++i) { unpackedData.push_back(bufs.SrcRead(c + i)); } c+=cp; len -= cp; continue; } if (type != 1) { #ifdef MMCMP_LOG MPT_LOG_GLOBAL(LogDebug, "XPK", MPT_UFORMAT("Invalid XPK type! ({} bytes left)")(len)); #endif break; } LimitMax(cup1, len); len -= cup1; cp = (cp + 3) & 0xfffc; c += cp; d0 = d1 = d2 = a2 = 0; d3 = bufs.SrcRead(src); src++; unpackedData.push_back(static_cast(d3)); cup1--; while (cup1 > 0) { if (d1 >= 8) goto l6dc; if (bfextu(src,d0,1,bufs)) goto l75a; d0 += 1; d5 = 0; d6 = 8; goto l734; l6dc: if (bfextu(src,d0,1,bufs)) goto l726; d0 += 1; if (! bfextu(src,d0,1,bufs)) goto l75a; d0 += 1; if (bfextu(src,d0,1,bufs)) goto l6f6; d6 = 2; goto l708; l6f6: d0 += 1; if (!bfextu(src,d0,1,bufs)) goto l706; d6 = bfextu(src,d0,3,bufs); d0 += 3; goto l70a; l706: d6 = 3; l708: d0 += 1; l70a: d6 = XPK_ReadTable((8*a2) + d6 -17); if (d6 != 8) goto l730; l718: if (d2 >= 20) { d5 = 1; goto l732; } d5 = 0; goto l734; l726: d0 += 1; d6 = 8; if (d6 == a2) goto l718; d6 = a2; l730: d5 = 4; l732: d2 += 8; l734: while ((d5 >= 0) && (cup1 > 0)) { d4 = bfexts(src,d0,d6,bufs); d0 += d6; d3 -= d4; unpackedData.push_back(static_cast(d3)); cup1--; d5--; } if (d1 != 31) d1++; a2 = d6; l74c: d6 = d2; d6 >>= 3; d2 -= d6; } } return !unpackedData.empty(); l75a: d0 += 1; if (bfextu(src,d0,1,bufs)) goto l766; d4 = 2; goto l79e; l766: d0 += 1; if (bfextu(src,d0,1,bufs)) goto l772; d4 = 4; goto l79e; l772: d0 += 1; if (bfextu(src,d0,1,bufs)) goto l77e; d4 = 6; goto l79e; l77e: d0 += 1; if (bfextu(src,d0,1,bufs)) goto l792; d0 += 1; d6 = bfextu(src,d0,3,bufs); d0 += 3; d6 += 8; goto l7a8; l792: d0 += 1; d6 = bfextu(src,d0,5,bufs); d0 += 5; d4 = 16; goto l7a6; l79e: d0 += 1; d6 = bfextu(src,d0,1,bufs); d0 += 1; l7a6: d6 += d4; l7a8: if(bfextu(src, d0, 1, bufs)) { d5 = 12; a5 = -0x100; } else { d0 += 1; if(bfextu(src, d0, 1, bufs)) { d5 = 14; a5 = -0x1100; } else { d5 = 8; a5 = 0; } } d0 += 1; d4 = bfextu(src,d0,d5,bufs); d0 += d5; d6 -= 3; if (d6 >= 0) { if (d6 > 0) d1 -= 1; d1 -= 1; if (d1 < 0) d1 = 0; } d6 += 2; phist = unpackedData.size() + a5 - d4 - 1; if(phist >= unpackedData.size()) throw XPK_error(); while ((d6 >= 0) && (cup1 > 0)) { d3 = unpackedData[phist]; phist++; unpackedData.push_back(static_cast(d3)); cup1--; d6--; } goto l74c; } static bool ValidateHeader(const XPKFILEHEADER &header) { if(std::memcmp(header.XPKF, "XPKF", 4) != 0) { return false; } if(std::memcmp(header.SQSH, "SQSH", 4) != 0) { return false; } if(header.SrcLen == 0) { return false; } if(header.DstLen == 0) { return false; } static_assert(sizeof(XPKFILEHEADER) >= 8); if(header.SrcLen < (sizeof(XPKFILEHEADER) - 8)) { return false; } return true; } static bool ValidateHeaderFileSize(const XPKFILEHEADER &header, uint64 filesize) { if(filesize < header.SrcLen - 8) { return false; } return true; } CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderXPK(MemoryFileReader file, const uint64 *pfilesize) { XPKFILEHEADER header; if(!file.ReadStruct(header)) { return ProbeWantMoreData; } if(!ValidateHeader(header)) { return ProbeFailure; } if(pfilesize) { if(!ValidateHeaderFileSize(header, *pfilesize)) { return ProbeFailure; } } return ProbeSuccess; } bool UnpackXPK(std::vector &containerItems, FileReader &file, ContainerLoadingFlags loadFlags) { file.Rewind(); containerItems.clear(); XPKFILEHEADER header; if(!file.ReadStruct(header)) { return false; } if(!ValidateHeader(header)) { return false; } if(loadFlags == ContainerOnlyVerifyHeader) { return true; } if(!file.CanRead(header.SrcLen - (sizeof(XPKFILEHEADER) - 8))) { return false; } containerItems.emplace_back(); containerItems.back().data_cache = std::make_unique >(); std::vector & unpackedData = *(containerItems.back().data_cache); #ifdef MMCMP_LOG MPT_LOG_GLOBAL(LogDebug, "XPK", MPT_UFORMAT("XPK detected (SrcLen={} DstLen={}) filesize={}")(static_cast(header.SrcLen), static_cast(header.DstLen), file.GetLength())); #endif bool result = false; try { result = XPK_DoUnpack(file.GetRawData().data(), header.SrcLen - (sizeof(XPKFILEHEADER) - 8), unpackedData, header.DstLen); } catch(mpt::out_of_memory e) { mpt::delete_out_of_memory(e); return false; } catch(const XPK_error &) { return false; } if(result) { containerItems.back().file = FileReader(mpt::byte_cast(mpt::as_span(unpackedData))); } return result; } #endif // !MPT_WITH_ANCIENT OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Container.h0000644000175000017500000000217714052707103020174 00000000000000/* * Container.h * ----------- * Purpose: General interface for MDO container and/or packers. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "../common/FileReader.h" #include OPENMPT_NAMESPACE_BEGIN struct ContainerItem { mpt::ustring name; FileReader file; std::unique_ptr > data_cache; // may be empty }; enum ContainerLoadingFlags { ContainerOnlyVerifyHeader = 0x00, ContainerUnwrapData = 0x01, }; #if !defined(MPT_WITH_ANCIENT) bool UnpackXPK(std::vector &containerItems, FileReader &file, ContainerLoadingFlags loadFlags); bool UnpackPP20(std::vector &containerItems, FileReader &file, ContainerLoadingFlags loadFlags); bool UnpackMMCMP(std::vector &containerItems, FileReader &file, ContainerLoadingFlags loadFlags); #endif // !MPT_WITH_ANCIENT bool UnpackUMX(std::vector &containerItems, FileReader &file, ContainerLoadingFlags loadFlags); OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Dlsbank.cpp0000644000175000017500000020132314173246013020157 00000000000000/* * DLSBank.cpp * ----------- * Purpose: Sound bank loading. * Notes : Supported sound bank types: DLS (including embedded DLS in MSS & RMI), SF2, SF3 / SF4 (modified SF2 with compressed samples) * Authors: Olivier Lapicque * OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Sndfile.h" #ifdef MODPLUG_TRACKER #include "../mptrack/Mptrack.h" #include "../common/mptFileIO.h" #endif #include "Dlsbank.h" #include "Loaders.h" #include "SampleCopy.h" #include "../common/mptStringBuffer.h" #include "../common/FileReader.h" #include "openmpt/base/Endian.hpp" #include "SampleIO.h" #include "mpt/io/base.hpp" #include "mpt/io/io.hpp" #include "mpt/io/io_stdstream.hpp" OPENMPT_NAMESPACE_BEGIN #ifdef MODPLUG_TRACKER #ifdef MPT_ALL_LOGGING #define DLSBANK_LOG #define DLSINSTR_LOG #endif #define F_RGN_OPTION_SELFNONEXCLUSIVE 0x0001 // Region Flags enum RegionFlags { DLSREGION_KEYGROUPMASK = 0x0F, DLSREGION_OVERRIDEWSMP = 0x10, DLSREGION_PINGPONGLOOP = 0x20, DLSREGION_SAMPLELOOP = 0x40, DLSREGION_SELFNONEXCLUSIVE = 0x80, DLSREGION_SUSTAINLOOP = 0x100, }; /////////////////////////////////////////////////////////////////////////// // Articulation connection graph definitions enum ConnectionSource : uint16 { // Generic Sources CONN_SRC_NONE = 0x0000, CONN_SRC_LFO = 0x0001, CONN_SRC_KEYONVELOCITY = 0x0002, CONN_SRC_KEYNUMBER = 0x0003, CONN_SRC_EG1 = 0x0004, CONN_SRC_EG2 = 0x0005, CONN_SRC_PITCHWHEEL = 0x0006, CONN_SRC_POLYPRESSURE = 0x0007, CONN_SRC_CHANNELPRESSURE = 0x0008, CONN_SRC_VIBRATO = 0x0009, // Midi Controllers 0-127 CONN_SRC_CC1 = 0x0081, CONN_SRC_CC7 = 0x0087, CONN_SRC_CC10 = 0x008a, CONN_SRC_CC11 = 0x008b, CONN_SRC_CC91 = 0x00db, CONN_SRC_CC93 = 0x00dd, CONN_SRC_RPN0 = 0x0100, CONN_SRC_RPN1 = 0x0101, CONN_SRC_RPN2 = 0x0102, }; enum ConnectionDestination : uint16 { // Generic Destinations CONN_DST_NONE = 0x0000, CONN_DST_ATTENUATION = 0x0001, CONN_DST_RESERVED = 0x0002, CONN_DST_PITCH = 0x0003, CONN_DST_PAN = 0x0004, // LFO Destinations CONN_DST_LFO_FREQUENCY = 0x0104, CONN_DST_LFO_STARTDELAY = 0x0105, CONN_DST_KEYNUMBER = 0x0005, // EG1 Destinations CONN_DST_EG1_ATTACKTIME = 0x0206, CONN_DST_EG1_DECAYTIME = 0x0207, CONN_DST_EG1_RESERVED = 0x0208, CONN_DST_EG1_RELEASETIME = 0x0209, CONN_DST_EG1_SUSTAINLEVEL = 0x020a, CONN_DST_EG1_DELAYTIME = 0x020b, CONN_DST_EG1_HOLDTIME = 0x020c, CONN_DST_EG1_SHUTDOWNTIME = 0x020d, // EG2 Destinations CONN_DST_EG2_ATTACKTIME = 0x030a, CONN_DST_EG2_DECAYTIME = 0x030b, CONN_DST_EG2_RESERVED = 0x030c, CONN_DST_EG2_RELEASETIME = 0x030d, CONN_DST_EG2_SUSTAINLEVEL = 0x030e, CONN_DST_EG2_DELAYTIME = 0x030f, CONN_DST_EG2_HOLDTIME = 0x0310, CONN_TRN_NONE = 0x0000, CONN_TRN_CONCAVE = 0x0001, }; ////////////////////////////////////////////////////////// // Supported DLS1 Articulations // [4-bit transform][12-bit dest][8-bit control][8-bit source] = 32-bit ID constexpr uint32 DLSArt(uint8 src, uint8 ctl, uint16 dst) { return (dst << 16u) | (ctl << 8u) | src; } enum DLSArt : uint32 { // Vibrato / Tremolo ART_LFO_FREQUENCY = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_LFO_FREQUENCY), ART_LFO_STARTDELAY = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_LFO_STARTDELAY), ART_LFO_ATTENUATION = DLSArt(CONN_SRC_LFO, CONN_SRC_NONE, CONN_DST_ATTENUATION), ART_LFO_PITCH = DLSArt(CONN_SRC_LFO, CONN_SRC_NONE, CONN_DST_PITCH), ART_LFO_MODWTOATTN = DLSArt(CONN_SRC_LFO, CONN_SRC_CC1, CONN_DST_ATTENUATION), ART_LFO_MODWTOPITCH = DLSArt(CONN_SRC_LFO, CONN_SRC_CC1, CONN_DST_PITCH), // Volume Envelope ART_VOL_EG_ATTACKTIME = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG1_ATTACKTIME), ART_VOL_EG_DECAYTIME = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG1_DECAYTIME), ART_VOL_EG_SUSTAINLEVEL = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG1_SUSTAINLEVEL), ART_VOL_EG_RELEASETIME = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG1_RELEASETIME), ART_VOL_EG_DELAYTIME = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG1_DELAYTIME), ART_VOL_EG_HOLDTIME = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG1_HOLDTIME), ART_VOL_EG_SHUTDOWNTIME = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG1_SHUTDOWNTIME), ART_VOL_EG_VELTOATTACK = DLSArt(CONN_SRC_KEYONVELOCITY, CONN_SRC_NONE, CONN_DST_EG1_ATTACKTIME), ART_VOL_EG_KEYTODECAY = DLSArt(CONN_SRC_KEYNUMBER, CONN_SRC_NONE, CONN_DST_EG1_DECAYTIME), // Pitch Envelope ART_PITCH_EG_ATTACKTIME = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG2_ATTACKTIME), ART_PITCH_EG_DECAYTIME = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG2_DECAYTIME), ART_PITCH_EG_SUSTAINLEVEL = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG2_SUSTAINLEVEL), ART_PITCH_EG_RELEASETIME = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG2_RELEASETIME), ART_PITCH_EG_VELTOATTACK = DLSArt(CONN_SRC_KEYONVELOCITY, CONN_SRC_NONE, CONN_DST_EG2_ATTACKTIME), ART_PITCH_EG_KEYTODECAY = DLSArt(CONN_SRC_KEYNUMBER, CONN_SRC_NONE, CONN_DST_EG2_DECAYTIME), // Default Pan ART_DEFAULTPAN = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_PAN), }; ////////////////////////////////////////////////////////// // DLS IFF Chunk IDs enum IFFChunkID : uint32 { // Standard IFF chunks IDs IFFID_FORM = MagicLE("FORM"), IFFID_RIFF = MagicLE("RIFF"), IFFID_LIST = MagicLE("LIST"), IFFID_INFO = MagicLE("INFO"), // IFF Info fields IFFID_ICOP = MagicLE("ICOP"), IFFID_INAM = MagicLE("INAM"), IFFID_ICMT = MagicLE("ICMT"), IFFID_IENG = MagicLE("IENG"), IFFID_ISFT = MagicLE("ISFT"), IFFID_ISBJ = MagicLE("ISBJ"), // Wave IFF chunks IDs IFFID_wave = MagicLE("wave"), IFFID_wsmp = MagicLE("wsmp"), IFFID_XDLS = MagicLE("XDLS"), IFFID_DLS = MagicLE("DLS "), IFFID_MLS = MagicLE("MLS "), IFFID_RMID = MagicLE("RMID"), IFFID_colh = MagicLE("colh"), IFFID_ins = MagicLE("ins "), IFFID_insh = MagicLE("insh"), IFFID_ptbl = MagicLE("ptbl"), IFFID_wvpl = MagicLE("wvpl"), IFFID_rgn = MagicLE("rgn "), IFFID_rgn2 = MagicLE("rgn2"), IFFID_rgnh = MagicLE("rgnh"), IFFID_wlnk = MagicLE("wlnk"), IFFID_art1 = MagicLE("art1"), IFFID_art2 = MagicLE("art2"), }; ////////////////////////////////////////////////////////// // DLS Structures definitions struct IFFCHUNK { uint32le id; uint32le len; }; MPT_BINARY_STRUCT(IFFCHUNK, 8) struct RIFFChunkID { uint32le id_RIFF; uint32le riff_len; uint32le id_DLS; }; MPT_BINARY_STRUCT(RIFFChunkID, 12) struct LISTChunk { uint32le id; uint32le len; uint32le listid; }; MPT_BINARY_STRUCT(LISTChunk, 12) struct DLSRgnRange { uint16le usLow; uint16le usHigh; }; MPT_BINARY_STRUCT(DLSRgnRange, 4) struct VERSChunk { uint32le id; uint32le len; uint16le version[4]; }; MPT_BINARY_STRUCT(VERSChunk, 16) struct PTBLChunk { uint32le cbSize; uint32le cCues; }; MPT_BINARY_STRUCT(PTBLChunk, 8) struct INSHChunk { uint32le cRegions; uint32le ulBank; uint32le ulInstrument; }; MPT_BINARY_STRUCT(INSHChunk, 12) struct RGNHChunk { DLSRgnRange RangeKey; DLSRgnRange RangeVelocity; uint16le fusOptions; uint16le usKeyGroup; }; MPT_BINARY_STRUCT(RGNHChunk, 12) struct WLNKChunk { uint16le fusOptions; uint16le usPhaseGroup; uint32le ulChannel; uint32le ulTableIndex; }; MPT_BINARY_STRUCT(WLNKChunk, 12) struct ART1Chunk { uint32le cbSize; uint32le cConnectionBlocks; }; MPT_BINARY_STRUCT(ART1Chunk, 8) struct ConnectionBlock { uint16le usSource; uint16le usControl; uint16le usDestination; uint16le usTransform; int32le lScale; }; MPT_BINARY_STRUCT(ConnectionBlock, 12) struct WSMPChunk { uint32le cbSize; uint16le usUnityNote; int16le sFineTune; int32le lAttenuation; uint32le fulOptions; uint32le cSampleLoops; }; MPT_BINARY_STRUCT(WSMPChunk, 20) struct WSMPSampleLoop { uint32le cbSize; uint32le ulLoopType; uint32le ulLoopStart; uint32le ulLoopLength; }; MPT_BINARY_STRUCT(WSMPSampleLoop, 16) ///////////////////////////////////////////////////////////////////// // SF2 IFF Chunk IDs enum SF2ChunkID : uint32 { IFFID_ifil = MagicLE("ifil"), IFFID_sfbk = MagicLE("sfbk"), IFFID_sfpk = MagicLE("sfpk"), // SF2Pack compressed soundfont IFFID_sdta = MagicLE("sdta"), IFFID_pdta = MagicLE("pdta"), IFFID_phdr = MagicLE("phdr"), IFFID_pbag = MagicLE("pbag"), IFFID_pgen = MagicLE("pgen"), IFFID_inst = MagicLE("inst"), IFFID_ibag = MagicLE("ibag"), IFFID_igen = MagicLE("igen"), IFFID_shdr = MagicLE("shdr"), }; /////////////////////////////////////////// // SF2 Generators IDs enum SF2Generators : uint16 { SF2_GEN_START_LOOP_FINE = 2, SF2_GEN_END_LOOP_FINE = 3, SF2_GEN_MODENVTOFILTERFC = 11, SF2_GEN_PAN = 17, SF2_GEN_DECAYMODENV = 28, SF2_GEN_ATTACKVOLENV = 34, SF2_GEN_HOLDVOLENV = 34, SF2_GEN_DECAYVOLENV = 36, SF2_GEN_SUSTAINVOLENV = 37, SF2_GEN_RELEASEVOLENV = 38, SF2_GEN_INSTRUMENT = 41, SF2_GEN_KEYRANGE = 43, SF2_GEN_START_LOOP_COARSE = 45, SF2_GEN_ATTENUATION = 48, SF2_GEN_END_LOOP_COARSE = 50, SF2_GEN_COARSETUNE = 51, SF2_GEN_FINETUNE = 52, SF2_GEN_SAMPLEID = 53, SF2_GEN_SAMPLEMODES = 54, SF2_GEN_SCALE_TUNING = 56, SF2_GEN_KEYGROUP = 57, SF2_GEN_UNITYNOTE = 58, }; ///////////////////////////////////////////////////////////////////// // SF2 Structures Definitions struct SFPresetHeader { char achPresetName[20]; uint16le wPreset; uint16le wBank; uint16le wPresetBagNdx; uint32le dwLibrary; uint32le dwGenre; uint32le dwMorphology; }; MPT_BINARY_STRUCT(SFPresetHeader, 38) struct SFPresetBag { uint16le wGenNdx; uint16le wModNdx; }; MPT_BINARY_STRUCT(SFPresetBag, 4) struct SFGenList { uint16le sfGenOper; uint16le genAmount; }; MPT_BINARY_STRUCT(SFGenList, 4) struct SFInst { char achInstName[20]; uint16le wInstBagNdx; }; MPT_BINARY_STRUCT(SFInst, 22) struct SFInstBag { uint16le wGenNdx; uint16le wModNdx; }; MPT_BINARY_STRUCT(SFInstBag, 4) struct SFInstGenList { uint16le sfGenOper; uint16le genAmount; }; MPT_BINARY_STRUCT(SFInstGenList, 4) struct SFSample { char achSampleName[20]; uint32le dwStart; uint32le dwEnd; uint32le dwStartloop; uint32le dwEndloop; uint32le dwSampleRate; uint8le byOriginalPitch; int8le chPitchCorrection; uint16le wSampleLink; uint16le sfSampleType; }; MPT_BINARY_STRUCT(SFSample, 46) // End of structures definitions ///////////////////////////////////////////////////////////////////// struct SF2LoaderInfo { FileReader presetBags; FileReader presetGens; FileReader insts; FileReader instBags; FileReader instGens; }; ///////////////////////////////////////////////////////////////////// // Unit conversion static uint8 DLSSustainLevelToLinear(int32 sustain) { // 0.1% units if(sustain >= 0) { int32 l = sustain / (1000 * 512); if(l >= 0 && l <= 128) return static_cast(l); } return 128; } static uint8 SF2SustainLevelToLinear(int32 sustain) { // 0.1% units int32 l = 128 * (1000 - Clamp(sustain, 0, 1000)) / 1000; return static_cast(l); } int32 CDLSBank::DLS32BitTimeCentsToMilliseconds(int32 lTimeCents) { // tc = log2(time[secs]) * 1200*65536 // time[secs] = 2^(tc/(1200*65536)) if ((uint32)lTimeCents == 0x80000000) return 0; double fmsecs = 1000.0 * std::pow(2.0, ((double)lTimeCents)/(1200.0*65536.0)); if (fmsecs < -32767) return -32767; if (fmsecs > 32767) return 32767; return (int32)fmsecs; } // 0dB = 0x10000 int32 CDLSBank::DLS32BitRelativeGainToLinear(int32 lCentibels) { // v = 10^(cb/(200*65536)) * V return (int32)(65536.0 * std::pow(10.0, ((double)lCentibels)/(200*65536.0)) ); } int32 CDLSBank::DLS32BitRelativeLinearToGain(int32 lGain) { // cb = log10(v/V) * 200 * 65536 if (lGain <= 0) return -960 * 65536; return (int32)(200 * 65536.0 * std::log10(((double)lGain) / 65536.0)); } int32 CDLSBank::DLSMidiVolumeToLinear(uint32 nMidiVolume) { return (nMidiVolume * nMidiVolume << 16) / (127*127); } ///////////////////////////////////////////////////////////////////// // Implementation CDLSBank::CDLSBank() { m_nMaxWaveLink = 0; m_nType = SOUNDBANK_TYPE_INVALID; } bool CDLSBank::IsDLSBank(const mpt::PathString &filename) { RIFFChunkID riff; if(filename.empty()) return false; mpt::ifstream f(filename, std::ios::binary); if(!f) { return false; } MemsetZero(riff); mpt::IO::Read(f, riff); // Check for embedded DLS sections if(riff.id_RIFF == IFFID_FORM) { // Miles Sound System do { uint32 len = mpt::bit_cast(riff.riff_len); if (len <= 4) break; if (riff.id_DLS == IFFID_XDLS) { mpt::IO::Read(f, riff); break; } if((len % 2u) != 0) len++; if (!mpt::IO::SeekRelative(f, len-4)) break; } while (mpt::IO::Read(f, riff)); } else if(riff.id_RIFF == IFFID_RIFF && riff.id_DLS == IFFID_RMID) { for (;;) { if(!mpt::IO::Read(f, riff)) break; if (riff.id_DLS == IFFID_DLS) break; // found it int len = riff.riff_len; if((len % 2u) != 0) len++; if ((len <= 4) || !mpt::IO::SeekRelative(f, len-4)) break; } } return ((riff.id_RIFF == IFFID_RIFF) && ((riff.id_DLS == IFFID_DLS) || (riff.id_DLS == IFFID_MLS) || (riff.id_DLS == IFFID_sfbk)) && (riff.riff_len >= 256)); } /////////////////////////////////////////////////////////////// // Find an instrument based on the given parameters const DLSINSTRUMENT *CDLSBank::FindInstrument(bool isDrum, uint32 bank, uint32 program, uint32 key, uint32 *pInsNo) const { // This helps finding the "more correct" instrument if we search for an instrument in any bank, and the higher-bank instruments appear first in the file // Fixes issues when loading GeneralUser GS into OpenMPT's MIDI library. std::vector> sortedInstr{m_Instruments.begin(), m_Instruments.end()}; if(bank >= 0x4000 || program >= 0x80) { std::sort(sortedInstr.begin(), sortedInstr.end(), [](const DLSINSTRUMENT &l, const DLSINSTRUMENT &r) { return std::tie(l.ulBank, l.ulInstrument) < std::tie(r.ulBank, r.ulInstrument); }); } for(const DLSINSTRUMENT &dlsIns : sortedInstr) { uint32 insbank = ((dlsIns.ulBank & 0x7F00) >> 1) | (dlsIns.ulBank & 0x7F); if((bank >= 0x4000) || (insbank == bank)) { if(isDrum && (dlsIns.ulBank & F_INSTRUMENT_DRUMS)) { if((program >= 0x80) || (program == (dlsIns.ulInstrument & 0x7F))) { for(const auto ®ion : dlsIns.Regions) { if(region.IsDummy()) continue; if((!key || key >= 0x80) || (key >= region.uKeyMin && key <= region.uKeyMax)) { if(pInsNo) *pInsNo = static_cast(std::distance(m_Instruments.data(), &dlsIns)); // cppcheck false-positive // cppcheck-suppress returnDanglingLifetime return &dlsIns; } } } } else if(!isDrum && !(dlsIns.ulBank & F_INSTRUMENT_DRUMS)) { if((program >= 0x80) || (program == (dlsIns.ulInstrument & 0x7F))) { if(pInsNo) *pInsNo = static_cast(std::distance(m_Instruments.data(), &dlsIns)); // cppcheck false-positive // cppcheck-suppress returnDanglingLifetime return &dlsIns; } } } } return nullptr; } bool CDLSBank::FindAndExtract(CSoundFile &sndFile, const INSTRUMENTINDEX ins, const bool isDrum) const { ModInstrument *pIns = sndFile.Instruments[ins]; if(pIns == nullptr) return false; uint32 dlsIns = 0, drumRgn = 0; const uint32 program = (pIns->nMidiProgram != 0) ? pIns->nMidiProgram - 1 : 0; const uint32 key = isDrum ? (pIns->nMidiDrumKey & 0x7F) : 0xFF; if(FindInstrument(isDrum, (pIns->wMidiBank - 1) & 0x3FFF, program, key, &dlsIns) || FindInstrument(isDrum, 0xFFFF, isDrum ? 0xFF : program, key, &dlsIns)) { if(key < 0x80) drumRgn = GetRegionFromKey(dlsIns, key); if(ExtractInstrument(sndFile, ins, dlsIns, drumRgn)) { pIns = sndFile.Instruments[ins]; // Reset pointer because ExtractInstrument may delete the previous value. if((key >= 24) && (key < 24 + std::size(szMidiPercussionNames))) { pIns->name = szMidiPercussionNames[key - 24]; } return true; } } return false; } /////////////////////////////////////////////////////////////// // Update DLS instrument definition from an IFF chunk bool CDLSBank::UpdateInstrumentDefinition(DLSINSTRUMENT *pDlsIns, FileReader chunk) { IFFCHUNK header; chunk.ReadStruct(header); if(!header.len || !chunk.CanRead(header.len)) return false; if(header.id == IFFID_LIST) { uint32 listid = chunk.ReadUint32LE(); while(chunk.CanRead(sizeof(IFFCHUNK))) { IFFCHUNK subHeader; chunk.ReadStruct(subHeader); chunk.SkipBack(sizeof(IFFCHUNK)); FileReader subData = chunk.ReadChunk(subHeader.len + sizeof(IFFCHUNK)); if(subHeader.len & 1) { chunk.Skip(1); } UpdateInstrumentDefinition(pDlsIns, subData); } switch(listid) { case IFFID_rgn: // Level 1 region case IFFID_rgn2: // Level 2 region pDlsIns->Regions.push_back({}); break; } } else { switch(header.id) { case IFFID_insh: { INSHChunk insh; chunk.ReadStruct(insh); pDlsIns->ulBank = insh.ulBank; pDlsIns->ulInstrument = insh.ulInstrument; //Log("%3d regions, bank 0x%04X instrument %3d\n", insh.cRegions, pDlsIns->ulBank, pDlsIns->ulInstrument); break; } case IFFID_rgnh: if(!pDlsIns->Regions.empty()) { RGNHChunk rgnh; chunk.ReadStruct(rgnh); DLSREGION ®ion = pDlsIns->Regions.back(); region.uKeyMin = (uint8)rgnh.RangeKey.usLow; region.uKeyMax = (uint8)rgnh.RangeKey.usHigh; region.fuOptions = (uint8)(rgnh.usKeyGroup & DLSREGION_KEYGROUPMASK); if(rgnh.fusOptions & F_RGN_OPTION_SELFNONEXCLUSIVE) region.fuOptions |= DLSREGION_SELFNONEXCLUSIVE; //Log(" Region %d: fusOptions=0x%02X usKeyGroup=0x%04X ", pDlsIns->nRegions, rgnh.fusOptions, rgnh.usKeyGroup); //Log("KeyRange[%3d,%3d] ", rgnh.RangeKey.usLow, rgnh.RangeKey.usHigh); } break; case IFFID_wlnk: if (!pDlsIns->Regions.empty()) { WLNKChunk wlnk; chunk.ReadStruct(wlnk); DLSREGION ®ion = pDlsIns->Regions.back(); region.nWaveLink = (uint16)wlnk.ulTableIndex; if((region.nWaveLink < Util::MaxValueOfType(region.nWaveLink)) && (region.nWaveLink >= m_nMaxWaveLink)) m_nMaxWaveLink = region.nWaveLink + 1; //Log(" WaveLink %d: fusOptions=0x%02X usPhaseGroup=0x%04X ", pDlsIns->nRegions, wlnk.fusOptions, wlnk.usPhaseGroup); //Log("ulChannel=%d ulTableIndex=%4d\n", wlnk.ulChannel, wlnk.ulTableIndex); } break; case IFFID_wsmp: if(!pDlsIns->Regions.empty()) { DLSREGION ®ion = pDlsIns->Regions.back(); WSMPChunk wsmp; chunk.ReadStruct(wsmp); region.fuOptions |= DLSREGION_OVERRIDEWSMP; region.uUnityNote = (uint8)wsmp.usUnityNote; region.sFineTune = wsmp.sFineTune; int32 lVolume = DLS32BitRelativeGainToLinear(wsmp.lAttenuation) / 256; if (lVolume > 256) lVolume = 256; if (lVolume < 4) lVolume = 4; region.usVolume = (uint16)lVolume; //Log(" WaveSample %d: usUnityNote=%2d sFineTune=%3d ", pDlsEnv->nRegions, p->usUnityNote, p->sFineTune); //Log("fulOptions=0x%04X loops=%d\n", p->fulOptions, p->cSampleLoops); if((wsmp.cSampleLoops) && (wsmp.cbSize + sizeof(WSMPSampleLoop) <= header.len)) { WSMPSampleLoop loop; chunk.Seek(sizeof(IFFCHUNK) + wsmp.cbSize); chunk.ReadStruct(loop); //Log("looptype=%2d loopstart=%5d loopend=%5d\n", ploop->ulLoopType, ploop->ulLoopStart, ploop->ulLoopLength); if(loop.ulLoopLength > 3) { region.fuOptions |= DLSREGION_SAMPLELOOP; //if(loop.ulLoopType) region.fuOptions |= DLSREGION_PINGPONGLOOP; region.ulLoopStart = loop.ulLoopStart; region.ulLoopEnd = loop.ulLoopStart + loop.ulLoopLength; } } } break; case IFFID_art1: case IFFID_art2: { ART1Chunk art1; chunk.ReadStruct(art1); if(!(pDlsIns->ulBank & F_INSTRUMENT_DRUMS)) { pDlsIns->nMelodicEnv = static_cast(m_Envelopes.size() + 1); } if(art1.cbSize + art1.cConnectionBlocks * sizeof(ConnectionBlock) > header.len) break; DLSENVELOPE dlsEnv; MemsetZero(dlsEnv); dlsEnv.nDefPan = 128; dlsEnv.nVolSustainLevel = 128; //Log(" art1 (%3d bytes): cbSize=%d cConnectionBlocks=%d\n", p->len, p->cbSize, p->cConnectionBlocks); chunk.Seek(sizeof(IFFCHUNK) + art1.cbSize); for (uint32 iblk = 0; iblk < art1.cConnectionBlocks; iblk++) { ConnectionBlock blk; chunk.ReadStruct(blk); // [4-bit transform][12-bit dest][8-bit control][8-bit source] = 32-bit ID uint32 dwArticulation = blk.usTransform; dwArticulation = (dwArticulation << 12) | (blk.usDestination & 0x0FFF); dwArticulation = (dwArticulation << 8) | (blk.usControl & 0x00FF); dwArticulation = (dwArticulation << 8) | (blk.usSource & 0x00FF); switch(dwArticulation) { case ART_DEFAULTPAN: { int32 pan = 128 + blk.lScale / (65536000/128); dlsEnv.nDefPan = mpt::saturate_cast(pan); } break; case ART_VOL_EG_ATTACKTIME: // 32-bit time cents units. range = [0s, 20s] dlsEnv.wVolAttack = 0; if(blk.lScale > -0x40000000) { int32 l = blk.lScale - 78743200; // maximum velocity if (l > 0) l = 0; int32 attacktime = DLS32BitTimeCentsToMilliseconds(l); if (attacktime < 0) attacktime = 0; if (attacktime > 20000) attacktime = 20000; if (attacktime >= 20) dlsEnv.wVolAttack = (uint16)(attacktime / 20); //Log("%3d: Envelope Attack Time set to %d (%d time cents)\n", (uint32)(dlsEnv.ulInstrument & 0x7F)|((dlsEnv.ulBank >> 16) & 0x8000), attacktime, pblk->lScale); } break; case ART_VOL_EG_DECAYTIME: // 32-bit time cents units. range = [0s, 20s] dlsEnv.wVolDecay = 0; if(blk.lScale > -0x40000000) { int32 decaytime = DLS32BitTimeCentsToMilliseconds(blk.lScale); if (decaytime > 20000) decaytime = 20000; if (decaytime >= 20) dlsEnv.wVolDecay = (uint16)(decaytime / 20); //Log("%3d: Envelope Decay Time set to %d (%d time cents)\n", (uint32)(dlsEnv.ulInstrument & 0x7F)|((dlsEnv.ulBank >> 16) & 0x8000), decaytime, pblk->lScale); } break; case ART_VOL_EG_RELEASETIME: // 32-bit time cents units. range = [0s, 20s] dlsEnv.wVolRelease = 0; if(blk.lScale > -0x40000000) { int32 releasetime = DLS32BitTimeCentsToMilliseconds(blk.lScale); if (releasetime > 20000) releasetime = 20000; if (releasetime >= 20) dlsEnv.wVolRelease = (uint16)(releasetime / 20); //Log("%3d: Envelope Release Time set to %d (%d time cents)\n", (uint32)(dlsEnv.ulInstrument & 0x7F)|((dlsEnv.ulBank >> 16) & 0x8000), dlsEnv.wVolRelease, pblk->lScale); } break; case ART_VOL_EG_SUSTAINLEVEL: // 0.1% units if(blk.lScale >= 0) { dlsEnv.nVolSustainLevel = DLSSustainLevelToLinear(blk.lScale); } break; //default: // Log(" Articulation = 0x%08X value=%d\n", dwArticulation, blk.lScale); } } m_Envelopes.push_back(dlsEnv); } break; case IFFID_INAM: chunk.ReadString(pDlsIns->szName, header.len); break; #if 0 default: { char sid[5]; memcpy(sid, &header.id, 4); sid[4] = 0; Log(" \"%s\": %d bytes\n", (uint32)sid, header.len.get()); } #endif } } return true; } /////////////////////////////////////////////////////////////// // Converts SF2 chunks to DLS bool CDLSBank::UpdateSF2PresetData(SF2LoaderInfo &sf2info, const IFFCHUNK &header, FileReader &chunk) { if (!chunk.IsValid()) return false; switch(header.id) { case IFFID_phdr: if(m_Instruments.empty()) { uint32 numIns = static_cast(chunk.GetLength() / sizeof(SFPresetHeader)); if(numIns <= 1) break; // The terminal sfPresetHeader record should never be accessed, and exists only to provide a terminal wPresetBagNdx with which to determine the number of zones in the last preset. numIns--; m_Instruments.resize(numIns); #ifdef DLSBANK_LOG MPT_LOG_GLOBAL(LogDebug, "DLSBank", MPT_UFORMAT("phdr: {} instruments")(m_Instruments.size())); #endif SFPresetHeader psfh; chunk.ReadStruct(psfh); for(auto &dlsIns : m_Instruments) { mpt::String::WriteAutoBuf(dlsIns.szName) = mpt::String::ReadAutoBuf(psfh.achPresetName); dlsIns.ulInstrument = psfh.wPreset & 0x7F; dlsIns.ulBank = (psfh.wBank >= 128) ? F_INSTRUMENT_DRUMS : (psfh.wBank << 8); dlsIns.wPresetBagNdx = psfh.wPresetBagNdx; dlsIns.wPresetBagNum = 1; chunk.ReadStruct(psfh); if (psfh.wPresetBagNdx > dlsIns.wPresetBagNdx) dlsIns.wPresetBagNum = static_cast(psfh.wPresetBagNdx - dlsIns.wPresetBagNdx); } } break; case IFFID_pbag: if(!m_Instruments.empty() && chunk.CanRead(sizeof(SFPresetBag))) { sf2info.presetBags = chunk.GetChunk(chunk.BytesLeft()); } #ifdef DLSINSTR_LOG else MPT_LOG_GLOBAL(LogDebug, "DLSINSTR", U_("pbag: no instruments!")); #endif break; case IFFID_pgen: if(!m_Instruments.empty() && chunk.CanRead(sizeof(SFGenList))) { sf2info.presetGens = chunk.GetChunk(chunk.BytesLeft()); } #ifdef DLSINSTR_LOG else MPT_LOG_GLOBAL(LogDebug, "DLSINSTR", U_("pgen: no instruments!")); #endif break; case IFFID_inst: if(!m_Instruments.empty() && chunk.CanRead(sizeof(SFInst))) { sf2info.insts = chunk.GetChunk(chunk.BytesLeft()); } break; case IFFID_ibag: if(!m_Instruments.empty() && chunk.CanRead(sizeof(SFInstBag))) { sf2info.instBags = chunk.GetChunk(chunk.BytesLeft()); } break; case IFFID_igen: if(!m_Instruments.empty() && chunk.CanRead(sizeof(SFInstGenList))) { sf2info.instGens = chunk.GetChunk(chunk.BytesLeft()); } break; case IFFID_shdr: if (m_SamplesEx.empty()) { uint32 numSmp = static_cast(chunk.GetLength() / sizeof(SFSample)); if (numSmp < 1) break; m_SamplesEx.resize(numSmp); m_WaveForms.resize(numSmp); #ifdef DLSINSTR_LOG MPT_LOG_GLOBAL(LogDebug, "DLSINSTR", MPT_UFORMAT("shdr: {} samples")(m_SamplesEx.size())); #endif for (uint32 i = 0; i < numSmp; i++) { SFSample p; chunk.ReadStruct(p); DLSSAMPLEEX &dlsSmp = m_SamplesEx[i]; mpt::String::WriteAutoBuf(dlsSmp.szName) = mpt::String::ReadAutoBuf(p.achSampleName); dlsSmp.dwLen = 0; dlsSmp.dwSampleRate = p.dwSampleRate; dlsSmp.byOriginalPitch = p.byOriginalPitch; dlsSmp.chPitchCorrection = static_cast(Util::muldivr(p.chPitchCorrection, 128, 100)); // cognitone's sf2convert tool doesn't set the correct sample flags (0x01 / 0x02 instead of 0x10/ 0x20). // For SF3, we ignore this and go by https://github.com/FluidSynth/fluidsynth/wiki/SoundFont3Format instead // As cognitone's tool is the only tool writing SF4 files, we always assume compressed samples with SF4 files if bits 0/1 are set. uint16 sampleType = p.sfSampleType; if(m_sf2version >= 0x4'0000 && m_sf2version <= 0x4'FFFF && (sampleType & 0x03)) sampleType = (sampleType & 0xFFFC) | 0x10; dlsSmp.compressed = (sampleType & 0x10); if(((sampleType & 0x7FCF) <= 4) && (p.dwEnd >= p.dwStart + 4)) { m_WaveForms[i] = p.dwStart; dlsSmp.dwLen = (p.dwEnd - p.dwStart); if(!dlsSmp.compressed) { m_WaveForms[i] *= 2; dlsSmp.dwLen *= 2; if((p.dwEndloop > p.dwStartloop + 7) && (p.dwStartloop >= p.dwStart)) { dlsSmp.dwStartloop = p.dwStartloop - p.dwStart; dlsSmp.dwEndloop = p.dwEndloop - p.dwStart; } } else { if(p.dwEndloop > p.dwStartloop + 7) { dlsSmp.dwStartloop = p.dwStartloop; dlsSmp.dwEndloop = p.dwEndloop; } } } } } break; #ifdef DLSINSTR_LOG default: { char sdbg[5]; memcpy(sdbg, &header.id, 4); sdbg[4] = 0; MPT_LOG_GLOBAL(LogDebug, "DLSINSTR", MPT_UFORMAT("Unsupported SF2 chunk: {} ({} bytes)")(mpt::ToUnicode(mpt::Charset::ASCII, mpt::String::ReadAutoBuf(sdbg)), header.len.get())); } #endif } return true; } static int16 SF2TimeToDLS(int16 amount) { int32 time = CDLSBank::DLS32BitTimeCentsToMilliseconds(static_cast(amount) << 16); return static_cast(Clamp(time, 20, 20000) / 20); } // Convert all instruments to the DLS format bool CDLSBank::ConvertSF2ToDLS(SF2LoaderInfo &sf2info) { if (m_Instruments.empty() || m_SamplesEx.empty()) return false; const uint32 numInsts = static_cast(sf2info.insts.GetLength() / sizeof(SFInst)); const uint32 numInstBags = static_cast(sf2info.instBags.GetLength() / sizeof(SFInstBag)); std::vector> instruments; // instrument, key range std::vector generators; std::vector instrGenerators; for(auto &dlsIns : m_Instruments) { instruments.clear(); DLSENVELOPE dlsEnv; int32 instrAttenuation = 0; int16 instrFinetune = 0; // Default Envelope Values dlsEnv.wVolAttack = 0; dlsEnv.wVolDecay = 0; dlsEnv.wVolRelease = 0; dlsEnv.nVolSustainLevel = 128; dlsEnv.nDefPan = 128; // Load Preset Bags sf2info.presetBags.Seek(dlsIns.wPresetBagNdx * sizeof(SFPresetBag)); for(uint32 ipbagcnt = 0; ipbagcnt < dlsIns.wPresetBagNum; ipbagcnt++) { // Load generators for each preset bag SFPresetBag bag[2]; if(!sf2info.presetBags.ReadArray(bag)) break; sf2info.presetBags.SkipBack(sizeof(SFPresetBag)); sf2info.presetGens.Seek(bag[0].wGenNdx * sizeof(SFGenList)); uint16 keyRange = 0xFFFF; if(!sf2info.presetGens.ReadVector(generators, bag[1].wGenNdx - bag[0].wGenNdx)) continue; for(const auto &gen : generators) { const int16 value = static_cast(gen.genAmount); switch(gen.sfGenOper) { case SF2_GEN_ATTACKVOLENV: dlsEnv.wVolAttack = SF2TimeToDLS(gen.genAmount); break; case SF2_GEN_DECAYVOLENV: dlsEnv.wVolDecay = SF2TimeToDLS(gen.genAmount); break; case SF2_GEN_SUSTAINVOLENV: // 0.1% units if(gen.genAmount >= 0) { dlsEnv.nVolSustainLevel = SF2SustainLevelToLinear(gen.genAmount); } break; case SF2_GEN_RELEASEVOLENV: dlsEnv.wVolRelease = SF2TimeToDLS(gen.genAmount); break; case SF2_GEN_INSTRUMENT: if(const auto instr = std::make_pair(gen.genAmount.get(), keyRange); !mpt::contains(instruments, instr)) instruments.push_back(instr); keyRange = 0xFFFF; break; case SF2_GEN_KEYRANGE: keyRange = gen.genAmount; break; case SF2_GEN_ATTENUATION: instrAttenuation = -value; break; case SF2_GEN_COARSETUNE: instrFinetune += value * 128; break; case SF2_GEN_FINETUNE: instrFinetune += static_cast(Util::muldiv(static_cast(value), 128, 100)); break; #if 0 default: Log("Ins %3d: bag %3d gen %3d: ", nIns, ipbagndx, ipgenndx); Log("genoper=%d amount=0x%04X ", gen.sfGenOper, gen.genAmount); Log((pSmp->ulBank & F_INSTRUMENT_DRUMS) ? "(drum)\n" : "\n"); #endif } } } // Envelope if (!(dlsIns.ulBank & F_INSTRUMENT_DRUMS)) { m_Envelopes.push_back(dlsEnv); dlsIns.nMelodicEnv = static_cast(m_Envelopes.size()); } // Load Instrument Bags dlsIns.Regions.clear(); for(const auto [nInstrNdx, keyRange] : instruments) { if(nInstrNdx >= numInsts) continue; sf2info.insts.Seek(nInstrNdx * sizeof(SFInst)); SFInst insts[2]; sf2info.insts.ReadArray(insts); const uint32 numRegions = insts[1].wInstBagNdx - insts[0].wInstBagNdx; dlsIns.Regions.reserve(dlsIns.Regions.size() + numRegions); //Log("\nIns %3d, %2d regions:\n", nIns, pSmp->nRegions); DLSREGION globalZone{}; globalZone.uUnityNote = 0xFF; // 0xFF means undefined -> use sample root note globalZone.tuning = 100; globalZone.sFineTune = instrFinetune; globalZone.nWaveLink = Util::MaxValueOfType(globalZone.nWaveLink); if(keyRange != 0xFFFF) { globalZone.uKeyMin = static_cast(keyRange & 0xFF); globalZone.uKeyMax = static_cast(keyRange >> 8); if(globalZone.uKeyMin > globalZone.uKeyMax) std::swap(globalZone.uKeyMin, globalZone.uKeyMax); } else { globalZone.uKeyMin = 0; globalZone.uKeyMax = 127; } for(uint32 nRgn = 0; nRgn < numRegions; nRgn++) { uint32 ibagcnt = insts[0].wInstBagNdx + nRgn; if(ibagcnt >= numInstBags) break; // Create a new envelope for drums DLSENVELOPE *pDlsEnv = &dlsEnv; if(!(dlsIns.ulBank & F_INSTRUMENT_DRUMS) && dlsIns.nMelodicEnv > 0 && dlsIns.nMelodicEnv <= m_Envelopes.size()) { pDlsEnv = &m_Envelopes[dlsIns.nMelodicEnv - 1]; } DLSREGION rgn = globalZone; // Region Default Values int32 regionAttn = 0; // Load Generators sf2info.instBags.Seek(ibagcnt * sizeof(SFInstBag)); SFInstBag bags[2]; sf2info.instBags.ReadArray(bags); sf2info.instGens.Seek(bags[0].wGenNdx * sizeof(SFInstGenList)); uint16 lastOp = SF2_GEN_SAMPLEID; int32 loopStart = 0, loopEnd = 0; if(!sf2info.instGens.ReadVector(instrGenerators, bags[1].wGenNdx - bags[0].wGenNdx)) break; for(const auto &gen : instrGenerators) { uint16 value = gen.genAmount; lastOp = gen.sfGenOper; switch(gen.sfGenOper) { case SF2_GEN_KEYRANGE: { uint8 keyMin = static_cast(value & 0xFF); uint8 keyMax = static_cast(value >> 8); if(keyMin > keyMax) std::swap(keyMin, keyMax); rgn.uKeyMin = std::max(rgn.uKeyMin, keyMin); rgn.uKeyMax = std::min(rgn.uKeyMax, keyMax); // There was no overlap between instrument region and preset region - skip it if(rgn.uKeyMin > rgn.uKeyMax) rgn.uKeyMin = rgn.uKeyMax = 0xFF; } break; case SF2_GEN_UNITYNOTE: if (value < 128) rgn.uUnityNote = static_cast(value); break; case SF2_GEN_ATTACKVOLENV: pDlsEnv->wVolAttack = SF2TimeToDLS(gen.genAmount); break; case SF2_GEN_DECAYVOLENV: pDlsEnv->wVolDecay = SF2TimeToDLS(gen.genAmount); break; case SF2_GEN_SUSTAINVOLENV: // 0.1% units if(gen.genAmount >= 0) { pDlsEnv->nVolSustainLevel = SF2SustainLevelToLinear(gen.genAmount); } break; case SF2_GEN_RELEASEVOLENV: pDlsEnv->wVolRelease = SF2TimeToDLS(gen.genAmount); break; case SF2_GEN_PAN: { int32 pan = static_cast(value); pan = std::clamp(Util::muldivr(pan + 500, 256, 1000), 0, 256); rgn.panning = static_cast(pan); pDlsEnv->nDefPan = mpt::saturate_cast(pan); } break; case SF2_GEN_ATTENUATION: regionAttn = -static_cast(value); break; case SF2_GEN_SAMPLEID: if (value < m_SamplesEx.size()) { rgn.nWaveLink = value; rgn.ulLoopStart = mpt::saturate_cast(m_SamplesEx[value].dwStartloop + loopStart); rgn.ulLoopEnd = mpt::saturate_cast(m_SamplesEx[value].dwEndloop + loopEnd); } break; case SF2_GEN_SAMPLEMODES: value &= 3; rgn.fuOptions &= uint16(~(DLSREGION_SAMPLELOOP|DLSREGION_PINGPONGLOOP|DLSREGION_SUSTAINLOOP)); if(value == 1) rgn.fuOptions |= DLSREGION_SAMPLELOOP; else if(value == 2) rgn.fuOptions |= DLSREGION_SAMPLELOOP | DLSREGION_PINGPONGLOOP; else if(value == 3) rgn.fuOptions |= DLSREGION_SAMPLELOOP | DLSREGION_SUSTAINLOOP; rgn.fuOptions |= DLSREGION_OVERRIDEWSMP; break; case SF2_GEN_KEYGROUP: rgn.fuOptions |= (value & DLSREGION_KEYGROUPMASK); break; case SF2_GEN_COARSETUNE: rgn.sFineTune += static_cast(value) * 128; break; case SF2_GEN_FINETUNE: rgn.sFineTune += static_cast(Util::muldiv(static_cast(value), 128, 100)); break; case SF2_GEN_SCALE_TUNING: rgn.tuning = mpt::saturate_cast(value); break; case SF2_GEN_START_LOOP_FINE: loopStart += static_cast(value); break; case SF2_GEN_END_LOOP_FINE: loopEnd += static_cast(value); break; case SF2_GEN_START_LOOP_COARSE: loopStart += static_cast(value) * 32768; break; case SF2_GEN_END_LOOP_COARSE: loopEnd += static_cast(value) * 32768; break; //default: // Log(" gen=%d value=%04X\n", pgen->sfGenOper, pgen->genAmount); } } int32 linearVol = DLS32BitRelativeGainToLinear(((instrAttenuation + regionAttn) * 65536) / 10) / 256; Limit(linearVol, 16, 256); rgn.usVolume = static_cast(linearVol); if(lastOp != SF2_GEN_SAMPLEID && nRgn == 0) globalZone = rgn; else if(!rgn.IsDummy()) dlsIns.Regions.push_back(rgn); //Log("\n"); } } } return true; } /////////////////////////////////////////////////////////////// // Open: opens a DLS bank bool CDLSBank::Open(const mpt::PathString &filename) { if(filename.empty()) return false; m_szFileName = filename; InputFile f(filename, SettingCacheCompleteFileBeforeLoading()); if(!f.IsValid()) return false; return Open(GetFileReader(f)); } bool CDLSBank::Open(FileReader file) { uint32 nInsDef; if(file.GetOptionalFileName()) m_szFileName = file.GetOptionalFileName().value(); file.Rewind(); size_t dwMemLength = file.GetLength(); size_t dwMemPos = 0; if(!file.CanRead(256)) { return false; } RIFFChunkID riff; file.ReadStruct(riff); // Check DLS sections embedded in RMI midi files if(riff.id_RIFF == IFFID_RIFF && riff.id_DLS == IFFID_RMID) { while(file.ReadStruct(riff)) { if(riff.id_RIFF == IFFID_RIFF && riff.id_DLS == IFFID_DLS) { file.SkipBack(sizeof(riff)); break; } uint32 len = riff.riff_len; if((len % 2u) != 0) len++; file.SkipBack(4); file.Skip(len); } } // Check XDLS sections embedded in big endian IFF files (Miles Sound System) if (riff.id_RIFF == IFFID_FORM) { do { if(riff.id_DLS == IFFID_XDLS) { file.ReadStruct(riff); break; } uint32 len = mpt::bit_cast(riff.riff_len); if((len % 2u) != 0) len++; file.SkipBack(4); file.Skip(len); } while(file.ReadStruct(riff)); } if (riff.id_RIFF != IFFID_RIFF || (riff.id_DLS != IFFID_DLS && riff.id_DLS != IFFID_MLS && riff.id_DLS != IFFID_sfbk) || !file.CanRead(riff.riff_len - 4)) { #ifdef DLSBANK_LOG MPT_LOG_GLOBAL(LogDebug, "DLSBANK", U_("Invalid DLS bank!")); #endif return false; } SF2LoaderInfo sf2info; m_nType = (riff.id_DLS == IFFID_sfbk) ? SOUNDBANK_TYPE_SF2 : SOUNDBANK_TYPE_DLS; m_dwWavePoolOffset = 0; m_sf2version = 0; m_Instruments.clear(); m_WaveForms.clear(); m_Envelopes.clear(); nInsDef = 0; if (dwMemLength > 8 + riff.riff_len + dwMemPos) dwMemLength = 8 + riff.riff_len + dwMemPos; bool applyPaddingToSampleChunk = true; while(file.CanRead(sizeof(IFFCHUNK))) { IFFCHUNK chunkHeader; file.ReadStruct(chunkHeader); dwMemPos = file.GetPosition(); FileReader chunk = file.ReadChunk(chunkHeader.len); bool applyPadding = (chunkHeader.len % 2u) != 0; if(!chunk.LengthIsAtLeast(chunkHeader.len)) break; switch(chunkHeader.id) { // DLS 1.0: Instruments Collection Header case IFFID_colh: #ifdef DLSBANK_LOG MPT_LOG_GLOBAL(LogDebug, "DLSBANK", MPT_UFORMAT("colh ({} bytes)")(chunkHeader.len.get())); #endif if (m_Instruments.empty()) { m_Instruments.resize(chunk.ReadUint32LE()); #ifdef DLSBANK_LOG MPT_LOG_GLOBAL(LogDebug, "DLSBANK", MPT_UFORMAT(" {} instruments")(m_Instruments.size())); #endif } break; // DLS 1.0: Instruments Pointers Table case IFFID_ptbl: #ifdef DLSBANK_LOG MPT_LOG_GLOBAL(LogDebug, "DLSBANK", MPT_UFORMAT("ptbl ({} bytes)")(chunkHeader.len.get())); #endif if (m_WaveForms.empty()) { PTBLChunk ptbl; chunk.ReadStruct(ptbl); chunk.Skip(ptbl.cbSize - 8); uint32 cues = std::min(ptbl.cCues.get(), mpt::saturate_cast(chunk.BytesLeft() / sizeof(uint32))); m_WaveForms.reserve(cues); for(uint32 i = 0; i < cues; i++) { m_WaveForms.push_back(chunk.ReadUint32LE()); } #ifdef DLSBANK_LOG MPT_LOG_GLOBAL(LogDebug, "DLSBANK", MPT_UFORMAT(" {} waveforms")(m_WaveForms.size())); #endif } break; // DLS 1.0: LIST section case IFFID_LIST: #ifdef DLSBANK_LOG MPT_LOG_GLOBAL(LogDebug, "DLSBANK", U_("LIST")); #endif { uint32 listid = chunk.ReadUint32LE(); if (((listid == IFFID_wvpl) && (m_nType & SOUNDBANK_TYPE_DLS)) || ((listid == IFFID_sdta) && (m_nType & SOUNDBANK_TYPE_SF2))) { m_dwWavePoolOffset = dwMemPos + 4; #ifdef DLSBANK_LOG MPT_LOG_GLOBAL(LogDebug, "DLSBANK", MPT_UFORMAT("Wave Pool offset: {}")(m_dwWavePoolOffset)); #endif if(!applyPaddingToSampleChunk) applyPadding = false; break; } while (chunk.CanRead(12)) { IFFCHUNK listHeader; chunk.ReadStruct(listHeader); if(!chunk.CanRead(listHeader.len)) break; FileReader subData = chunk.GetChunkAt(chunk.GetPosition() - sizeof(IFFCHUNK), listHeader.len + 8); FileReader listChunk = chunk.ReadChunk(listHeader.len); if(listHeader.len % 2u) chunk.Skip(1); // DLS Instrument Headers if (listHeader.id == IFFID_LIST && (m_nType & SOUNDBANK_TYPE_DLS)) { uint32 subID = listChunk.ReadUint32LE(); if ((subID == IFFID_ins) && (nInsDef < m_Instruments.size())) { DLSINSTRUMENT &dlsIns = m_Instruments[nInsDef]; //Log("Instrument %d:\n", nInsDef); dlsIns.Regions.push_back({}); UpdateInstrumentDefinition(&dlsIns, subData); nInsDef++; } } else // DLS/SF2 Bank Information if (listid == IFFID_INFO && listHeader.len) { switch(listHeader.id) { case IFFID_ifil: m_sf2version = listChunk.ReadUint16LE() << 16; m_sf2version |= listChunk.ReadUint16LE(); if(m_sf2version >= 0x3'0000 && m_sf2version <= 0x4'FFFF) { // "SF3" / "SF4" with compressed samples. The padding of the sample chunk is now optional (probably because it was simply forgotten to be added) applyPaddingToSampleChunk = false; } listChunk.Skip(2); break; case IFFID_INAM: listChunk.ReadString(m_BankInfo.szBankName, listChunk.BytesLeft()); break; case IFFID_IENG: listChunk.ReadString(m_BankInfo.szEngineer, listChunk.BytesLeft()); break; case IFFID_ICOP: listChunk.ReadString(m_BankInfo.szCopyRight, listChunk.BytesLeft()); break; case IFFID_ICMT: listChunk.ReadString(m_BankInfo.szComments, listChunk.BytesLeft()); break; case IFFID_ISFT: listChunk.ReadString(m_BankInfo.szSoftware, listChunk.BytesLeft()); break; case IFFID_ISBJ: listChunk.ReadString(m_BankInfo.szDescription, listChunk.BytesLeft()); break; } } else if ((listid == IFFID_pdta) && (m_nType & SOUNDBANK_TYPE_SF2)) { UpdateSF2PresetData(sf2info, listHeader, listChunk); } } } break; #ifdef DLSBANK_LOG default: { char sdbg[5]; memcpy(sdbg, &chunkHeader.id, 4); sdbg[4] = 0; MPT_LOG_GLOBAL(LogDebug, "DLSBANK", MPT_UFORMAT("Unsupported chunk: {} ({} bytes)")(mpt::ToUnicode(mpt::Charset::ASCII, mpt::String::ReadAutoBuf(sdbg)), chunkHeader.len.get())); } break; #endif } if(applyPadding) file.Skip(1); } // Build the ptbl is not present in file if ((m_WaveForms.empty()) && (m_dwWavePoolOffset) && (m_nType & SOUNDBANK_TYPE_DLS) && (m_nMaxWaveLink > 0)) { #ifdef DLSBANK_LOG MPT_LOG_GLOBAL(LogDebug, "DLSBANK", MPT_UFORMAT("ptbl not present: building table ({} wavelinks)...")(m_nMaxWaveLink)); #endif m_WaveForms.reserve(m_nMaxWaveLink); file.Seek(m_dwWavePoolOffset); while(m_WaveForms.size() < m_nMaxWaveLink && file.CanRead(sizeof(IFFCHUNK))) { IFFCHUNK chunk; file.ReadStruct(chunk); if (chunk.id == IFFID_LIST) m_WaveForms.push_back(file.GetPosition() - m_dwWavePoolOffset - sizeof(IFFCHUNK)); file.Skip(chunk.len); } #ifdef DLSBANK_LOG MPT_LOG_GLOBAL(LogDebug, "DLSBANK", MPT_UFORMAT("Found {} waveforms")(m_WaveForms.size())); #endif } // Convert the SF2 data to DLS if ((m_nType & SOUNDBANK_TYPE_SF2) && !m_SamplesEx.empty() && !m_Instruments.empty()) { ConvertSF2ToDLS(sf2info); } #ifdef DLSBANK_LOG MPT_LOG_GLOBAL(LogDebug, "DLSBANK", U_("DLS bank closed")); #endif return true; } //////////////////////////////////////////////////////////////////////////////////////// // Extracts the Waveforms from a DLS/SF2 bank uint32 CDLSBank::GetRegionFromKey(uint32 nIns, uint32 nKey) const { if(nIns >= m_Instruments.size()) return 0; const DLSINSTRUMENT &dlsIns = m_Instruments[nIns]; for(uint32 rgn = 0; rgn < static_cast(dlsIns.Regions.size()); rgn++) { const auto ®ion = dlsIns.Regions[rgn]; if(nKey < region.uKeyMin || nKey > region.uKeyMax) continue; if(region.nWaveLink == Util::MaxValueOfType(region.nWaveLink)) continue; return rgn; } return 0; } bool CDLSBank::ExtractWaveForm(uint32 nIns, uint32 nRgn, std::vector &waveData, uint32 &length) const { waveData.clear(); length = 0; if (nIns >= m_Instruments.size() || !m_dwWavePoolOffset) { #ifdef DLSBANK_LOG MPT_LOG_GLOBAL(LogDebug, "DLSBANK", MPT_UFORMAT("ExtractWaveForm({}) failed: m_Instruments.size()={} m_dwWavePoolOffset={} m_WaveForms.size()={}")(nIns, m_Instruments.size(), m_dwWavePoolOffset, m_WaveForms.size())); #endif return false; } const DLSINSTRUMENT &dlsIns = m_Instruments[nIns]; if(nRgn >= dlsIns.Regions.size()) { #ifdef DLSBANK_LOG MPT_LOG_GLOBAL(LogDebug, "DLSBANK", MPT_UFORMAT("invalid waveform region: nIns={} nRgn={} pSmp->nRegions={}")(nIns, nRgn, dlsIns.Regions.size())); #endif return false; } uint32 nWaveLink = dlsIns.Regions[nRgn].nWaveLink; if(nWaveLink >= m_WaveForms.size()) { #ifdef DLSBANK_LOG MPT_LOG_GLOBAL(LogDebug, "DLSBANK", MPT_UFORMAT("Invalid wavelink id: nWaveLink={} nWaveForms={}")(nWaveLink, m_WaveForms.size())); #endif return false; } mpt::ifstream f(m_szFileName, std::ios::binary); if(!f) { return false; } mpt::IO::Offset sampleOffset = mpt::saturate_cast(m_WaveForms[nWaveLink] + m_dwWavePoolOffset); if(mpt::IO::SeekAbsolute(f, sampleOffset)) { if (m_nType & SOUNDBANK_TYPE_SF2) { if (m_SamplesEx[nWaveLink].dwLen) { if (mpt::IO::SeekRelative(f, 8)) { length = m_SamplesEx[nWaveLink].dwLen; try { waveData.assign(length + 8, 0); mpt::IO::ReadRaw(f, waveData.data(), length); } catch(mpt::out_of_memory e) { mpt::delete_out_of_memory(e); } } } } else { LISTChunk chunk; if(mpt::IO::Read(f, chunk)) { if((chunk.id == IFFID_LIST) && (chunk.listid == IFFID_wave) && (chunk.len > 4)) { length = chunk.len + 8; try { waveData.assign(chunk.len + sizeof(IFFCHUNK), 0); memcpy(waveData.data(), &chunk, sizeof(chunk)); mpt::IO::ReadRaw(f, &waveData[sizeof(chunk)], length - sizeof(chunk)); } catch(mpt::out_of_memory e) { mpt::delete_out_of_memory(e); } } } } } return !waveData.empty(); } bool CDLSBank::ExtractSample(CSoundFile &sndFile, SAMPLEINDEX nSample, uint32 nIns, uint32 nRgn, int transpose) const { std::vector pWaveForm; uint32 dwLen = 0; bool ok, hasWaveform; if(nIns >= m_Instruments.size()) return false; const DLSINSTRUMENT &dlsIns = m_Instruments[nIns]; if(nRgn >= dlsIns.Regions.size()) return false; if(!ExtractWaveForm(nIns, nRgn, pWaveForm, dwLen)) return false; if(dwLen < 16) return false; ok = false; FileReader wsmpChunk; if (m_nType & SOUNDBANK_TYPE_SF2) { sndFile.DestroySample(nSample); uint32 nWaveLink = dlsIns.Regions[nRgn].nWaveLink; ModSample &sample = sndFile.GetSample(nSample); if (sndFile.m_nSamples < nSample) sndFile.m_nSamples = nSample; if (nWaveLink < m_SamplesEx.size()) { const DLSSAMPLEEX &p = m_SamplesEx[nWaveLink]; #ifdef DLSINSTR_LOG MPT_LOG_GLOBAL(LogDebug, "DLSINSTR", MPT_UFORMAT(" SF2 WaveLink #{}: {}Hz")(nWaveLink, p.dwSampleRate)); #endif sample.Initialize(); FileReader chunk{mpt::as_span(pWaveForm.data(), dwLen)}; if(!p.compressed || !sndFile.ReadSampleFromFile(nSample, chunk, false, false)) { sample.nLength = dwLen / 2; SampleIO( SampleIO::_16bit, SampleIO::mono, SampleIO::littleEndian, SampleIO::signedPCM) .ReadSample(sample, chunk); } sample.nLoopStart = dlsIns.Regions[nRgn].ulLoopStart; sample.nLoopEnd = dlsIns.Regions[nRgn].ulLoopEnd; sample.nC5Speed = p.dwSampleRate; sample.RelativeTone = p.byOriginalPitch; sample.nFineTune = p.chPitchCorrection; if(p.szName[0]) sndFile.m_szNames[nSample] = mpt::String::ReadAutoBuf(p.szName); else if(dlsIns.szName[0]) sndFile.m_szNames[nSample] = mpt::String::ReadAutoBuf(dlsIns.szName); } hasWaveform = sample.HasSampleData(); } else { FileReader file(mpt::as_span(pWaveForm.data(), dwLen)); hasWaveform = sndFile.ReadWAVSample(nSample, file, false, &wsmpChunk); if(dlsIns.szName[0]) sndFile.m_szNames[nSample] = mpt::String::ReadAutoBuf(dlsIns.szName); } if (hasWaveform) { ModSample &sample = sndFile.GetSample(nSample); const DLSREGION &rgn = dlsIns.Regions[nRgn]; sample.uFlags.reset(CHN_LOOP | CHN_PINGPONGLOOP | CHN_SUSTAINLOOP | CHN_PINGPONGSUSTAIN); if (rgn.fuOptions & DLSREGION_SAMPLELOOP) sample.uFlags.set(CHN_LOOP); if (rgn.fuOptions & DLSREGION_SUSTAINLOOP) sample.uFlags.set(CHN_SUSTAINLOOP); if (rgn.fuOptions & DLSREGION_PINGPONGLOOP) sample.uFlags.set(CHN_PINGPONGLOOP); if (sample.uFlags[CHN_LOOP | CHN_SUSTAINLOOP]) { if (rgn.ulLoopEnd > rgn.ulLoopStart) { if (sample.uFlags[CHN_SUSTAINLOOP]) { sample.nSustainStart = rgn.ulLoopStart; sample.nSustainEnd = rgn.ulLoopEnd; } else { sample.nLoopStart = rgn.ulLoopStart; sample.nLoopEnd = rgn.ulLoopEnd; } } else { sample.uFlags.reset(CHN_LOOP|CHN_SUSTAINLOOP); } } // WSMP chunk { uint32 usUnityNote = rgn.uUnityNote; int sFineTune = rgn.sFineTune; int lVolume = rgn.usVolume; WSMPChunk wsmp; if(!(rgn.fuOptions & DLSREGION_OVERRIDEWSMP) && wsmpChunk.IsValid() && wsmpChunk.Skip(sizeof(IFFCHUNK)) && wsmpChunk.ReadStructPartial(wsmp)) { usUnityNote = wsmp.usUnityNote; sFineTune = wsmp.sFineTune; lVolume = DLS32BitRelativeGainToLinear(wsmp.lAttenuation) / 256; if(wsmp.cSampleLoops) { WSMPSampleLoop loop; wsmpChunk.Seek(sizeof(IFFCHUNK) + wsmp.cbSize); wsmpChunk.ReadStruct(loop); if(loop.ulLoopLength > 3) { sample.uFlags.set(CHN_LOOP); //if (loop.ulLoopType) sample.uFlags |= CHN_PINGPONGLOOP; sample.nLoopStart = loop.ulLoopStart; sample.nLoopEnd = loop.ulLoopStart + loop.ulLoopLength; } } } else if (m_nType & SOUNDBANK_TYPE_SF2) { usUnityNote = (usUnityNote < 0x80) ? usUnityNote : sample.RelativeTone; sFineTune += sample.nFineTune; } #ifdef DLSINSTR_LOG MPT_LOG_GLOBAL(LogDebug, "DLSINSTR", MPT_UFORMAT("WSMP: usUnityNote={}.{}, {}Hz (transp={})")(usUnityNote, sFineTune, sample.nC5Speed, transpose)); #endif if (usUnityNote > 0x7F) usUnityNote = 60; int steps = (60 + transpose - usUnityNote) * 128 + sFineTune; sample.Transpose(steps * (1.0 / (12.0 * 128.0))); sample.RelativeTone = 0; Limit(lVolume, 16, 256); sample.nGlobalVol = (uint8)(lVolume / 4); // 0-64 } sample.nPan = GetPanning(nIns, nRgn); sample.Convert(MOD_TYPE_IT, sndFile.GetType()); sample.PrecomputeLoops(sndFile, false); ok = true; } return ok; } static uint16 ScaleEnvelope(uint32 time, float tempoScale) { return std::max(mpt::saturate_round(time * tempoScale), uint16(1)); } bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, uint32 nIns, uint32 nDrumRgn) const { uint32 minRegion, maxRegion, nEnv; if (nIns >= m_Instruments.size()) return false; const DLSINSTRUMENT &dlsIns = m_Instruments[nIns]; const bool isDrum = (dlsIns.ulBank & F_INSTRUMENT_DRUMS) && nDrumRgn != uint32_max; if(isDrum) { if(nDrumRgn >= dlsIns.Regions.size()) return false; minRegion = nDrumRgn; maxRegion = nDrumRgn + 1; nEnv = dlsIns.Regions[nDrumRgn].uPercEnv; } else { if(dlsIns.Regions.empty()) return false; minRegion = 0; maxRegion = static_cast(dlsIns.Regions.size()); nEnv = dlsIns.nMelodicEnv; } #ifdef DLSINSTR_LOG MPT_LOG_GLOBAL(LogDebug, "DLSINSTR", MPT_UFORMAT("DLS Instrument #{}: {}")(nIns, mpt::ToUnicode(mpt::Charset::ASCII, mpt::String::ReadAutoBuf(dlsIns.szName)))); MPT_LOG_GLOBAL(LogDebug, "DLSINSTR", MPT_UFORMAT(" Bank=0x{} Instrument=0x{}")(mpt::ufmt::HEX0<4>(dlsIns.ulBank), mpt::ufmt::HEX0<4>(dlsIns.ulInstrument))); MPT_LOG_GLOBAL(LogDebug, "DLSINSTR", MPT_UFORMAT(" {} regions, nMelodicEnv={}")(dlsIns.Regions.size(), dlsIns.nMelodicEnv)); for (uint32 iDbg=0; iDbgnWaveLink, prgn->ulLoopStart, prgn->ulLoopEnd)); MPT_LOG_GLOBAL(LogDebug, "DLSINSTR", MPT_UFORMAT(" Key Range: [{}, {}]")(prgn->uKeyMin, prgn->uKeyMax)); MPT_LOG_GLOBAL(LogDebug, "DLSINSTR", MPT_UFORMAT(" fuOptions = 0x{}")(mpt::ufmt::HEX0<4>(prgn->fuOptions))); MPT_LOG_GLOBAL(LogDebug, "DLSINSTR", MPT_UFORMAT(" usVolume = {}, Unity Note = {}")(prgn->usVolume, prgn->uUnityNote)); } #endif ModInstrument *pIns = new (std::nothrow) ModInstrument(); if(pIns == nullptr) { return false; } if(sndFile.Instruments[nInstr]) { sndFile.DestroyInstrument(nInstr, deleteAssociatedSamples); } // Initializes Instrument if(isDrum) { uint32 key = dlsIns.Regions[nDrumRgn].uKeyMin; if((key >= 24) && (key <= 84)) { std::string s = szMidiPercussionNames[key-24]; if(!mpt::String::ReadAutoBuf(dlsIns.szName).empty()) { s += MPT_AFORMAT(" ({})")(mpt::trim_right(mpt::String::ReadAutoBuf(dlsIns.szName))); } pIns->name = s; } else { pIns->name = mpt::String::ReadAutoBuf(dlsIns.szName); } } else { pIns->name = mpt::String::ReadAutoBuf(dlsIns.szName); } int transpose = 0; if(isDrum) { for(uint32 iNoteMap = 0; iNoteMap < NOTE_MAX; iNoteMap++) { if(sndFile.GetType() & (MOD_TYPE_IT | MOD_TYPE_MID | MOD_TYPE_MPT)) { // Format has instrument note mapping if(dlsIns.Regions[nDrumRgn].tuning == 0) pIns->NoteMap[iNoteMap] = NOTE_MIDDLEC; else if (iNoteMap < dlsIns.Regions[nDrumRgn].uKeyMin) pIns->NoteMap[iNoteMap] = (uint8)(dlsIns.Regions[nDrumRgn].uKeyMin + NOTE_MIN); else if(iNoteMap > dlsIns.Regions[nDrumRgn].uKeyMax) pIns->NoteMap[iNoteMap] = (uint8)(dlsIns.Regions[nDrumRgn].uKeyMax + NOTE_MIN); } else { if(iNoteMap == dlsIns.Regions[nDrumRgn].uKeyMin) { transpose = (dlsIns.Regions[nDrumRgn].uKeyMin + (dlsIns.Regions[nDrumRgn].uKeyMax - dlsIns.Regions[nDrumRgn].uKeyMin) / 2) - 60; } } } } pIns->nFadeOut = 1024; pIns->nMidiProgram = (uint8)(dlsIns.ulInstrument & 0x7F) + 1; pIns->nMidiChannel = (uint8)(isDrum ? 10 : 0); pIns->wMidiBank = (uint16)(((dlsIns.ulBank & 0x7F00) >> 1) | (dlsIns.ulBank & 0x7F)); pIns->nNNA = NewNoteAction::NoteOff; pIns->nDCT = DuplicateCheckType::Note; pIns->nDNA = DuplicateNoteAction::NoteFade; sndFile.Instruments[nInstr] = pIns; uint32 nLoadedSmp = 0; SAMPLEINDEX nextSample = 0; // Extract Samples std::vector RgnToSmp(dlsIns.Regions.size()); std::set extractedSamples; for(uint32 nRgn = minRegion; nRgn < maxRegion; nRgn++) { bool duplicateRegion = false; SAMPLEINDEX nSmp = 0; const DLSREGION &rgn = dlsIns.Regions[nRgn]; if(rgn.IsDummy()) continue; // Elimitate Duplicate Regions uint32 dupRegion; for(dupRegion = minRegion; dupRegion < nRgn; dupRegion++) { const DLSREGION &rgn2 = dlsIns.Regions[dupRegion]; if(RgnToSmp[dupRegion] == 0 || rgn2.IsDummy()) continue; // No need to extract the same sample data twice const bool sameSample = (rgn2.nWaveLink == rgn.nWaveLink) && (rgn2.ulLoopEnd == rgn.ulLoopEnd) && (rgn2.ulLoopStart == rgn.ulLoopStart) && extractedSamples.count(rgn.nWaveLink); // Candidate for stereo sample creation const bool sameKeyRange = (rgn2.uKeyMin == rgn.uKeyMin) && (rgn2.uKeyMax == rgn.uKeyMax); if(sameSample || sameKeyRange) { duplicateRegion = true; if(!sameKeyRange) nSmp = RgnToSmp[dupRegion]; break; } } // Create a new sample if (!duplicateRegion) { uint32 nmaxsmp = (m_nType & MOD_TYPE_XM) ? 16 : (NOTE_MAX - NOTE_MIN + 1); if (nLoadedSmp >= nmaxsmp) { nSmp = RgnToSmp[nRgn - 1]; } else { nextSample = sndFile.GetNextFreeSample(nInstr, nextSample + 1); if (nextSample == SAMPLEINDEX_INVALID) break; if (nextSample > sndFile.GetNumSamples()) sndFile.m_nSamples = nextSample; nSmp = nextSample; nLoadedSmp++; } } RgnToSmp[nRgn] = nSmp; // Map all notes to the right sample if(nSmp) { for(uint8 key = 0; key < NOTE_MAX; key++) { if(isDrum || (key >= rgn.uKeyMin && key <= rgn.uKeyMax)) { pIns->Keyboard[key] = nSmp; } } // Load the sample if(!duplicateRegion || !sndFile.GetSample(nSmp).HasSampleData()) { ExtractSample(sndFile, nSmp, nIns, nRgn, transpose); extractedSamples.insert(rgn.nWaveLink); } } else if(duplicateRegion && sndFile.GetSample(RgnToSmp[dupRegion]).GetNumChannels() == 1) { // Try to combine stereo samples const uint16 pan1 = GetPanning(nIns, nRgn), pan2 = GetPanning(nIns, dupRegion); if((pan1 < 16 && pan2 >= 240) || (pan2 < 16 && pan1 >= 240)) { ModSample &sample = sndFile.GetSample(RgnToSmp[dupRegion]); ModSample sampleCopy = sample; sampleCopy.pData.pSample = nullptr; sampleCopy.uFlags.set(CHN_16BIT | CHN_STEREO); if(!sampleCopy.AllocateSample()) continue; const uint8 offsetOrig = (pan1 < pan2) ? 1 : 0; const uint8 offsetNew = (pan1 < pan2) ? 0 : 1; std::vector pWaveForm; uint32 dwLen = 0; if(!ExtractWaveForm(nIns, nRgn, pWaveForm, dwLen)) continue; extractedSamples.insert(rgn.nWaveLink); // First copy over original channel auto pDest = sampleCopy.sample16() + offsetOrig; if(sample.uFlags[CHN_16BIT]) CopySample, SC::DecodeIdentity>>(pDest, sample.nLength, 2, sample.sample16(), sample.GetSampleSizeInBytes(), 1); else CopySample, SC::DecodeIdentity>>(pDest, sample.nLength, 2, sample.sample8(), sample.GetSampleSizeInBytes(), 1); sample.FreeSample(); // Now read the other channel if(m_SamplesEx[m_Instruments[nIns].Regions[nRgn].nWaveLink].compressed) { FileReader file{mpt::as_span(pWaveForm)}; if(sndFile.ReadSampleFromFile(nSmp, file, false, false)) { pDest = sampleCopy.sample16() + offsetNew; const SmpLength copyLength = std::min(sample.nLength, sampleCopy.nLength); if(sample.uFlags[CHN_16BIT]) CopySample, SC::DecodeIdentity>>(pDest, copyLength, 2, sample.sample16(), sample.GetSampleSizeInBytes(), sample.GetNumChannels()); else CopySample, SC::DecodeIdentity>>(pDest, copyLength, 2, sample.sample8(), sample.GetSampleSizeInBytes(), sample.GetNumChannels()); } } else { SmpLength len = std::min(dwLen / 2u, sampleCopy.nLength); const int16 *src = reinterpret_cast(pWaveForm.data()); int16 *dst = sampleCopy.sample16() + offsetNew; CopySample, SC::DecodeIdentity>>(dst, len, 2, src, pWaveForm.size(), 1); } sample.FreeSample(); sample = sampleCopy; } } } float tempoScale = 1.0f; if(sndFile.m_nTempoMode == TempoMode::Modern) { uint32 ticksPerBeat = sndFile.m_nDefaultRowsPerBeat * sndFile.m_nDefaultSpeed; if(ticksPerBeat != 0) tempoScale = ticksPerBeat / 24.0f; } // Initializes Envelope if ((nEnv) && (nEnv <= m_Envelopes.size())) { const DLSENVELOPE &part = m_Envelopes[nEnv - 1]; // Volume Envelope if ((part.wVolAttack) || (part.wVolDecay < 20*50) || (part.nVolSustainLevel) || (part.wVolRelease < 20*50)) { pIns->VolEnv.dwFlags.set(ENV_ENABLED); // Delay section // -> DLS level 2 // Attack section pIns->VolEnv.clear(); if (part.wVolAttack) { pIns->VolEnv.push_back(0, (uint8)(ENVELOPE_MAX / (part.wVolAttack / 2 + 2) + 8)); // /----- pIns->VolEnv.push_back(ScaleEnvelope(part.wVolAttack, tempoScale), ENVELOPE_MAX); // | } else { pIns->VolEnv.push_back(0, ENVELOPE_MAX); } // Hold section // -> DLS Level 2 // Sustain Level if (part.nVolSustainLevel > 0) { if (part.nVolSustainLevel < 128) { uint16 lStartTime = pIns->VolEnv.back().tick; int32 lSusLevel = - DLS32BitRelativeLinearToGain(part.nVolSustainLevel << 9) / 65536; int32 lDecayTime = 1; if (lSusLevel > 0) { lDecayTime = (lSusLevel * (int32)part.wVolDecay) / 960; for (uint32 i=0; i<7; i++) { int32 lFactor = 128 - (1 << i); if (lFactor <= part.nVolSustainLevel) break; int32 lev = - DLS32BitRelativeLinearToGain(lFactor << 9) / 65536; if (lev > 0) { int32 ltime = (lev * (int32)part.wVolDecay) / 960; if ((ltime > 1) && (ltime < lDecayTime)) { uint16 tick = lStartTime + ScaleEnvelope(ltime, tempoScale); if(tick > pIns->VolEnv.back().tick) { pIns->VolEnv.push_back(tick, (uint8)(lFactor / 2)); } } } } } uint16 decayEnd = lStartTime + ScaleEnvelope(lDecayTime, tempoScale); if (decayEnd > pIns->VolEnv.back().tick) { pIns->VolEnv.push_back(decayEnd, (uint8)((part.nVolSustainLevel+1) / 2)); } } pIns->VolEnv.dwFlags.set(ENV_SUSTAIN); } else { pIns->VolEnv.dwFlags.set(ENV_SUSTAIN); pIns->VolEnv.push_back(pIns->VolEnv.back().tick + 1u, pIns->VolEnv.back().value); } pIns->VolEnv.nSustainStart = pIns->VolEnv.nSustainEnd = (uint8)(pIns->VolEnv.size() - 1); // Release section if ((part.wVolRelease) && (pIns->VolEnv.back().value > 1)) { int32 lReleaseTime = part.wVolRelease; uint16 lStartTime = pIns->VolEnv.back().tick; int32 lStartFactor = pIns->VolEnv.back().value; int32 lSusLevel = - DLS32BitRelativeLinearToGain(lStartFactor << 10) / 65536; int32 lDecayEndTime = (lReleaseTime * lSusLevel) / 960; lReleaseTime -= lDecayEndTime; if(pIns->VolEnv.nSustainEnd > 0) pIns->VolEnv.nReleaseNode = pIns->VolEnv.nSustainEnd; for (uint32 i=0; i<5; i++) { int32 lFactor = 1 + ((lStartFactor * 3) >> (i+2)); if ((lFactor <= 1) || (lFactor >= lStartFactor)) continue; int32 lev = - DLS32BitRelativeLinearToGain(lFactor << 10) / 65536; if (lev > 0) { int32 ltime = (((int32)part.wVolRelease * lev) / 960) - lDecayEndTime; if ((ltime > 1) && (ltime < lReleaseTime)) { uint16 tick = lStartTime + ScaleEnvelope(ltime, tempoScale); if(tick > pIns->VolEnv.back().tick) { pIns->VolEnv.push_back(tick, (uint8)lFactor); } } } } if (lReleaseTime < 1) lReleaseTime = 1; auto releaseTicks = ScaleEnvelope(lReleaseTime, tempoScale); pIns->VolEnv.push_back(lStartTime + releaseTicks, ENVELOPE_MIN); if(releaseTicks > 0) { pIns->nFadeOut = 32768 / releaseTicks; } } else { pIns->VolEnv.push_back(pIns->VolEnv.back().tick + 1u, ENVELOPE_MIN); } } } if(isDrum) { // Create a default envelope for drums pIns->VolEnv.dwFlags.reset(ENV_SUSTAIN); if(!pIns->VolEnv.dwFlags[ENV_ENABLED]) { pIns->VolEnv.dwFlags.set(ENV_ENABLED); pIns->VolEnv.resize(4); pIns->VolEnv[0] = EnvelopeNode(0, ENVELOPE_MAX); pIns->VolEnv[1] = EnvelopeNode(ScaleEnvelope(5, tempoScale), ENVELOPE_MAX); pIns->VolEnv[2] = EnvelopeNode(pIns->VolEnv[1].tick * 2u, ENVELOPE_MID); pIns->VolEnv[3] = EnvelopeNode(pIns->VolEnv[2].tick * 2u, ENVELOPE_MIN); // 1 second max. for drums } } pIns->Sanitize(MOD_TYPE_MPT); pIns->Convert(MOD_TYPE_MPT, sndFile.GetType()); return true; } const char *CDLSBank::GetRegionName(uint32 nIns, uint32 nRgn) const { if(nIns >= m_Instruments.size()) return nullptr; const DLSINSTRUMENT &dlsIns = m_Instruments[nIns]; if(nRgn >= dlsIns.Regions.size()) return nullptr; if (m_nType & SOUNDBANK_TYPE_SF2) { uint32 nWaveLink = dlsIns.Regions[nRgn].nWaveLink; if (nWaveLink < m_SamplesEx.size()) { return m_SamplesEx[nWaveLink].szName; } } return nullptr; } uint16 CDLSBank::GetPanning(uint32 ins, uint32 region) const { const DLSINSTRUMENT &dlsIns = m_Instruments[ins]; if(region >= std::size(dlsIns.Regions)) return 128; const DLSREGION &rgn = dlsIns.Regions[region]; if(rgn.panning >= 0) return static_cast(rgn.panning); if(dlsIns.ulBank & F_INSTRUMENT_DRUMS) { if(rgn.uPercEnv > 0 && rgn.uPercEnv <= m_Envelopes.size()) { return m_Envelopes[rgn.uPercEnv - 1].nDefPan; } } else { if(dlsIns.nMelodicEnv > 0 && dlsIns.nMelodicEnv <= m_Envelopes.size()) { return m_Envelopes[dlsIns.nMelodicEnv - 1].nDefPan; } } return 128; } #else // !MODPLUG_TRACKER MPT_MSVC_WORKAROUND_LNK4221(Dlsbank) #endif // MODPLUG_TRACKER OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Dlsbank.h0000644000175000017500000001141514166574422017637 00000000000000/* * DLSBank.h * --------- * Purpose: Sound bank loading. * Notes : Supported sound bank types: DLS (including embedded DLS in MSS & RMI), SF2 * Authors: Olivier Lapicque * OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" OPENMPT_NAMESPACE_BEGIN class CSoundFile; OPENMPT_NAMESPACE_END #include "Snd_defs.h" OPENMPT_NAMESPACE_BEGIN #ifdef MODPLUG_TRACKER struct DLSREGION { uint32 ulLoopStart; uint32 ulLoopEnd; uint16 nWaveLink; uint16 uPercEnv; uint16 usVolume; // 0..256 uint16 fuOptions; // flags + key group int16 sFineTune; // +128 = +1 semitone int16 panning = -1; // -1= unset (DLS), otherwise 0...256 uint8 uKeyMin; uint8 uKeyMax; uint8 uUnityNote; uint8 tuning = 100; constexpr bool IsDummy() const noexcept { return uKeyMin == 0xFF || nWaveLink == Util::MaxValueOfType(nWaveLink); } }; struct DLSENVELOPE { // Volume Envelope uint16 wVolAttack; // Attack Time: 0-1000, 1 = 20ms (1/50s) -> [0-20s] uint16 wVolDecay; // Decay Time: 0-1000, 1 = 20ms (1/50s) -> [0-20s] uint16 wVolRelease; // Release Time: 0-1000, 1 = 20ms (1/50s) -> [0-20s] uint8 nVolSustainLevel; // Sustain Level: 0-128, 128=100% uint8 nDefPan; // Default Pan }; // Special Bank bits #define F_INSTRUMENT_DRUMS 0x80000000 struct DLSINSTRUMENT { uint32 ulBank = 0, ulInstrument = 0; uint32 nMelodicEnv = 0; std::vector Regions; char szName[32]; // SF2 stuff (DO NOT USE! -> used internally by the SF2 loader) uint16 wPresetBagNdx = 0, wPresetBagNum = 0; }; struct DLSSAMPLEEX { char szName[20]; uint32 dwLen; uint32 dwStartloop; uint32 dwEndloop; uint32 dwSampleRate; uint8 byOriginalPitch; int8 chPitchCorrection; bool compressed = false; }; #define SOUNDBANK_TYPE_INVALID 0 #define SOUNDBANK_TYPE_DLS 0x01 #define SOUNDBANK_TYPE_SF2 0x02 struct SOUNDBANKINFO { std::string szBankName, szCopyRight, szComments, szEngineer, szSoftware, // ISFT: Software szDescription; // ISBJ: Subject }; struct IFFCHUNK; struct SF2LoaderInfo; class CDLSBank { protected: SOUNDBANKINFO m_BankInfo; mpt::PathString m_szFileName; size_t m_dwWavePoolOffset; uint32 m_nType; // DLS Information uint32 m_nMaxWaveLink; uint32 m_sf2version = 0; std::vector m_WaveForms; std::vector m_Instruments; std::vector m_SamplesEx; std::vector m_Envelopes; public: CDLSBank(); bool operator==(const CDLSBank &other) const noexcept { return !mpt::PathString::CompareNoCase(m_szFileName, other.m_szFileName); } static bool IsDLSBank(const mpt::PathString &filename); static uint32 MakeMelodicCode(uint32 bank, uint32 instr) { return ((bank << 16) | (instr));} static uint32 MakeDrumCode(uint32 rgn, uint32 instr) { return (0x80000000 | (rgn << 16) | (instr));} public: bool Open(const mpt::PathString &filename); bool Open(FileReader file); mpt::PathString GetFileName() const { return m_szFileName; } uint32 GetBankType() const { return m_nType; } const SOUNDBANKINFO &GetBankInfo() const { return m_BankInfo; } public: uint32 GetNumInstruments() const { return static_cast(m_Instruments.size()); } uint32 GetNumSamples() const { return static_cast(m_WaveForms.size()); } const DLSINSTRUMENT *GetInstrument(uint32 iIns) const { return iIns < m_Instruments.size() ? &m_Instruments[iIns] : nullptr; } const DLSINSTRUMENT *FindInstrument(bool isDrum, uint32 bank = 0xFF, uint32 program = 0xFF, uint32 key = 0xFF, uint32 *pInsNo = nullptr) const; bool FindAndExtract(CSoundFile &sndFile, const INSTRUMENTINDEX ins, const bool isDrum) const; uint32 GetRegionFromKey(uint32 nIns, uint32 nKey) const; bool ExtractWaveForm(uint32 nIns, uint32 nRgn, std::vector &waveData, uint32 &length) const; bool ExtractSample(CSoundFile &sndFile, SAMPLEINDEX nSample, uint32 nIns, uint32 nRgn, int transpose = 0) const; bool ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, uint32 nIns, uint32 nDrumRgn) const; const char *GetRegionName(uint32 nIns, uint32 nRgn) const; uint16 GetPanning(uint32 ins, uint32 region) const; // Internal Loader Functions protected: bool UpdateInstrumentDefinition(DLSINSTRUMENT *pDlsIns, FileReader chunk); bool UpdateSF2PresetData(SF2LoaderInfo &sf2info, const IFFCHUNK &header, FileReader &chunk); bool ConvertSF2ToDLS(SF2LoaderInfo &sf2info); public: // DLS Unit conversion static int32 DLS32BitTimeCentsToMilliseconds(int32 lTimeCents); static int32 DLS32BitRelativeGainToLinear(int32 lCentibels); // 0dB = 0x10000 static int32 DLS32BitRelativeLinearToGain(int32 lGain); // 0dB = 0x10000 static int32 DLSMidiVolumeToLinear(uint32 nMidiVolume); // [0-127] -> [0-0x10000] }; #endif // MODPLUG_TRACKER OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Fastmix.cpp0000644000175000017500000005720614175042045020226 00000000000000/* * Fastmix.cpp * ----------- * Purpose: Mixer core for rendering samples, mixing plugins, etc... * Notes : If this is Fastmix.cpp, where is Slowmix.cpp? :) * Authors: Olivier Lapicque * OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ // FIXME: // - Playing samples backwards should reverse interpolation LUTs for interpolation modes // with more than two taps since they're not symmetric. We might need separate LUTs // because otherwise we will add tons of branches. // - Loop wraparound works pretty well in general, but not at the start of bidi samples. // - The loop lookahead stuff might still fail for samples with backward loops. #include "stdafx.h" #include "Sndfile.h" #include "MixerLoops.h" #include "MixFuncTable.h" #include "plugins/PlugInterface.h" #include // For FLT_EPSILON #include OPENMPT_NAMESPACE_BEGIN ///////////////////////////////////////////////////////////////////////// struct MixLoopState { const int8 * samplePointer = nullptr; const int8 * lookaheadPointer = nullptr; SmpLength lookaheadStart = 0; uint32 maxSamples = 0; const uint8 ITPingPongDiff; const bool precisePingPongLoops; MixLoopState(const CSoundFile &sndFile, const ModChannel &chn) : ITPingPongDiff{sndFile.m_playBehaviour[kITPingPongMode] ? uint8(1) : uint8(0)} , precisePingPongLoops{!sndFile.m_playBehaviour[kImprecisePingPongLoops]} { if(chn.pCurrentSample == nullptr) return; UpdateLookaheadPointers(chn); // For platforms that have no fast 64-bit division, precompute this constant // as it won't change during the invocation of CreateStereoMix. SamplePosition increment = chn.increment; if(increment.IsNegative()) increment.Negate(); maxSamples = 16384u / (increment.GetUInt() + 1u); if(maxSamples < 2) maxSamples = 2; } // Calculate offset of loop wrap-around buffer for this sample. void UpdateLookaheadPointers(const ModChannel &chn) { samplePointer = static_cast(chn.pCurrentSample); lookaheadPointer = nullptr; if(!samplePointer) return; if(chn.nLoopEnd < InterpolationLookaheadBufferSize) lookaheadStart = chn.nLoopStart; else lookaheadStart = std::max(chn.nLoopStart, chn.nLoopEnd - InterpolationLookaheadBufferSize); // We only need to apply the loop wrap-around logic if the sample is actually looping and if interpolation is applied. // If there is no interpolation happening, there is no lookahead happening the sample read-out is exact. if(chn.dwFlags[CHN_LOOP] && chn.resamplingMode != SRCMODE_NEAREST) { const bool inSustainLoop = chn.InSustainLoop() && chn.nLoopStart == chn.pModSample->nSustainStart && chn.nLoopEnd == chn.pModSample->nSustainEnd; // Do not enable wraparound magic if we're previewing a custom loop! if(inSustainLoop || chn.nLoopEnd == chn.pModSample->nLoopEnd) { SmpLength lookaheadOffset = 3 * InterpolationLookaheadBufferSize + chn.pModSample->nLength - chn.nLoopEnd; if(inSustainLoop) { lookaheadOffset += 4 * InterpolationLookaheadBufferSize; } lookaheadPointer = samplePointer + lookaheadOffset * chn.pModSample->GetBytesPerSample(); } } } // Returns the buffer length required to render a certain amount of samples, based on the channel's playback speed. static MPT_FORCEINLINE uint32 DistanceToBufferLength(SamplePosition from, SamplePosition to, SamplePosition inc) { return static_cast((to - from - SamplePosition(1)) / inc) + 1; } // Check how many samples can be rendered without encountering loop or sample end, and also update loop position / direction MPT_FORCEINLINE uint32 GetSampleCount(ModChannel &chn, uint32 nSamples) const { int32 nLoopStart = chn.dwFlags[CHN_LOOP] ? chn.nLoopStart : 0; SamplePosition nInc = chn.increment; if(nSamples <= 0 || nInc.IsZero() || !chn.nLength || !samplePointer) return 0; // Part 1: Making sure the play position is valid, and if necessary, invert the play direction in case we reached a loop boundary of a ping-pong loop. chn.pCurrentSample = samplePointer; // Under zero ? if (chn.position.GetInt() < nLoopStart) { if (nInc.IsNegative()) { // Invert loop direction for bidi loops chn.position = SamplePosition(nLoopStart + nLoopStart, 0) - chn.position; if ((chn.position.GetInt() < nLoopStart) || (chn.position.GetUInt() >= (nLoopStart + chn.nLength) / 2)) { chn.position.Set(nLoopStart, 0); } if(chn.dwFlags[CHN_PINGPONGLOOP]) { chn.dwFlags.reset(CHN_PINGPONGFLAG); // go forward nInc.Negate(); chn.increment = nInc; } else { chn.position.SetInt(chn.nLength - 1); } if(!chn.dwFlags[CHN_LOOP] || chn.position.GetUInt() >= chn.nLength) { chn.position.Set(chn.nLength); return 0; } } else { // We probably didn't hit the loop end yet (first loop), so we do nothing if (chn.position.GetInt() < 0) chn.position.SetInt(0); } } else if (chn.position.GetUInt() >= chn.nLength) { // Past the end if(!chn.dwFlags[CHN_LOOP]) return 0; // not looping -> stop this channel if(chn.dwFlags[CHN_PINGPONGLOOP]) { // Invert loop if (nInc.IsPositive()) { nInc.Negate(); chn.increment = nInc; } chn.dwFlags.set(CHN_PINGPONGFLAG); // Adjust loop position if(precisePingPongLoops) { // More accurate loop end overshoot calculation. // Test cases: BidiPrecision.it, BidiPrecision.xm const auto overshoot = chn.position - SamplePosition(chn.nLength, 0); const auto loopLength = chn.nLoopEnd - chn.nLoopStart - ITPingPongDiff; if(overshoot.GetUInt() < loopLength) chn.position = SamplePosition(chn.nLength - ITPingPongDiff, 0) - overshoot; else chn.position = SamplePosition(chn.nLoopStart, 0); } else { SamplePosition invFract = chn.position.GetInvertedFract(); chn.position = SamplePosition(chn.nLength - (chn.position.GetInt() - chn.nLength) - invFract.GetInt(), invFract.GetFract()); if(chn.position.GetUInt() <= chn.nLoopStart || chn.position.GetUInt() >= chn.nLength) { // Impulse Tracker's software mixer would put a -2 (instead of -1) in the following line (doesn't happen on a GUS) chn.position.SetInt(chn.nLength - std::min(chn.nLength, static_cast(ITPingPongDiff + 1))); } } } else { if (nInc.IsNegative()) // This is a bug { nInc.Negate(); chn.increment = nInc; } // Restart at loop start chn.position += SamplePosition(nLoopStart - chn.nLength, 0); MPT_ASSERT(chn.position.GetInt() >= nLoopStart); // Interpolate correctly after wrapping around chn.dwFlags.set(CHN_WRAPPED_LOOP); } } // Part 2: Compute how many samples we can render until we reach the end of sample / loop boundary / etc. SamplePosition nPos = chn.position; // too big increment, and/or too small loop length if (nPos.GetInt() < nLoopStart) { if (nPos.IsNegative() || nInc.IsNegative()) return 0; } if (nPos.IsNegative() || nPos.GetUInt() >= chn.nLength) return 0; uint32 nSmpCount = nSamples; SamplePosition nInv = nInc; if (nInc.IsNegative()) { nInv.Negate(); } LimitMax(nSamples, maxSamples); SamplePosition incSamples = nInc * (nSamples - 1); int32 nPosDest = (nPos + incSamples).GetInt(); const SmpLength nPosInt = nPos.GetUInt(); const bool isAtLoopStart = (nPosInt >= chn.nLoopStart && nPosInt < chn.nLoopStart + InterpolationLookaheadBufferSize); if(!isAtLoopStart) { chn.dwFlags.reset(CHN_WRAPPED_LOOP); } // Loop wrap-around magic. bool checkDest = true; if(lookaheadPointer != nullptr) { if(nPos.GetUInt() >= lookaheadStart) { #if 0 const uint32 oldCount = nSmpCount; // When going backwards - we can only go back up to lookaheadStart. // When going forwards - read through the whole pre-computed wrap-around buffer if possible. // TODO: ProTracker sample swapping needs hard cut at sample end. int32 samplesToRead = nInc.IsNegative() ? (nPosInt - lookaheadStart) //: 2 * InterpolationMaxLookahead - (nPosInt - mixLoopState.lookaheadStart); : (chn.nLoopEnd - nPosInt); //LimitMax(samplesToRead, chn.nLoopEnd - chn.nLoopStart); nSmpCount = SamplesToBufferLength(samplesToRead, chn); Limit(nSmpCount, 1u, oldCount); #else if (nInc.IsNegative()) { nSmpCount = DistanceToBufferLength(SamplePosition(lookaheadStart, 0), nPos, nInv); } else { nSmpCount = DistanceToBufferLength(nPos, SamplePosition(chn.nLoopEnd, 0), nInv); } #endif chn.pCurrentSample = lookaheadPointer; checkDest = false; } else if(chn.dwFlags[CHN_WRAPPED_LOOP] && isAtLoopStart) { // We just restarted the loop, so interpolate correctly after wrapping around nSmpCount = DistanceToBufferLength(nPos, SamplePosition(nLoopStart + InterpolationLookaheadBufferSize, 0), nInv); chn.pCurrentSample = lookaheadPointer + (chn.nLoopEnd - nLoopStart) * chn.pModSample->GetBytesPerSample(); checkDest = false; } else if(nInc.IsPositive() && static_cast(nPosDest) >= lookaheadStart && nSmpCount > 1) { // We shouldn't read that far if we're not using the pre-computed wrap-around buffer. nSmpCount = DistanceToBufferLength(nPos, SamplePosition(lookaheadStart, 0), nInv); checkDest = false; } } if(checkDest) { // Fix up sample count if target position is invalid if (nInc.IsNegative()) { if (nPosDest < nLoopStart) { nSmpCount = DistanceToBufferLength(SamplePosition(nLoopStart, 0), nPos, nInv); } } else { if (nPosDest >= (int32)chn.nLength) { nSmpCount = DistanceToBufferLength(nPos, SamplePosition(chn.nLength, 0), nInv); } } } Limit(nSmpCount, uint32(1u), nSamples); #ifdef MPT_BUILD_DEBUG { SmpLength posDest = (nPos + nInc * (nSmpCount - 1)).GetUInt(); if (posDest < 0 || posDest > chn.nLength) { // We computed an invalid delta! MPT_ASSERT_NOTREACHED(); return 0; } } #endif return nSmpCount; } }; // Render count * number of channels samples void CSoundFile::CreateStereoMix(int count) { mixsample_t *pOfsL, *pOfsR; if(!count) return; // Resetting sound buffer StereoFill(MixSoundBuffer, count, m_dryROfsVol, m_dryLOfsVol); if(m_MixerSettings.gnChannels > 2) StereoFill(MixRearBuffer, count, m_surroundROfsVol, m_surroundLOfsVol); CHANNELINDEX nchmixed = 0; for(uint32 nChn = 0; nChn < m_nMixChannels; nChn++) { ModChannel &chn = m_PlayState.Chn[m_PlayState.ChnMix[nChn]]; if(!chn.pCurrentSample && !chn.nLOfs && !chn.nROfs) continue; pOfsR = &m_dryROfsVol; pOfsL = &m_dryLOfsVol; uint32 functionNdx = MixFuncTable::ResamplingModeToMixFlags(static_cast(chn.resamplingMode)); if(chn.dwFlags[CHN_16BIT]) functionNdx |= MixFuncTable::ndx16Bit; if(chn.dwFlags[CHN_STEREO]) functionNdx |= MixFuncTable::ndxStereo; #ifndef NO_FILTER if(chn.dwFlags[CHN_FILTER]) functionNdx |= MixFuncTable::ndxFilter; #endif mixsample_t *pbuffer = MixSoundBuffer; #ifndef NO_REVERB if(((m_MixerSettings.DSPMask & SNDDSP_REVERB) && !chn.dwFlags[CHN_NOREVERB]) || chn.dwFlags[CHN_REVERB]) { m_Reverb.TouchReverbSendBuffer(ReverbSendBuffer, m_RvbROfsVol, m_RvbLOfsVol, count); pbuffer = ReverbSendBuffer; pOfsR = &m_RvbROfsVol; pOfsL = &m_RvbLOfsVol; } #endif if(chn.dwFlags[CHN_SURROUND] && m_MixerSettings.gnChannels > 2) { pbuffer = MixRearBuffer; pOfsR = &m_surroundROfsVol; pOfsL = &m_surroundLOfsVol; } //Look for plugins associated with this implicit tracker channel. #ifndef NO_PLUGINS PLUGINDEX nMixPlugin = GetBestPlugin(m_PlayState, m_PlayState.ChnMix[nChn], PrioritiseInstrument, RespectMutes); if ((nMixPlugin > 0) && (nMixPlugin <= MAX_MIXPLUGINS) && m_MixPlugins[nMixPlugin - 1].pMixPlugin != nullptr) { // Render into plugin buffer instead of global buffer SNDMIXPLUGINSTATE &mixState = m_MixPlugins[nMixPlugin - 1].pMixPlugin->m_MixState; if (mixState.pMixBuffer) { pbuffer = mixState.pMixBuffer; pOfsR = &mixState.nVolDecayR; pOfsL = &mixState.nVolDecayL; if (!(mixState.dwFlags & SNDMIXPLUGINSTATE::psfMixReady)) { StereoFill(pbuffer, count, *pOfsR, *pOfsL); mixState.dwFlags |= SNDMIXPLUGINSTATE::psfMixReady; } } } #endif // NO_PLUGINS if(chn.isPaused) { EndChannelOfs(chn, pbuffer, count); *pOfsR += chn.nROfs; *pOfsL += chn.nLOfs; chn.nROfs = chn.nLOfs = 0; continue; } MixLoopState mixLoopState(*this, chn); //////////////////////////////////////////////////// CHANNELINDEX naddmix = 0; int nsamples = count; // Keep mixing this sample until the buffer is filled. do { uint32 nrampsamples = nsamples; int32 nSmpCount; if(chn.nRampLength > 0) { if (nrampsamples > chn.nRampLength) nrampsamples = chn.nRampLength; } if((nSmpCount = mixLoopState.GetSampleCount(chn, nrampsamples)) <= 0) { // Stopping the channel chn.pCurrentSample = nullptr; chn.nLength = 0; chn.position.Set(0); chn.nRampLength = 0; EndChannelOfs(chn, pbuffer, nsamples); *pOfsR += chn.nROfs; *pOfsL += chn.nLOfs; chn.nROfs = chn.nLOfs = 0; chn.dwFlags.reset(CHN_PINGPONGFLAG); break; } // Should we mix this channel ? if((nchmixed >= m_MixerSettings.m_nMaxMixChannels) // Too many channels || (!chn.nRampLength && !(chn.leftVol | chn.rightVol))) // Channel is completely silent { chn.position += chn.increment * nSmpCount; chn.nROfs = chn.nLOfs = 0; pbuffer += nSmpCount * 2; naddmix = 0; } #ifdef MODPLUG_TRACKER else if(m_SamplePlayLengths != nullptr) { // Detecting the longest play time for each sample for optimization SmpLength pos = chn.position.GetUInt(); chn.position += chn.increment * nSmpCount; if(!chn.increment.IsNegative()) { pos = chn.position.GetUInt(); } size_t smp = std::distance(static_cast(static_cast::type>(Samples)), chn.pModSample); if(smp < m_SamplePlayLengths->size()) { (*m_SamplePlayLengths)[smp] = std::max((*m_SamplePlayLengths)[smp], pos); } } #endif else { // Do mixing mixsample_t *pbufmax = pbuffer + (nSmpCount * 2); chn.nROfs = -*(pbufmax - 2); chn.nLOfs = -*(pbufmax - 1); #ifdef MPT_BUILD_DEBUG SamplePosition targetpos = chn.position + chn.increment * nSmpCount; #endif MixFuncTable::Functions[functionNdx | (chn.nRampLength ? MixFuncTable::ndxRamp : 0)](chn, m_Resampler, pbuffer, nSmpCount); #ifdef MPT_BUILD_DEBUG MPT_ASSERT(chn.position.GetUInt() == targetpos.GetUInt()); #endif chn.nROfs += *(pbufmax - 2); chn.nLOfs += *(pbufmax - 1); pbuffer = pbufmax; naddmix = 1; } nsamples -= nSmpCount; if (chn.nRampLength) { if (chn.nRampLength <= static_cast(nSmpCount)) { // Ramping is done chn.nRampLength = 0; chn.leftVol = chn.newLeftVol; chn.rightVol = chn.newRightVol; chn.rightRamp = chn.leftRamp = 0; if(chn.dwFlags[CHN_NOTEFADE] && !chn.nFadeOutVol) { chn.nLength = 0; chn.pCurrentSample = nullptr; } } else { chn.nRampLength -= nSmpCount; } } const bool pastLoopEnd = chn.position.GetUInt() >= chn.nLoopEnd && chn.dwFlags[CHN_LOOP]; const bool pastSampleEnd = chn.position.GetUInt() >= chn.nLength && !chn.dwFlags[CHN_LOOP] && chn.nLength && !chn.nMasterChn; const bool doSampleSwap = m_playBehaviour[kMODSampleSwap] && chn.nNewIns && chn.nNewIns <= GetNumSamples() && chn.pModSample != &Samples[chn.nNewIns]; if((pastLoopEnd || pastSampleEnd) && doSampleSwap) { // ProTracker compatibility: Instrument changes without a note do not happen instantly, but rather when the sample loop has finished playing. // Test case: PTInstrSwap.mod, PTSwapNoLoop.mod const ModSample &smp = Samples[chn.nNewIns]; chn.pModSample = &smp; chn.pCurrentSample = smp.samplev(); chn.dwFlags = (chn.dwFlags & CHN_CHANNELFLAGS) | smp.uFlags; chn.nLength = smp.uFlags[CHN_LOOP] ? smp.nLoopEnd : 0; // non-looping sample continue in oneshot mode (i.e. they will most probably just play silence) chn.nLoopStart = smp.nLoopStart; chn.nLoopEnd = smp.nLoopEnd; chn.position.SetInt(chn.nLoopStart); mixLoopState.UpdateLookaheadPointers(chn); if(!chn.pCurrentSample) { break; } } else if(pastLoopEnd && !doSampleSwap && m_playBehaviour[kMODOneShotLoops] && chn.nLoopStart == 0) { // ProTracker "oneshot" loops (if loop start is 0, play the whole sample once and then repeat until loop end) chn.position.SetInt(0); chn.nLoopEnd = chn.nLength = chn.pModSample->nLoopEnd; } } while(nsamples > 0); // Restore sample pointer in case it got changed through loop wrap-around chn.pCurrentSample = mixLoopState.samplePointer; nchmixed += naddmix; #ifndef NO_PLUGINS if(naddmix && nMixPlugin > 0 && nMixPlugin <= MAX_MIXPLUGINS && m_MixPlugins[nMixPlugin - 1].pMixPlugin) { m_MixPlugins[nMixPlugin - 1].pMixPlugin->ResetSilence(); } #endif // NO_PLUGINS } m_nMixStat = std::max(m_nMixStat, nchmixed); } void CSoundFile::ProcessPlugins(uint32 nCount) { #ifndef NO_PLUGINS // If any sample channels are active or any plugin has some input, possibly suspended master plugins need to be woken up. bool masterHasInput = (m_nMixStat > 0); #ifdef MPT_INTMIXER const float IntToFloat = m_PlayConfig.getIntToFloat(); const float FloatToInt = m_PlayConfig.getFloatToInt(); #endif // MPT_INTMIXER // Setup float inputs from samples for(PLUGINDEX plug = 0; plug < MAX_MIXPLUGINS; plug++) { SNDMIXPLUGIN &plugin = m_MixPlugins[plug]; if(plugin.pMixPlugin != nullptr && plugin.pMixPlugin->m_MixState.pMixBuffer != nullptr && plugin.pMixPlugin->m_mixBuffer.Ok()) { IMixPlugin *mixPlug = plugin.pMixPlugin; SNDMIXPLUGINSTATE &state = mixPlug->m_MixState; //We should only ever reach this point if the song is playing. if (!mixPlug->IsSongPlaying()) { //Plugin doesn't know it is in a song that is playing; //we must have added it during playback. Initialise it! mixPlug->NotifySongPlaying(true); mixPlug->Resume(); } // Setup float input float *plugInputL = mixPlug->m_mixBuffer.GetInputBuffer(0); float *plugInputR = mixPlug->m_mixBuffer.GetInputBuffer(1); if (state.dwFlags & SNDMIXPLUGINSTATE::psfMixReady) { #ifdef MPT_INTMIXER StereoMixToFloat(state.pMixBuffer, plugInputL, plugInputR, nCount, IntToFloat); #else DeinterleaveStereo(state.pMixBuffer, plugInputL, plugInputR, nCount); #endif // MPT_INTMIXER } else if (state.nVolDecayR || state.nVolDecayL) { StereoFill(state.pMixBuffer, nCount, state.nVolDecayR, state.nVolDecayL); #ifdef MPT_INTMIXER StereoMixToFloat(state.pMixBuffer, plugInputL, plugInputR, nCount, IntToFloat); #else DeinterleaveStereo(state.pMixBuffer, plugInputL, plugInputR, nCount); #endif // MPT_INTMIXER } else { memset(plugInputL, 0, nCount * sizeof(plugInputL[0])); memset(plugInputR, 0, nCount * sizeof(plugInputR[0])); } state.dwFlags &= ~SNDMIXPLUGINSTATE::psfMixReady; if(!plugin.IsMasterEffect() && !(state.dwFlags & SNDMIXPLUGINSTATE::psfSilenceBypass)) { masterHasInput = true; } } } // Convert mix buffer #ifdef MPT_INTMIXER StereoMixToFloat(MixSoundBuffer, MixFloatBuffer[0], MixFloatBuffer[1], nCount, IntToFloat); #else DeinterleaveStereo(MixSoundBuffer, MixFloatBuffer[0], MixFloatBuffer[1], nCount); #endif // MPT_INTMIXER float *pMixL = MixFloatBuffer[0]; float *pMixR = MixFloatBuffer[1]; const bool positionChanged = HasPositionChanged(); // Process Plugins for(PLUGINDEX plug = 0; plug < MAX_MIXPLUGINS; plug++) { SNDMIXPLUGIN &plugin = m_MixPlugins[plug]; if (plugin.pMixPlugin != nullptr && plugin.pMixPlugin->m_MixState.pMixBuffer != nullptr && plugin.pMixPlugin->m_mixBuffer.Ok()) { IMixPlugin *pObject = plugin.pMixPlugin; if(!plugin.IsMasterEffect() && !plugin.pMixPlugin->ShouldProcessSilence() && !(plugin.pMixPlugin->m_MixState.dwFlags & SNDMIXPLUGINSTATE::psfHasInput)) { // If plugin has no inputs and isn't a master plugin, we shouldn't let it process silence if possible. // I have yet to encounter a VST plugin which actually sets this flag. bool hasInput = false; for(PLUGINDEX inPlug = 0; inPlug < plug; inPlug++) { if(m_MixPlugins[inPlug].GetOutputPlugin() == plug) { hasInput = true; break; } } if(!hasInput) { continue; } } bool isMasterMix = false; float *plugInputL = pObject->m_mixBuffer.GetInputBuffer(0); float *plugInputR = pObject->m_mixBuffer.GetInputBuffer(1); if (pMixL == plugInputL) { isMasterMix = true; pMixL = MixFloatBuffer[0]; pMixR = MixFloatBuffer[1]; } SNDMIXPLUGINSTATE &state = plugin.pMixPlugin->m_MixState; float *pOutL = pMixL; float *pOutR = pMixR; if (!plugin.IsOutputToMaster()) { PLUGINDEX nOutput = plugin.GetOutputPlugin(); if(nOutput > plug && nOutput < MAX_MIXPLUGINS && m_MixPlugins[nOutput].pMixPlugin != nullptr) { IMixPlugin *outPlugin = m_MixPlugins[nOutput].pMixPlugin; if(!(state.dwFlags & SNDMIXPLUGINSTATE::psfSilenceBypass)) outPlugin->ResetSilence(); if(outPlugin->m_mixBuffer.Ok()) { pOutL = outPlugin->m_mixBuffer.GetInputBuffer(0); pOutR = outPlugin->m_mixBuffer.GetInputBuffer(1); } } } /* if (plugin.multiRouting) { int nOutput=0; for (int nOutput=0; nOutput < plugin.nOutputs / 2; nOutput++) { destinationPlug = plugin.multiRoutingDestinations[nOutput]; pOutState = m_MixPlugins[destinationPlug].pMixState; pOutputs[2 * nOutput] = plugInputL; pOutputs[2 * (nOutput + 1)] = plugInputR; } }*/ if (plugin.IsMasterEffect()) { if (!isMasterMix) { float *pInL = plugInputL; float *pInR = plugInputR; for (uint32 i=0; iResetSilence(); SNDMIXPLUGIN *chain = &plugin; PLUGINDEX out = chain->GetOutputPlugin(), prevOut = plug; while(out > prevOut && out < MAX_MIXPLUGINS) { chain = &m_MixPlugins[out]; prevOut = out; out = chain->GetOutputPlugin(); if(chain->pMixPlugin) { chain->pMixPlugin->ResetSilence(); } } } } if(plugin.IsBypassed() || (plugin.IsAutoSuspendable() && (state.dwFlags & SNDMIXPLUGINSTATE::psfSilenceBypass))) { const float * const pInL = plugInputL; const float * const pInR = plugInputR; for (uint32 i=0; iPositionChanged(); pObject->Process(pOutL, pOutR, nCount); state.inputSilenceCount += nCount; if(plugin.IsAutoSuspendable() && pObject->GetNumOutputChannels() > 0 && state.inputSilenceCount >= m_MixerSettings.gdwMixingFreq * 4) { bool isSilent = true; for(uint32 i = 0; i < nCount; i++) { if(pOutL[i] >= FLT_EPSILON || pOutL[i] <= -FLT_EPSILON || pOutR[i] >= FLT_EPSILON || pOutR[i] <= -FLT_EPSILON) { isSilent = false; break; } } if(isSilent) { state.dwFlags |= SNDMIXPLUGINSTATE::psfSilenceBypass; } else { state.inputSilenceCount = 0; } } } state.dwFlags &= ~SNDMIXPLUGINSTATE::psfHasInput; } } #ifdef MPT_INTMIXER FloatToStereoMix(pMixL, pMixR, MixSoundBuffer, nCount, FloatToInt); #else InterleaveStereo(pMixL, pMixR, MixSoundBuffer, nCount); #endif // MPT_INTMIXER #else MPT_UNREFERENCED_PARAMETER(nCount); #endif // NO_PLUGINS } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/FloatMixer.h0000644000175000017500000002414114052666041020324 00000000000000/* * FloatMixer.h * ------------ * Purpose: Floating point mixer classes * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "MixerInterface.h" #include "Resampler.h" OPENMPT_NAMESPACE_BEGIN template struct IntToFloatTraits : public MixerTraits { static_assert(std::numeric_limits::is_integer, "Input must be integer"); static_assert(!std::numeric_limits::is_integer, "Output must be floating point"); static MPT_CONSTEXPRINLINE output_t Convert(const input_t x) { return static_cast(x) * (static_cast(1) / static_cast(int2float)); } }; typedef IntToFloatTraits<2, 1, mixsample_t, int8, -int8_min> Int8MToFloatS; typedef IntToFloatTraits<2, 1, mixsample_t, int16, -int16_min> Int16MToFloatS; typedef IntToFloatTraits<2, 2, mixsample_t, int8, -int8_min> Int8SToFloatS; typedef IntToFloatTraits<2, 2, mixsample_t, int16, -int16_min> Int16SToFloatS; ////////////////////////////////////////////////////////////////////////// // Interpolation templates template struct LinearInterpolation { MPT_FORCEINLINE void Start(const ModChannel &, const CResampler &) { } MPT_FORCEINLINE void End(const ModChannel &) { } MPT_FORCEINLINE void operator() (typename Traits::outbuf_t &outSample, const typename Traits::input_t * const inBuffer, const uint32 posLo) { static_assert(static_cast(Traits::numChannelsIn) <= static_cast(Traits::numChannelsOut), "Too many input channels"); const typename Traits::output_t fract = posLo / static_cast(0x100000000); //CResampler::LinearTablef[posLo >> 24]; for(int i = 0; i < Traits::numChannelsIn; i++) { typename Traits::output_t srcVol = Traits::Convert(inBuffer[i]); typename Traits::output_t destVol = Traits::Convert(inBuffer[i + Traits::numChannelsIn]); outSample[i] = srcVol + fract * (destVol - srcVol); } } }; template struct FastSincInterpolation { MPT_FORCEINLINE void Start(const ModChannel &, const CResampler &) { } MPT_FORCEINLINE void End(const ModChannel &) { } MPT_FORCEINLINE void operator() (typename Traits::outbuf_t &outSample, const typename Traits::input_t * const inBuffer, const uint32 posLo) { static_assert(static_cast(Traits::numChannelsIn) <= static_cast(Traits::numChannelsOut), "Too many input channels"); const typename Traits::output_t *lut = CResampler::FastSincTablef + ((posLo >> 22) & 0x3FC); for(int i = 0; i < Traits::numChannelsIn; i++) { outSample[i] = lut[0] * Traits::Convert(inBuffer[i - Traits::numChannelsIn]) + lut[1] * Traits::Convert(inBuffer[i]) + lut[2] * Traits::Convert(inBuffer[i + Traits::numChannelsIn]) + lut[3] * Traits::Convert(inBuffer[i + 2 * Traits::numChannelsIn]); } } }; template struct PolyphaseInterpolation { const typename Traits::output_t *sinc; MPT_FORCEINLINE void Start(const ModChannel &chn, const CResampler &resampler) { sinc = (((chn.increment > SamplePosition(0x130000000ll)) || (chn.increment < -SamplePosition(-0x130000000ll))) ? (((chn.increment > SamplePosition(0x180000000ll)) || (chn.increment < SamplePosition(-0x180000000ll))) ? resampler.gDownsample2x : resampler.gDownsample13x) : resampler.gKaiserSinc); } MPT_FORCEINLINE void End(const ModChannel &) { } MPT_FORCEINLINE void operator() (typename Traits::outbuf_t &outSample, const typename Traits::input_t * const inBuffer, const uint32 posLo) { static_assert(static_cast(Traits::numChannelsIn) <= static_cast(Traits::numChannelsOut), "Too many input channels"); const typename Traits::output_t *lut = sinc + ((posLo >> (32 - SINC_PHASES_BITS)) & SINC_MASK) * SINC_WIDTH; for(int i = 0; i < Traits::numChannelsIn; i++) { outSample[i] = lut[0] * Traits::Convert(inBuffer[i - 3 * Traits::numChannelsIn]) + lut[1] * Traits::Convert(inBuffer[i - 2 * Traits::numChannelsIn]) + lut[2] * Traits::Convert(inBuffer[i - Traits::numChannelsIn]) + lut[3] * Traits::Convert(inBuffer[i]) + lut[4] * Traits::Convert(inBuffer[i + Traits::numChannelsIn]) + lut[5] * Traits::Convert(inBuffer[i + 2 * Traits::numChannelsIn]) + lut[6] * Traits::Convert(inBuffer[i + 3 * Traits::numChannelsIn]) + lut[7] * Traits::Convert(inBuffer[i + 4 * Traits::numChannelsIn]); } } }; template struct FIRFilterInterpolation { const typename Traits::output_t *WFIRlut; MPT_FORCEINLINE void Start(const ModChannel &, const CResampler &resampler) { WFIRlut = resampler.m_WindowedFIR.lut; } MPT_FORCEINLINE void End(const ModChannel &) { } MPT_FORCEINLINE void operator() (typename Traits::outbuf_t &outSample, const typename Traits::input_t * const inBuffer, const uint32 posLo) { static_assert(static_cast(Traits::numChannelsIn) <= static_cast(Traits::numChannelsOut), "Too many input channels"); const typename Traits::output_t * const lut = WFIRlut + ((((posLo >> 16) + WFIR_FRACHALVE) >> WFIR_FRACSHIFT) & WFIR_FRACMASK); for(int i = 0; i < Traits::numChannelsIn; i++) { outSample[i] = lut[0] * Traits::Convert(inBuffer[i - 3 * Traits::numChannelsIn]) + lut[1] * Traits::Convert(inBuffer[i - 2 * Traits::numChannelsIn]) + lut[2] * Traits::Convert(inBuffer[i - Traits::numChannelsIn]) + lut[3] * Traits::Convert(inBuffer[i]) + lut[4] * Traits::Convert(inBuffer[i + Traits::numChannelsIn]) + lut[5] * Traits::Convert(inBuffer[i + 2 * Traits::numChannelsIn]) + lut[6] * Traits::Convert(inBuffer[i + 3 * Traits::numChannelsIn]) + lut[7] * Traits::Convert(inBuffer[i + 4 * Traits::numChannelsIn]); } } }; ////////////////////////////////////////////////////////////////////////// // Mixing templates (add sample to stereo mix) template struct NoRamp { typename Traits::output_t lVol, rVol; MPT_FORCEINLINE void Start(const ModChannel &chn) { lVol = static_cast(chn.leftVol) * (1.0f / 4096.0f); rVol = static_cast(chn.rightVol) * (1.0f / 4096.0f); } MPT_FORCEINLINE void End(const ModChannel &) { } }; struct Ramp { int32 lRamp, rRamp; MPT_FORCEINLINE void Start(const ModChannel &chn) { lRamp = chn.rampLeftVol; rRamp = chn.rampRightVol; } MPT_FORCEINLINE void End(ModChannel &chn) { chn.rampLeftVol = lRamp; chn.leftVol = lRamp >> VOLUMERAMPPRECISION; chn.rampRightVol = rRamp; chn.rightVol = rRamp >> VOLUMERAMPPRECISION; } }; // Legacy optimization: If chn.nLeftVol == chn.nRightVol, save one multiplication instruction template struct MixMonoFastNoRamp : public NoRamp { MPT_FORCEINLINE void operator() (const typename Traits::outbuf_t &outSample, const ModChannel &chn, typename Traits::output_t * const outBuffer) { typename Traits::output_t vol = outSample[0] * lVol; for(int i = 0; i < Traits::numChannelsOut; i++) { outBuffer[i] += vol; } } }; template struct MixMonoNoRamp : public NoRamp { MPT_FORCEINLINE void operator() (const typename Traits::outbuf_t &outSample, const ModChannel &, typename Traits::output_t * const outBuffer) { outBuffer[0] += outSample[0] * lVol; outBuffer[1] += outSample[0] * rVol; } }; template struct MixMonoRamp : public Ramp { MPT_FORCEINLINE void operator() (const typename Traits::outbuf_t &outSample, const ModChannel &chn, typename Traits::output_t * const outBuffer) { // TODO volume is not float, can we optimize this? lRamp += chn.leftRamp; rRamp += chn.rightRamp; outBuffer[0] += outSample[0] * (lRamp >> VOLUMERAMPPRECISION) * (1.0f / 4096.0f); outBuffer[1] += outSample[0] * (rRamp >> VOLUMERAMPPRECISION) * (1.0f / 4096.0f); } }; template struct MixStereoNoRamp : public NoRamp { MPT_FORCEINLINE void operator() (const typename Traits::outbuf_t &outSample, const ModChannel &, typename Traits::output_t * const outBuffer) { outBuffer[0] += outSample[0] * lVol; outBuffer[1] += outSample[1] * rVol; } }; template struct MixStereoRamp : public Ramp { MPT_FORCEINLINE void operator() (const typename Traits::outbuf_t &outSample, const ModChannel &chn, typename Traits::output_t * const outBuffer) { // TODO volume is not float, can we optimize this? lRamp += chn.leftRamp; rRamp += chn.rightRamp; outBuffer[0] += outSample[0] * (lRamp >> VOLUMERAMPPRECISION) * (1.0f / 4096.0f); outBuffer[1] += outSample[1] * (rRamp >> VOLUMERAMPPRECISION) * (1.0f / 4096.0f); } }; ////////////////////////////////////////////////////////////////////////// // Filter templates template struct NoFilter { MPT_FORCEINLINE void Start(const ModChannel &) { } MPT_FORCEINLINE void End(const ModChannel &) { } MPT_FORCEINLINE void operator() (const typename Traits::outbuf_t &, const ModChannel &) { } }; // Resonant filter template struct ResonantFilter { // Filter history typename Traits::output_t fy[Traits::numChannelsIn][2]; MPT_FORCEINLINE void Start(const ModChannel &chn) { for(int i = 0; i < Traits::numChannelsIn; i++) { fy[i][0] = chn.nFilter_Y[i][0]; fy[i][1] = chn.nFilter_Y[i][1]; } } MPT_FORCEINLINE void End(ModChannel &chn) { for(int i = 0; i < Traits::numChannelsIn; i++) { chn.nFilter_Y[i][0] = fy[i][0]; chn.nFilter_Y[i][1] = fy[i][1]; } } // Filter values are clipped to double the input range #define ClipFilter(x) Clamp(x, static_cast(-2.0f), static_cast(2.0f)) MPT_FORCEINLINE void operator() (typename Traits::outbuf_t &outSample, const ModChannel &chn) { static_assert(static_cast(Traits::numChannelsIn) <= static_cast(Traits::numChannelsOut), "Too many input channels"); for(int i = 0; i < Traits::numChannelsIn; i++) { typename Traits::output_t val = outSample[i] * chn.nFilter_A0 + ClipFilter(fy[i][0]) * chn.nFilter_B0 + ClipFilter(fy[i][1]) * chn.nFilter_B1; fy[i][1] = fy[i][0]; fy[i][0] = val - (outSample[i] * chn.nFilter_HP); outSample[i] = val; } } #undef ClipFilter }; OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/InstrumentExtensions.cpp0000644000175000017500000007560314101712124023033 00000000000000/* * InstrumentExtensions.cpp * ------------------------ * Purpose: Instrument properties I/O * Notes : Welcome to the absolutely horrible abominations that are the "extended instrument properties" * which are some of the earliest additions OpenMPT did to the IT / XM format. They are ugly, * and the way they work even differs between IT/XM and ITI/XI/ITP. * Yes, the world would be a better place without this stuff. * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Loaders.h" #ifndef MODPLUG_NO_FILESAVE #include "mpt/io/base.hpp" #include "mpt/io/io.hpp" #include "mpt/io/io_stdstream.hpp" #endif OPENMPT_NAMESPACE_BEGIN /*--------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------- MODULAR (in/out) ModInstrument : ----------------------------------------------------------------------------------------------- * to update: ------------ - both following functions need to be updated when adding a new member in ModInstrument : void WriteInstrumentHeaderStructOrField(ModInstrument * input, std::ostream &file, uint32 only_this_code, int16 fixedsize); bool ReadInstrumentHeaderField(ModInstrument * input, uint32 fcode, int16 fsize, FileReader &file); - see below for body declaration. * members: ---------- - 32bit identification CODE tag (must be unique) - 16bit content SIZE in byte(s) - member field * CODE tag naming convention: ----------------------------- - have a look below in current tag dictionnary - take the initial ones of the field name - 4 caracters code (not more, not less) - must be filled with '.' caracters if code has less than 4 caracters - for arrays, must include a '[' caracter following significant caracters ('.' not significant!!!) - use only caracters used in full member name, ordered as they appear in it - match caracter attribute (small,capital) Example with "PanEnv.nLoopEnd" , "PitchEnv.nLoopEnd" & "VolEnv.Values[MAX_ENVPOINTS]" members : - use 'PLE.' for PanEnv.nLoopEnd - use 'PiLE' for PitchEnv.nLoopEnd - use 'VE[.' for VolEnv.Values[MAX_ENVPOINTS] * In use CODE tag dictionary (alphabetical order): -------------------------------------------------- !!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!! SECTION TO BE UPDATED !!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!! [EXT] means external (not related) to ModInstrument content AUTH [EXT] Song artist C... [EXT] nChannels ChnS [EXT] IT/MPTM: Channel settings for channels 65-127 if needed (doesn't fit to IT header). CS.. nCutSwing CUES [EXT] Sample cue points CWV. [EXT] dwCreatedWithVersion DCT. nDCT; dF.. dwFlags; DGV. [EXT] nDefaultGlobalVolume DT.. [EXT] nDefaultTempo; DTFR [EXT] Fractional part of default tempo DNA. nDNA; EBIH [EXT] embeded instrument header tag (ITP file format) FM.. filterMode; fn[. filename[12]; FO.. nFadeOut; GV.. nGlobalVol; IFC. nIFC; IFR. nIFR; K[. Keyboard[128]; LSWV [EXT] Last Saved With Version MB.. wMidiBank; MC.. nMidiChannel; MDK. nMidiDrumKey; MIMA [EXT] MIdi MApping directives MiP. nMixPlug; MP.. nMidiProgram; MPTS [EXT] Extra song info tag MPTX [EXT] EXTRA INFO tag MSF. [EXT] Mod(Specific)Flags n[.. name[32]; NNA. nNNA; NM[. NoteMap[128]; P... nPan; PE.. PanEnv.nNodes; PE[. PanEnv.Values[MAX_ENVPOINTS]; PiE. PitchEnv.nNodes; PiE[ PitchEnv.Values[MAX_ENVPOINTS]; PiLE PitchEnv.nLoopEnd; PiLS PitchEnv.nLoopStart; PiP[ PitchEnv.Ticks[MAX_ENVPOINTS]; PiSB PitchEnv.nSustainStart; PiSE PitchEnv.nSustainEnd; PLE. PanEnv.nLoopEnd; PLS. PanEnv.nLoopStart; PMM. [EXT] nPlugMixMode; PP[. PanEnv.Ticks[MAX_ENVPOINTS]; PPC. nPPC; PPS. nPPS; PS.. nPanSwing; PSB. PanEnv.nSustainStart; PSE. PanEnv.nSustainEnd; PTTL pitchToTempoLock; PTTF pitchToTempoLock (fractional part); PVEH pluginVelocityHandling; PVOH pluginVolumeHandling; R... resampling; RP.. [EXT] nRestartPos; RPB. [EXT] nRowsPerBeat; RPM. [EXT] nRowsPerMeasure; RS.. nResSwing; RSMP [EXT] Global resampling SEP@ [EXT] chunk SEPARATOR tag SPA. [EXT] m_nSamplePreAmp; TM.. [EXT] nTempoMode; VE.. VolEnv.nNodes; VE[. VolEnv.Values[MAX_ENVPOINTS]; VLE. VolEnv.nLoopEnd; VLS. VolEnv.nLoopStart; VP[. VolEnv.Ticks[MAX_ENVPOINTS]; VR.. nVolRampUp; VS.. nVolSwing; VSB. VolEnv.nSustainStart; VSE. VolEnv.nSustainEnd; VSTV [EXT] nVSTiVolume; PERN PitchEnv.nReleaseNode AERN PanEnv.nReleaseNode VERN VolEnv.nReleaseNode PFLG PitchEnv.dwFlag AFLG PanEnv.dwFlags VFLG VolEnv.dwFlags MPWD MIDI Pitch Wheel Depth ----------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------*/ #ifndef MODPLUG_NO_FILESAVE template struct IsNegativeFunctor { bool operator()(T val) const { return val < 0; } }; template struct IsNegativeFunctor { bool operator()(T val) const { return val < 0; } }; template struct IsNegativeFunctor { bool operator()(T /*val*/) const { return false; } }; template bool IsNegative(const T &val) { return IsNegativeFunctor::is_signed>()(val); } // ------------------------------------------------------------------------------------------ // Convenient macro to help WRITE_HEADER declaration for single type members ONLY (non-array) // ------------------------------------------------------------------------------------------ #define WRITE_MPTHEADER_sized_member(name,type,code) \ static_assert(sizeof(input->name) == sizeof(type), "Instrument property does match specified type!");\ fcode = code;\ fsize = sizeof( type );\ if(writeAll) \ { \ mpt::IO::WriteIntLE(file, fcode); \ mpt::IO::WriteIntLE(file, fsize); \ } else if(only_this_code == fcode)\ { \ MPT_ASSERT(fixedsize == fsize); \ } \ if(only_this_code == fcode || only_this_code == Util::MaxValueOfType(only_this_code)) \ { \ type tmp = (type)(input-> name ); \ mpt::IO::WriteIntLE(file, tmp); \ } \ /**/ // ----------------------------------------------------------------------------------------------------- // Convenient macro to help WRITE_HEADER declaration for single type members which are written truncated // ----------------------------------------------------------------------------------------------------- #define WRITE_MPTHEADER_trunc_member(name,type,code) \ static_assert(sizeof(input->name) > sizeof(type), "Instrument property would not be truncated, use WRITE_MPTHEADER_sized_member instead!");\ fcode = code;\ fsize = sizeof( type );\ if(writeAll) \ { \ mpt::IO::WriteIntLE(file, fcode); \ mpt::IO::WriteIntLE(file, fsize); \ type tmp = (type)(input-> name ); \ mpt::IO::WriteIntLE(file, tmp); \ } else if(only_this_code == fcode)\ { \ /* hackish workaround to resolve mismatched size values: */ \ /* nResampling was a long time declared as uint32 but these macro tables used uint16 and UINT. */ \ /* This worked fine on little-endian, on big-endian not so much. Thus support writing size-mismatched fields. */ \ MPT_ASSERT(fixedsize >= fsize); \ type tmp = (type)(input-> name ); \ mpt::IO::WriteIntLE(file, tmp); \ if(fixedsize > fsize) \ { \ for(int16 i = 0; i < fixedsize - fsize; ++i) \ { \ uint8 fillbyte = !IsNegative(tmp) ? 0 : 0xff; /* sign extend */ \ mpt::IO::WriteIntLE(file, fillbyte); \ } \ } \ } \ /**/ // ------------------------------------------------------------------------ // Convenient macro to help WRITE_HEADER declaration for array members ONLY // ------------------------------------------------------------------------ #define WRITE_MPTHEADER_array_member(name,type,code,arraysize) \ static_assert(sizeof(type) == sizeof(input-> name [0])); \ MPT_ASSERT(sizeof(input->name) >= sizeof(type) * arraysize);\ fcode = code;\ fsize = sizeof( type ) * arraysize;\ if(writeAll) \ { \ mpt::IO::WriteIntLE(file, fcode); \ mpt::IO::WriteIntLE(file, fsize); \ } else if(only_this_code == fcode)\ { \ /* MPT_ASSERT(fixedsize <= fsize); */ \ fsize = fixedsize; /* just trust the size we got passed */ \ } \ if(only_this_code == fcode || only_this_code == Util::MaxValueOfType(only_this_code)) \ { \ for(std::size_t i = 0; i < fsize/sizeof(type); ++i) \ { \ type tmp; \ tmp = input-> name [i]; \ mpt::IO::WriteIntLE(file, tmp); \ } \ } \ /**/ // ------------------------------------------------------------------------ // Convenient macro to help WRITE_HEADER declaration for envelope members ONLY // ------------------------------------------------------------------------ #define WRITE_MPTHEADER_envelope_member(envType,envField,type,code) \ {\ const InstrumentEnvelope &env = input->GetEnvelope(envType); \ static_assert(sizeof(type) == sizeof(env[0]. envField)); \ fcode = code;\ fsize = mpt::saturate_cast(sizeof( type ) * env.size());\ MPT_ASSERT(size_t(fsize) == sizeof( type ) * env.size()); \ \ if(writeAll) \ { \ mpt::IO::WriteIntLE(file, fcode); \ mpt::IO::WriteIntLE(file, fsize); \ } else if(only_this_code == fcode)\ { \ fsize = fixedsize; /* just trust the size we got passed */ \ } \ if(only_this_code == fcode || only_this_code == Util::MaxValueOfType(only_this_code)) \ { \ uint32 maxNodes = std::min(static_cast(fsize/sizeof(type)), static_cast(env.size())); \ for(uint32 i = 0; i < maxNodes; ++i) \ { \ type tmp; \ tmp = env[i]. envField ; \ mpt::IO::WriteIntLE(file, tmp); \ } \ /* Not every instrument's envelope will be the same length. fill up with zeros. */ \ for(uint32 i = maxNodes; i < fsize/sizeof(type); ++i) \ { \ type tmp = 0; \ mpt::IO::WriteIntLE(file, tmp); \ } \ } \ }\ /**/ // Write (in 'file') 'input' ModInstrument with 'code' & 'size' extra field infos for each member void WriteInstrumentHeaderStructOrField(ModInstrument * input, std::ostream &file, uint32 only_this_code, uint16 fixedsize) { uint32 fcode; uint16 fsize; // If true, all extension are written to the file; otherwise only the specified extension is written. // writeAll is true iff we are saving an instrument (or, hypothetically, the legacy ITP format) const bool writeAll = only_this_code == Util::MaxValueOfType(only_this_code); if(!writeAll) { MPT_ASSERT(fixedsize > 0); } // clang-format off WRITE_MPTHEADER_sized_member( nFadeOut , uint32 , MagicBE("FO..") ) WRITE_MPTHEADER_sized_member( nPan , uint32 , MagicBE("P...") ) WRITE_MPTHEADER_sized_member( VolEnv.size() , uint32 , MagicBE("VE..") ) WRITE_MPTHEADER_sized_member( PanEnv.size() , uint32 , MagicBE("PE..") ) WRITE_MPTHEADER_sized_member( PitchEnv.size() , uint32 , MagicBE("PiE.") ) WRITE_MPTHEADER_sized_member( wMidiBank , uint16 , MagicBE("MB..") ) WRITE_MPTHEADER_sized_member( nMidiProgram , uint8 , MagicBE("MP..") ) WRITE_MPTHEADER_sized_member( nMidiChannel , uint8 , MagicBE("MC..") ) WRITE_MPTHEADER_envelope_member( ENV_VOLUME , tick , uint16 , MagicBE("VP[.") ) WRITE_MPTHEADER_envelope_member( ENV_PANNING , tick , uint16 , MagicBE("PP[.") ) WRITE_MPTHEADER_envelope_member( ENV_PITCH , tick , uint16 , MagicBE("PiP[") ) WRITE_MPTHEADER_envelope_member( ENV_VOLUME , value , uint8 , MagicBE("VE[.") ) WRITE_MPTHEADER_envelope_member( ENV_PANNING , value , uint8 , MagicBE("PE[.") ) WRITE_MPTHEADER_envelope_member( ENV_PITCH , value , uint8 , MagicBE("PiE[") ) WRITE_MPTHEADER_sized_member( nMixPlug , uint8 , MagicBE("MiP.") ) WRITE_MPTHEADER_sized_member( nVolRampUp , uint16 , MagicBE("VR..") ) WRITE_MPTHEADER_sized_member( resampling , uint8 , MagicBE("R...") ) WRITE_MPTHEADER_sized_member( nCutSwing , uint8 , MagicBE("CS..") ) WRITE_MPTHEADER_sized_member( nResSwing , uint8 , MagicBE("RS..") ) WRITE_MPTHEADER_sized_member( filterMode , uint8 , MagicBE("FM..") ) WRITE_MPTHEADER_sized_member( pluginVelocityHandling , uint8 , MagicBE("PVEH") ) WRITE_MPTHEADER_sized_member( pluginVolumeHandling , uint8 , MagicBE("PVOH") ) WRITE_MPTHEADER_trunc_member( pitchToTempoLock.GetInt() , uint16 , MagicBE("PTTL") ) WRITE_MPTHEADER_trunc_member( pitchToTempoLock.GetFract() , uint16 , MagicLE("PTTF") ) WRITE_MPTHEADER_sized_member( PitchEnv.nReleaseNode , uint8 , MagicBE("PERN") ) WRITE_MPTHEADER_sized_member( PanEnv.nReleaseNode , uint8 , MagicBE("AERN") ) WRITE_MPTHEADER_sized_member( VolEnv.nReleaseNode , uint8 , MagicBE("VERN") ) WRITE_MPTHEADER_sized_member( PitchEnv.dwFlags , uint8 , MagicBE("PFLG") ) WRITE_MPTHEADER_sized_member( PanEnv.dwFlags , uint8 , MagicBE("AFLG") ) WRITE_MPTHEADER_sized_member( VolEnv.dwFlags , uint8 , MagicBE("VFLG") ) WRITE_MPTHEADER_sized_member( midiPWD , int8 , MagicBE("MPWD") ) // clang-format on } template static bool IsPropertyNeeded(const TIns &Instruments, PropType ModInstrument::*Prop) { const ModInstrument defaultIns; for(const auto &ins : Instruments) { if(ins != nullptr && defaultIns.*Prop != ins->*Prop) return true; } return false; } template static void WritePropertyIfNeeded(const CSoundFile &sndFile, PropType ModInstrument::*Prop, uint32 code, uint16 size, std::ostream &f, INSTRUMENTINDEX numInstruments) { if(IsPropertyNeeded(sndFile.Instruments, Prop)) { sndFile.WriteInstrumentPropertyForAllInstruments(code, size, f, numInstruments); } } // Used only when saving IT, XM and MPTM. // ITI, ITP saves using Ericus' macros etc... // The reason is that ITs and XMs save [code][size][ins1.Value][ins2.Value]... // whereas ITP saves [code][size][ins1.Value][code][size][ins2.Value]... // too late to turn back.... void CSoundFile::SaveExtendedInstrumentProperties(INSTRUMENTINDEX numInstruments, std::ostream &f) const { uint32 code = MagicBE("MPTX"); // write extension header code mpt::IO::WriteIntLE(f, code); if (numInstruments == 0) return; WritePropertyIfNeeded(*this, &ModInstrument::nVolRampUp, MagicBE("VR.."), sizeof(ModInstrument::nVolRampUp), f, numInstruments); WritePropertyIfNeeded(*this, &ModInstrument::nMixPlug, MagicBE("MiP."), sizeof(ModInstrument::nMixPlug), f, numInstruments); WritePropertyIfNeeded(*this, &ModInstrument::nMidiChannel, MagicBE("MC.."), sizeof(ModInstrument::nMidiChannel), f, numInstruments); WritePropertyIfNeeded(*this, &ModInstrument::nMidiProgram, MagicBE("MP.."), sizeof(ModInstrument::nMidiProgram), f, numInstruments); WritePropertyIfNeeded(*this, &ModInstrument::wMidiBank, MagicBE("MB.."), sizeof(ModInstrument::wMidiBank), f, numInstruments); WritePropertyIfNeeded(*this, &ModInstrument::resampling, MagicBE("R..."), sizeof(ModInstrument::resampling), f, numInstruments); WritePropertyIfNeeded(*this, &ModInstrument::pluginVelocityHandling, MagicBE("PVEH"), sizeof(ModInstrument::pluginVelocityHandling), f, numInstruments); WritePropertyIfNeeded(*this, &ModInstrument::pluginVolumeHandling, MagicBE("PVOH"), sizeof(ModInstrument::pluginVolumeHandling), f, numInstruments); if(!(GetType() & MOD_TYPE_XM)) { // XM instrument headers already stores full-precision fade-out WritePropertyIfNeeded(*this, &ModInstrument::nFadeOut, MagicBE("FO.."), sizeof(ModInstrument::nFadeOut), f, numInstruments); // XM instrument headers already have support for this WritePropertyIfNeeded(*this, &ModInstrument::midiPWD, MagicBE("MPWD"), sizeof(ModInstrument::midiPWD), f, numInstruments); // We never supported these as hacks in XM (luckily!) WritePropertyIfNeeded(*this, &ModInstrument::nPan, MagicBE("P..."), sizeof(ModInstrument::nPan), f, numInstruments); WritePropertyIfNeeded(*this, &ModInstrument::nCutSwing, MagicBE("CS.."), sizeof(ModInstrument::nCutSwing), f, numInstruments); WritePropertyIfNeeded(*this, &ModInstrument::nResSwing, MagicBE("RS.."), sizeof(ModInstrument::nResSwing), f, numInstruments); WritePropertyIfNeeded(*this, &ModInstrument::filterMode, MagicBE("FM.."), sizeof(ModInstrument::filterMode), f, numInstruments); if(IsPropertyNeeded(Instruments, &ModInstrument::pitchToTempoLock)) { WriteInstrumentPropertyForAllInstruments(MagicBE("PTTL"), sizeof(uint16), f, numInstruments); WriteInstrumentPropertyForAllInstruments(MagicLE("PTTF"), sizeof(uint16), f, numInstruments); } } if(GetType() & MOD_TYPE_MPT) { uint32 maxNodes[3] = { 0, 0, 0 }; bool hasReleaseNode[3] = { false, false, false }; for(INSTRUMENTINDEX i = 1; i <= numInstruments; i++) if(Instruments[i] != nullptr) { maxNodes[0] = std::max(maxNodes[0], Instruments[i]->VolEnv.size()); maxNodes[1] = std::max(maxNodes[1], Instruments[i]->PanEnv.size()); maxNodes[2] = std::max(maxNodes[2], Instruments[i]->PitchEnv.size()); hasReleaseNode[0] |= (Instruments[i]->VolEnv.nReleaseNode != ENV_RELEASE_NODE_UNSET); hasReleaseNode[1] |= (Instruments[i]->PanEnv.nReleaseNode != ENV_RELEASE_NODE_UNSET); hasReleaseNode[2] |= (Instruments[i]->PitchEnv.nReleaseNode != ENV_RELEASE_NODE_UNSET); } // write full envelope information for MPTM files (more env points) if(maxNodes[0] > 25) { WriteInstrumentPropertyForAllInstruments(MagicBE("VE.."), sizeof(ModInstrument::VolEnv.size()), f, numInstruments); WriteInstrumentPropertyForAllInstruments(MagicBE("VP[."), static_cast(maxNodes[0] * sizeof(EnvelopeNode::tick)), f, numInstruments); WriteInstrumentPropertyForAllInstruments(MagicBE("VE[."), static_cast(maxNodes[0] * sizeof(EnvelopeNode::value)), f, numInstruments); } if(maxNodes[1] > 25) { WriteInstrumentPropertyForAllInstruments(MagicBE("PE.."), sizeof(ModInstrument::PanEnv.size()), f, numInstruments); WriteInstrumentPropertyForAllInstruments(MagicBE("PP[."), static_cast(maxNodes[1] * sizeof(EnvelopeNode::tick)), f, numInstruments); WriteInstrumentPropertyForAllInstruments(MagicBE("PE[."), static_cast(maxNodes[1] * sizeof(EnvelopeNode::value)), f, numInstruments); } if(maxNodes[2] > 25) { WriteInstrumentPropertyForAllInstruments(MagicBE("PiE."), sizeof(ModInstrument::PitchEnv.size()), f, numInstruments); WriteInstrumentPropertyForAllInstruments(MagicBE("PiP["), static_cast(maxNodes[2] * sizeof(EnvelopeNode::tick)), f, numInstruments); WriteInstrumentPropertyForAllInstruments(MagicBE("PiE["), static_cast(maxNodes[2] * sizeof(EnvelopeNode::value)), f, numInstruments); } if(hasReleaseNode[0]) WriteInstrumentPropertyForAllInstruments(MagicBE("VERN"), sizeof(ModInstrument::VolEnv.nReleaseNode), f, numInstruments); if(hasReleaseNode[1]) WriteInstrumentPropertyForAllInstruments(MagicBE("AERN"), sizeof(ModInstrument::PanEnv.nReleaseNode), f, numInstruments); if(hasReleaseNode[2]) WriteInstrumentPropertyForAllInstruments(MagicBE("PERN"), sizeof(ModInstrument::PitchEnv.nReleaseNode), f, numInstruments); } } void CSoundFile::WriteInstrumentPropertyForAllInstruments(uint32 code, uint16 size, std::ostream &f, INSTRUMENTINDEX nInstruments) const { mpt::IO::WriteIntLE(f, code); //write code mpt::IO::WriteIntLE(f, size); //write size for(INSTRUMENTINDEX i = 1; i <= nInstruments; i++) //for all instruments... { if (Instruments[i]) { WriteInstrumentHeaderStructOrField(Instruments[i], f, code, size); } else { ModInstrument emptyInstrument; WriteInstrumentHeaderStructOrField(&emptyInstrument, f, code, size); } } } #endif // !MODPLUG_NO_FILESAVE // -------------------------------------------------------------------------------------------- // Convenient macro to help GET_HEADER declaration for single type members ONLY (non-array) // -------------------------------------------------------------------------------------------- #define GET_MPTHEADER_sized_member(name,type,code) \ case code: \ {\ if( fsize <= sizeof( type ) ) \ { \ /* hackish workaround to resolve mismatched size values: */ \ /* nResampling was a long time declared as uint32 but these macro tables used uint16 and UINT. */ \ /* This worked fine on little-endian, on big-endian not so much. Thus support reading size-mismatched fields. */ \ if(file.CanRead(fsize)) \ { \ type tmp; \ tmp = file.ReadTruncatedIntLE(fsize); \ static_assert(sizeof(tmp) == sizeof(input-> name )); \ input-> name = decltype(input-> name )(tmp); \ result = true; \ } \ } \ } break; // -------------------------------------------------------------------------------------------- // Convenient macro to help GET_HEADER declaration for array members ONLY // -------------------------------------------------------------------------------------------- #define GET_MPTHEADER_array_member(name,type,code) \ case code: \ {\ if( fsize <= sizeof( type ) * std::size(input-> name) ) \ { \ FileReader arrayChunk = file.ReadChunk(fsize); \ for(std::size_t i = 0; i < std::size(input-> name); ++i) \ { \ input-> name [i] = arrayChunk.ReadIntLE(); \ } \ result = true; \ } \ } break; // -------------------------------------------------------------------------------------------- // Convenient macro to help GET_HEADER declaration for character buffer members ONLY // -------------------------------------------------------------------------------------------- #define GET_MPTHEADER_charbuf_member(name,type,code) \ case code: \ {\ if( fsize <= sizeof( type ) * input-> name .static_length() ) \ { \ FileReader arrayChunk = file.ReadChunk(fsize); \ std::string tmp; \ for(std::size_t i = 0; i < fsize; ++i) \ { \ tmp += arrayChunk.ReadChar(); \ } \ input-> name = tmp; \ result = true; \ } \ } break; // -------------------------------------------------------------------------------------------- // Convenient macro to help GET_HEADER declaration for envelope tick/value members // -------------------------------------------------------------------------------------------- #define GET_MPTHEADER_envelope_member(envType,envField,type,code) \ case code: \ {\ FileReader arrayChunk = file.ReadChunk(fsize); \ InstrumentEnvelope &env = input->GetEnvelope(envType); \ for(uint32 i = 0; i < env.size(); i++) \ { \ env[i]. envField = arrayChunk.ReadIntLE(); \ } \ result = true; \ } break; // Return a pointer on the wanted field in 'input' ModInstrument given field code & size bool ReadInstrumentHeaderField(ModInstrument *input, uint32 fcode, uint16 fsize, FileReader &file) { if(input == nullptr) return false; bool result = false; // Members which can be found in this table but not in the write table are only required in the legacy ITP format. switch(fcode) { // clang-format off GET_MPTHEADER_sized_member( nFadeOut , uint32 , MagicBE("FO..") ) GET_MPTHEADER_sized_member( dwFlags , uint8 , MagicBE("dF..") ) GET_MPTHEADER_sized_member( nGlobalVol , uint32 , MagicBE("GV..") ) GET_MPTHEADER_sized_member( nPan , uint32 , MagicBE("P...") ) GET_MPTHEADER_sized_member( VolEnv.nLoopStart , uint8 , MagicBE("VLS.") ) GET_MPTHEADER_sized_member( VolEnv.nLoopEnd , uint8 , MagicBE("VLE.") ) GET_MPTHEADER_sized_member( VolEnv.nSustainStart , uint8 , MagicBE("VSB.") ) GET_MPTHEADER_sized_member( VolEnv.nSustainEnd , uint8 , MagicBE("VSE.") ) GET_MPTHEADER_sized_member( PanEnv.nLoopStart , uint8 , MagicBE("PLS.") ) GET_MPTHEADER_sized_member( PanEnv.nLoopEnd , uint8 , MagicBE("PLE.") ) GET_MPTHEADER_sized_member( PanEnv.nSustainStart , uint8 , MagicBE("PSB.") ) GET_MPTHEADER_sized_member( PanEnv.nSustainEnd , uint8 , MagicBE("PSE.") ) GET_MPTHEADER_sized_member( PitchEnv.nLoopStart , uint8 , MagicBE("PiLS") ) GET_MPTHEADER_sized_member( PitchEnv.nLoopEnd , uint8 , MagicBE("PiLE") ) GET_MPTHEADER_sized_member( PitchEnv.nSustainStart , uint8 , MagicBE("PiSB") ) GET_MPTHEADER_sized_member( PitchEnv.nSustainEnd , uint8 , MagicBE("PiSE") ) GET_MPTHEADER_sized_member( nNNA , uint8 , MagicBE("NNA.") ) GET_MPTHEADER_sized_member( nDCT , uint8 , MagicBE("DCT.") ) GET_MPTHEADER_sized_member( nDNA , uint8 , MagicBE("DNA.") ) GET_MPTHEADER_sized_member( nPanSwing , uint8 , MagicBE("PS..") ) GET_MPTHEADER_sized_member( nVolSwing , uint8 , MagicBE("VS..") ) GET_MPTHEADER_sized_member( nIFC , uint8 , MagicBE("IFC.") ) GET_MPTHEADER_sized_member( nIFR , uint8 , MagicBE("IFR.") ) GET_MPTHEADER_sized_member( wMidiBank , uint16 , MagicBE("MB..") ) GET_MPTHEADER_sized_member( nMidiProgram , uint8 , MagicBE("MP..") ) GET_MPTHEADER_sized_member( nMidiChannel , uint8 , MagicBE("MC..") ) GET_MPTHEADER_sized_member( nPPS , int8 , MagicBE("PPS.") ) GET_MPTHEADER_sized_member( nPPC , uint8 , MagicBE("PPC.") ) GET_MPTHEADER_envelope_member(ENV_VOLUME , tick , uint16 , MagicBE("VP[.") ) GET_MPTHEADER_envelope_member(ENV_PANNING , tick , uint16 , MagicBE("PP[.") ) GET_MPTHEADER_envelope_member(ENV_PITCH , tick , uint16 , MagicBE("PiP[") ) GET_MPTHEADER_envelope_member(ENV_VOLUME , value , uint8 , MagicBE("VE[.") ) GET_MPTHEADER_envelope_member(ENV_PANNING , value , uint8 , MagicBE("PE[.") ) GET_MPTHEADER_envelope_member(ENV_PITCH , value , uint8 , MagicBE("PiE[") ) GET_MPTHEADER_array_member( NoteMap , uint8 , MagicBE("NM[.") ) GET_MPTHEADER_array_member( Keyboard , uint16 , MagicBE("K[..") ) GET_MPTHEADER_charbuf_member( name , char , MagicBE("n[..") ) GET_MPTHEADER_charbuf_member( filename , char , MagicBE("fn[.") ) GET_MPTHEADER_sized_member( nMixPlug , uint8 , MagicBE("MiP.") ) GET_MPTHEADER_sized_member( nVolRampUp , uint16 , MagicBE("VR..") ) GET_MPTHEADER_sized_member( nCutSwing , uint8 , MagicBE("CS..") ) GET_MPTHEADER_sized_member( nResSwing , uint8 , MagicBE("RS..") ) GET_MPTHEADER_sized_member( filterMode , uint8 , MagicBE("FM..") ) GET_MPTHEADER_sized_member( pluginVelocityHandling , uint8 , MagicBE("PVEH") ) GET_MPTHEADER_sized_member( pluginVolumeHandling , uint8 , MagicBE("PVOH") ) GET_MPTHEADER_sized_member( PitchEnv.nReleaseNode , uint8 , MagicBE("PERN") ) GET_MPTHEADER_sized_member( PanEnv.nReleaseNode , uint8 , MagicBE("AERN") ) GET_MPTHEADER_sized_member( VolEnv.nReleaseNode , uint8 , MagicBE("VERN") ) GET_MPTHEADER_sized_member( PitchEnv.dwFlags , uint8 , MagicBE("PFLG") ) GET_MPTHEADER_sized_member( PanEnv.dwFlags , uint8 , MagicBE("AFLG") ) GET_MPTHEADER_sized_member( VolEnv.dwFlags , uint8 , MagicBE("VFLG") ) GET_MPTHEADER_sized_member( midiPWD , int8 , MagicBE("MPWD") ) // clang-format on case MagicBE("R..."): { // Resampling has been written as various sizes including uint16 and uint32 in the past uint32 tmp = file.ReadSizedIntLE(fsize); if(Resampling::IsKnownMode(tmp)) input->resampling = static_cast(tmp); result = true; } break; case MagicBE("PTTL"): { // Integer part of pitch/tempo lock uint16 tmp = file.ReadSizedIntLE(fsize); input->pitchToTempoLock.Set(tmp, input->pitchToTempoLock.GetFract()); result = true; } break; case MagicLE("PTTF"): { // Fractional part of pitch/tempo lock uint16 tmp = file.ReadSizedIntLE(fsize); input->pitchToTempoLock.Set(input->pitchToTempoLock.GetInt(), tmp); result = true; } break; case MagicBE("VE.."): input->VolEnv.resize(std::min(uint32(MAX_ENVPOINTS), file.ReadSizedIntLE(fsize))); result = true; break; case MagicBE("PE.."): input->PanEnv.resize(std::min(uint32(MAX_ENVPOINTS), file.ReadSizedIntLE(fsize))); result = true; break; case MagicBE("PiE."): input->PitchEnv.resize(std::min(uint32(MAX_ENVPOINTS), file.ReadSizedIntLE(fsize))); result = true; break; } return result; } // Convert instrument flags which were read from 'dF..' extension to proper internal representation. static void ConvertReadExtendedFlags(ModInstrument *pIns) { // Flags of 'dF..' datafield in extended instrument properties. enum { dFdd_VOLUME = 0x0001, dFdd_VOLSUSTAIN = 0x0002, dFdd_VOLLOOP = 0x0004, dFdd_PANNING = 0x0008, dFdd_PANSUSTAIN = 0x0010, dFdd_PANLOOP = 0x0020, dFdd_PITCH = 0x0040, dFdd_PITCHSUSTAIN = 0x0080, dFdd_PITCHLOOP = 0x0100, dFdd_SETPANNING = 0x0200, dFdd_FILTER = 0x0400, dFdd_VOLCARRY = 0x0800, dFdd_PANCARRY = 0x1000, dFdd_PITCHCARRY = 0x2000, dFdd_MUTE = 0x4000, }; const uint32 dwOldFlags = pIns->dwFlags.GetRaw(); pIns->VolEnv.dwFlags.set(ENV_ENABLED, (dwOldFlags & dFdd_VOLUME) != 0); pIns->VolEnv.dwFlags.set(ENV_SUSTAIN, (dwOldFlags & dFdd_VOLSUSTAIN) != 0); pIns->VolEnv.dwFlags.set(ENV_LOOP, (dwOldFlags & dFdd_VOLLOOP) != 0); pIns->VolEnv.dwFlags.set(ENV_CARRY, (dwOldFlags & dFdd_VOLCARRY) != 0); pIns->PanEnv.dwFlags.set(ENV_ENABLED, (dwOldFlags & dFdd_PANNING) != 0); pIns->PanEnv.dwFlags.set(ENV_SUSTAIN, (dwOldFlags & dFdd_PANSUSTAIN) != 0); pIns->PanEnv.dwFlags.set(ENV_LOOP, (dwOldFlags & dFdd_PANLOOP) != 0); pIns->PanEnv.dwFlags.set(ENV_CARRY, (dwOldFlags & dFdd_PANCARRY) != 0); pIns->PitchEnv.dwFlags.set(ENV_ENABLED, (dwOldFlags & dFdd_PITCH) != 0); pIns->PitchEnv.dwFlags.set(ENV_SUSTAIN, (dwOldFlags & dFdd_PITCHSUSTAIN) != 0); pIns->PitchEnv.dwFlags.set(ENV_LOOP, (dwOldFlags & dFdd_PITCHLOOP) != 0); pIns->PitchEnv.dwFlags.set(ENV_CARRY, (dwOldFlags & dFdd_PITCHCARRY) != 0); pIns->PitchEnv.dwFlags.set(ENV_FILTER, (dwOldFlags & dFdd_FILTER) != 0); pIns->dwFlags.reset(); pIns->dwFlags.set(INS_SETPANNING, (dwOldFlags & dFdd_SETPANNING) != 0); pIns->dwFlags.set(INS_MUTE, (dwOldFlags & dFdd_MUTE) != 0); } void ReadInstrumentExtensionField(ModInstrument* pIns, const uint32 code, const uint16 size, FileReader &file) { if(code == MagicBE("K[..")) { // skip keyboard mapping file.Skip(size); return; } bool success = ReadInstrumentHeaderField(pIns, code, size, file); if(!success) { file.Skip(size); return; } if(code == MagicBE("dF..")) // 'dF..' field requires additional processing. ConvertReadExtendedFlags(pIns); } void ReadExtendedInstrumentProperty(ModInstrument* pIns, const uint32 code, FileReader &file) { uint16 size = file.ReadUint16LE(); if(!file.CanRead(size)) { return; } ReadInstrumentExtensionField(pIns, code, size, file); } void ReadExtendedInstrumentProperties(ModInstrument* pIns, FileReader &file) { if(!file.ReadMagic("XTPM")) // 'MPTX' { return; } while(file.CanRead(7)) { ReadExtendedInstrumentProperty(pIns, file.ReadUint32LE(), file); } } bool CSoundFile::LoadExtendedInstrumentProperties(FileReader &file) { if(!file.ReadMagic("XTPM")) // 'MPTX' { return false; } while(file.CanRead(6)) { uint32 code = file.ReadUint32LE(); if(code == MagicBE("MPTS") // Reached song extensions, break out of this loop || code == MagicLE("228\x04") // Reached MPTM extensions (in case there are no song extensions) || (code & 0x80808080) || !(code & 0x60606060)) // Non-ASCII chunk ID { file.SkipBack(4); break; } // Read size of this property for *one* instrument const uint16 size = file.ReadUint16LE(); for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++) { if(Instruments[i]) { ReadInstrumentExtensionField(Instruments[i], code, size, file); } } } return true; } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/IntMixer.h0000644000175000017500000003200214052666041020004 00000000000000/* * IntMixer.h * ---------- * Purpose: Fixed point mixer classes * Notes : (currently none) * Authors: Olivier Lapicque * OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "Resampler.h" #include "MixerInterface.h" #include "Paula.h" OPENMPT_NAMESPACE_BEGIN template struct IntToIntTraits : public MixerTraits { typedef MixerTraits base_t; typedef typename base_t::input_t input_t; typedef typename base_t::output_t output_t; static MPT_CONSTEXPRINLINE output_t Convert(const input_t x) { static_assert(std::numeric_limits::is_integer, "Input must be integer"); static_assert(std::numeric_limits::is_integer, "Output must be integer"); static_assert(sizeof(out) * 8 >= mixPrecision, "Mix precision is higher than output type can handle"); static_assert(sizeof(in) * 8 <= mixPrecision, "Mix precision is lower than input type"); return static_cast(x) * (1<<(mixPrecision - sizeof(in) * 8)); } }; typedef IntToIntTraits<2, 1, mixsample_t, int8, 16> Int8MToIntS; typedef IntToIntTraits<2, 1, mixsample_t, int16, 16> Int16MToIntS; typedef IntToIntTraits<2, 2, mixsample_t, int8, 16> Int8SToIntS; typedef IntToIntTraits<2, 2, mixsample_t, int16, 16> Int16SToIntS; ////////////////////////////////////////////////////////////////////////// // Interpolation templates template struct AmigaBlepInterpolation { SamplePosition subIncrement; Paula::State *paula; const Paula::BlepArray *WinSincIntegral; int numSteps; MPT_FORCEINLINE void Start(ModChannel &chn, const CResampler &resampler) { paula = &chn.paulaState; numSteps = paula->numSteps; WinSincIntegral = &resampler.blepTables.GetAmigaTable(resampler.m_Settings.emulateAmiga, chn.dwFlags[CHN_AMIGAFILTER]); if(numSteps) subIncrement = chn.increment / numSteps; } MPT_FORCEINLINE void End(const ModChannel &) { } MPT_FORCEINLINE void operator() (typename Traits::outbuf_t &outSample, const typename Traits::input_t * const MPT_RESTRICT inBuffer, const uint32 posLo) { SamplePosition pos(0, posLo); // First, process steps of full length (one Amiga clock interval) for(int step = numSteps; step > 0; step--) { typename Traits::output_t inSample = 0; int32 posInt = pos.GetInt() * Traits::numChannelsIn; for(int32 i = 0; i < Traits::numChannelsIn; i++) inSample += Traits::Convert(inBuffer[posInt + i]); paula->InputSample(static_cast(inSample / (4 * Traits::numChannelsIn))); paula->Clock(Paula::MINIMUM_INTERVAL); pos += subIncrement; } paula->remainder += paula->stepRemainder; // Now, process any remaining integer clock amount < MINIMUM_INTERVAL uint32 remainClocks = paula->remainder.GetInt(); if(remainClocks) { typename Traits::output_t inSample = 0; int32 posInt = pos.GetInt() * Traits::numChannelsIn; for(int32 i = 0; i < Traits::numChannelsIn; i++) inSample += Traits::Convert(inBuffer[posInt + i]); paula->InputSample(static_cast(inSample / (4 * Traits::numChannelsIn))); paula->Clock(remainClocks); paula->remainder.RemoveInt(); } auto out = paula->OutputSample(*WinSincIntegral); for(int i = 0; i < Traits::numChannelsOut; i++) outSample[i] = out; } }; template struct LinearInterpolation { MPT_FORCEINLINE void Start(const ModChannel &, const CResampler &) { } MPT_FORCEINLINE void End(const ModChannel &) { } MPT_FORCEINLINE void operator() (typename Traits::outbuf_t &outSample, const typename Traits::input_t * const MPT_RESTRICT inBuffer, const uint32 posLo) { static_assert(static_cast(Traits::numChannelsIn) <= static_cast(Traits::numChannelsOut), "Too many input channels"); const typename Traits::output_t fract = posLo >> 18u; for(int i = 0; i < Traits::numChannelsIn; i++) { typename Traits::output_t srcVol = Traits::Convert(inBuffer[i]); typename Traits::output_t destVol = Traits::Convert(inBuffer[i + Traits::numChannelsIn]); outSample[i] = srcVol + ((fract * (destVol - srcVol)) / 16384); } } }; template struct FastSincInterpolation { MPT_FORCEINLINE void Start(const ModChannel &, const CResampler &) { } MPT_FORCEINLINE void End(const ModChannel &) { } MPT_FORCEINLINE void operator() (typename Traits::outbuf_t &outSample, const typename Traits::input_t * const MPT_RESTRICT inBuffer, const uint32 posLo) { static_assert(static_cast(Traits::numChannelsIn) <= static_cast(Traits::numChannelsOut), "Too many input channels"); const int16 *lut = CResampler::FastSincTable + ((posLo >> 22) & 0x3FC); for(int i = 0; i < Traits::numChannelsIn; i++) { outSample[i] = (lut[0] * Traits::Convert(inBuffer[i - Traits::numChannelsIn]) + lut[1] * Traits::Convert(inBuffer[i]) + lut[2] * Traits::Convert(inBuffer[i + Traits::numChannelsIn]) + lut[3] * Traits::Convert(inBuffer[i + 2 * Traits::numChannelsIn])) / 16384; } } }; template struct PolyphaseInterpolation { const SINC_TYPE *sinc; MPT_FORCEINLINE void Start(const ModChannel &chn, const CResampler &resampler) { #ifdef MODPLUG_TRACKER // Otherwise causes "warning C4100: 'resampler' : unreferenced formal parameter" // because all 3 tables are static members. // #pragma warning fails with this templated case for unknown reasons. MPT_UNREFERENCED_PARAMETER(resampler); #endif // MODPLUG_TRACKER sinc = (((chn.increment > SamplePosition(0x130000000ll)) || (chn.increment < SamplePosition(-0x130000000ll))) ? (((chn.increment > SamplePosition(0x180000000ll)) || (chn.increment < SamplePosition(-0x180000000ll))) ? resampler.gDownsample2x : resampler.gDownsample13x) : resampler.gKaiserSinc); } MPT_FORCEINLINE void End(const ModChannel &) { } MPT_FORCEINLINE void operator() (typename Traits::outbuf_t &outSample, const typename Traits::input_t * const MPT_RESTRICT inBuffer, const uint32 posLo) { static_assert(static_cast(Traits::numChannelsIn) <= static_cast(Traits::numChannelsOut), "Too many input channels"); const SINC_TYPE *lut = sinc + ((posLo >> (32 - SINC_PHASES_BITS)) & SINC_MASK) * SINC_WIDTH; for(int i = 0; i < Traits::numChannelsIn; i++) { outSample[i] = (lut[0] * Traits::Convert(inBuffer[i - 3 * Traits::numChannelsIn]) + lut[1] * Traits::Convert(inBuffer[i - 2 * Traits::numChannelsIn]) + lut[2] * Traits::Convert(inBuffer[i - Traits::numChannelsIn]) + lut[3] * Traits::Convert(inBuffer[i]) + lut[4] * Traits::Convert(inBuffer[i + Traits::numChannelsIn]) + lut[5] * Traits::Convert(inBuffer[i + 2 * Traits::numChannelsIn]) + lut[6] * Traits::Convert(inBuffer[i + 3 * Traits::numChannelsIn]) + lut[7] * Traits::Convert(inBuffer[i + 4 * Traits::numChannelsIn])) / (1 << SINC_QUANTSHIFT); } } }; template struct FIRFilterInterpolation { const int16 *WFIRlut; MPT_FORCEINLINE void Start(const ModChannel &, const CResampler &resampler) { WFIRlut = resampler.m_WindowedFIR.lut; } MPT_FORCEINLINE void End(const ModChannel &) { } MPT_FORCEINLINE void operator() (typename Traits::outbuf_t &outSample, const typename Traits::input_t * const MPT_RESTRICT inBuffer, const uint32 posLo) { static_assert(static_cast(Traits::numChannelsIn) <= static_cast(Traits::numChannelsOut), "Too many input channels"); const int16 * const lut = WFIRlut + ((((posLo >> 16) + WFIR_FRACHALVE) >> WFIR_FRACSHIFT) & WFIR_FRACMASK); for(int i = 0; i < Traits::numChannelsIn; i++) { typename Traits::output_t vol1 = (lut[0] * Traits::Convert(inBuffer[i - 3 * Traits::numChannelsIn])) + (lut[1] * Traits::Convert(inBuffer[i - 2 * Traits::numChannelsIn])) + (lut[2] * Traits::Convert(inBuffer[i - Traits::numChannelsIn])) + (lut[3] * Traits::Convert(inBuffer[i])); typename Traits::output_t vol2 = (lut[4] * Traits::Convert(inBuffer[i + 1 * Traits::numChannelsIn])) + (lut[5] * Traits::Convert(inBuffer[i + 2 * Traits::numChannelsIn])) + (lut[6] * Traits::Convert(inBuffer[i + 3 * Traits::numChannelsIn])) + (lut[7] * Traits::Convert(inBuffer[i + 4 * Traits::numChannelsIn])); outSample[i] = ((vol1 / 2) + (vol2 / 2)) / (1 << (WFIR_16BITSHIFT - 1)); } } }; ////////////////////////////////////////////////////////////////////////// // Mixing templates (add sample to stereo mix) template struct NoRamp { typename Traits::output_t lVol, rVol; MPT_FORCEINLINE void Start(const ModChannel &chn) { lVol = chn.leftVol; rVol = chn.rightVol; } MPT_FORCEINLINE void End(const ModChannel &) { } }; struct Ramp { int32 lRamp, rRamp; MPT_FORCEINLINE void Start(const ModChannel &chn) { lRamp = chn.rampLeftVol; rRamp = chn.rampRightVol; } MPT_FORCEINLINE void End(ModChannel &chn) { chn.rampLeftVol = lRamp; chn.leftVol = lRamp >> VOLUMERAMPPRECISION; chn.rampRightVol = rRamp; chn.rightVol = rRamp >> VOLUMERAMPPRECISION; } }; // Legacy optimization: If chn.nLeftVol == chn.nRightVol, save one multiplication instruction template struct MixMonoFastNoRamp : public NoRamp { typedef NoRamp base_t; MPT_FORCEINLINE void operator() (const typename Traits::outbuf_t &outSample, const ModChannel &, typename Traits::output_t * const MPT_RESTRICT outBuffer) { typename Traits::output_t vol = outSample[0] * base_t::lVol; for(int i = 0; i < Traits::numChannelsOut; i++) { outBuffer[i] += vol; } } }; template struct MixMonoNoRamp : public NoRamp { typedef NoRamp base_t; MPT_FORCEINLINE void operator() (const typename Traits::outbuf_t &outSample, const ModChannel &, typename Traits::output_t * const MPT_RESTRICT outBuffer) { outBuffer[0] += outSample[0] * base_t::lVol; outBuffer[1] += outSample[0] * base_t::rVol; } }; template struct MixMonoRamp : public Ramp { MPT_FORCEINLINE void operator() (const typename Traits::outbuf_t &outSample, const ModChannel &chn, typename Traits::output_t * const MPT_RESTRICT outBuffer) { lRamp += chn.leftRamp; rRamp += chn.rightRamp; outBuffer[0] += outSample[0] * (lRamp >> VOLUMERAMPPRECISION); outBuffer[1] += outSample[0] * (rRamp >> VOLUMERAMPPRECISION); } }; template struct MixStereoNoRamp : public NoRamp { typedef NoRamp base_t; MPT_FORCEINLINE void operator() (const typename Traits::outbuf_t &outSample, const ModChannel &, typename Traits::output_t * const MPT_RESTRICT outBuffer) { outBuffer[0] += outSample[0] * base_t::lVol; outBuffer[1] += outSample[1] * base_t::rVol; } }; template struct MixStereoRamp : public Ramp { MPT_FORCEINLINE void operator() (const typename Traits::outbuf_t &outSample, const ModChannel &chn, typename Traits::output_t * const MPT_RESTRICT outBuffer) { lRamp += chn.leftRamp; rRamp += chn.rightRamp; outBuffer[0] += outSample[0] * (lRamp >> VOLUMERAMPPRECISION); outBuffer[1] += outSample[1] * (rRamp >> VOLUMERAMPPRECISION); } }; ////////////////////////////////////////////////////////////////////////// // Filter templates template struct NoFilter { MPT_FORCEINLINE void Start(const ModChannel &) { } MPT_FORCEINLINE void End(const ModChannel &) { } MPT_FORCEINLINE void operator() (const typename Traits::outbuf_t &, const ModChannel &) { } }; // Resonant filter template struct ResonantFilter { // Filter history typename Traits::output_t fy[Traits::numChannelsIn][2]; MPT_FORCEINLINE void Start(const ModChannel &chn) { for(int i = 0; i < Traits::numChannelsIn; i++) { fy[i][0] = chn.nFilter_Y[i][0]; fy[i][1] = chn.nFilter_Y[i][1]; } } MPT_FORCEINLINE void End(ModChannel &chn) { for(int i = 0; i < Traits::numChannelsIn; i++) { chn.nFilter_Y[i][0] = fy[i][0]; chn.nFilter_Y[i][1] = fy[i][1]; } } // To avoid a precision loss in the state variables especially with quiet samples at low cutoff and high mix rate, we pre-amplify the sample. #define MIXING_FILTER_PREAMP 256 // Filter values are clipped to double the input range #define ClipFilter(x) Clamp(x, int16_min * 2 * MIXING_FILTER_PREAMP, int16_max * 2 * MIXING_FILTER_PREAMP) MPT_FORCEINLINE void operator() (typename Traits::outbuf_t &outSample, const ModChannel &chn) { static_assert(static_cast(Traits::numChannelsIn) <= static_cast(Traits::numChannelsOut), "Too many input channels"); for(int i = 0; i < Traits::numChannelsIn; i++) { const auto inputAmp = outSample[i] * MIXING_FILTER_PREAMP; typename Traits::output_t val = static_cast(mpt::rshift_signed( Util::mul32to64(inputAmp, chn.nFilter_A0) + Util::mul32to64(ClipFilter(fy[i][0]), chn.nFilter_B0) + Util::mul32to64(ClipFilter(fy[i][1]), chn.nFilter_B1) + (1 << (MIXING_FILTER_PRECISION - 1)), MIXING_FILTER_PRECISION)); fy[i][1] = fy[i][0]; fy[i][0] = val - (inputAmp & chn.nFilter_HP); outSample[i] = val / MIXING_FILTER_PREAMP; } } #undef ClipFilter }; OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/ITCompression.cpp0000644000175000017500000002477514056354456021370 00000000000000/* * ITCompression.cpp * ----------------- * Purpose: Code for IT sample compression and decompression. * Notes : The original Python compression code was written by GreaseMonkey and has been released into the public domain. * Authors: OpenMPT Devs * Ben "GreaseMonkey" Russell * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include #include "ITCompression.h" #include "mpt/io/base.hpp" #include "mpt/io/io.hpp" #include "mpt/io/io_stdstream.hpp" #include "../common/misc_util.h" #include "ModSample.h" #include "SampleCopy.h" OPENMPT_NAMESPACE_BEGIN // Algorithm parameters for 16-Bit samples struct IT16BitParams { using sample_t = int16; static constexpr int16 lowerTab[] = {0, -1, -3, -7, -15, -31, -56, -120, -248, -504, -1016, -2040, -4088, -8184, -16376, -32760, -32768}; static constexpr int16 upperTab[] = {0, 1, 3, 7, 15, 31, 55, 119, 247, 503, 1015, 2039, 4087, 8183, 16375, 32759, 32767}; static constexpr int8 fetchA = 4; static constexpr int8 lowerB = -8; static constexpr int8 upperB = 7; static constexpr int8 defWidth = 17; static constexpr int mask = 0xFFFF; }; // Algorithm parameters for 8-Bit samples struct IT8BitParams { using sample_t = int8; static constexpr int8 lowerTab[] = {0, -1, -3, -7, -15, -31, -60, -124, -128}; static constexpr int8 upperTab[] = {0, 1, 3, 7, 15, 31, 59, 123, 127}; static constexpr int8 fetchA = 3; static constexpr int8 lowerB = -4; static constexpr int8 upperB = 3; static constexpr int8 defWidth = 9; static constexpr int mask = 0xFF; }; static constexpr int8 ITWidthChangeSize[] = { 4, 5, 6, 7, 8, 9, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 }; ////////////////////////////////////////////////////////////////////////////// // IT 2.14 compression ITCompression::ITCompression(const ModSample &sample, bool it215, std::ostream *f, SmpLength maxLength) : file(f) , mptSample(sample) , is215(it215) { if(mptSample.GetElementarySampleSize() > 1) Compress(mptSample.sample16(), maxLength); else Compress(mptSample.sample8(), maxLength); } template void ITCompression::Compress(const typename Properties::sample_t *mptSampleData, SmpLength maxLength) { packedData.resize(bufferSize); std::vector sampleData; sampleData.resize(blockSize / sizeof(typename Properties::sample_t)); if(maxLength == 0 || maxLength > mptSample.nLength) maxLength = mptSample.nLength; for(uint8 chn = 0; chn < mptSample.GetNumChannels(); chn++) { SmpLength offset = 0; SmpLength remain = maxLength; while(remain > 0) { // Initialise output buffer and bit writer positions packedLength = 2; bitPos = 0; remBits = 8; byteVal = 0; CompressBlock(mptSampleData + chn, offset, remain, sampleData.data()); if(file) mpt::IO::WriteRaw(*file, packedData.data(), packedLength); packedTotalLength += packedLength; offset += baseLength; remain -= baseLength; } } packedData.resize(0); packedData.shrink_to_fit(); } template void ITCompression::CopySample(T *target, const T *source, SmpLength offset, SmpLength length, SmpLength skip) { T *out = target; const T *in = source + offset * skip; for(SmpLength i = 0, j = 0; j < length; i += skip, j++) { out[j] = in[i]; } } // Convert sample to delta values. template void ITCompression::Deltafy(T *sampleData) { T *p = sampleData; int oldVal = 0; for(SmpLength i = 0; i < baseLength; i++) { int newVal = p[i]; p[i] = static_cast(newVal - oldVal); oldVal = newVal; } } template void ITCompression::CompressBlock(const typename Properties::sample_t *data, SmpLength offset, SmpLength actualLength, typename Properties::sample_t *sampleData) { baseLength = std::min(actualLength, SmpLength(blockSize / sizeof(typename Properties::sample_t))); CopySample(sampleData, data, offset, baseLength, mptSample.GetNumChannels()); Deltafy(sampleData); if(is215) { Deltafy(sampleData); } // Initialise bit width table with initial values bwt.assign(baseLength, Properties::defWidth); // Recurse! SquishRecurse(Properties::defWidth, Properties::defWidth, Properties::defWidth, Properties::defWidth - 2, 0, baseLength, sampleData); // Write those bits! const typename Properties::sample_t *p = sampleData; int8 width = Properties::defWidth; for(size_t i = 0; i < baseLength; i++) { if(bwt[i] != width) { if(width <= 6) { // Mode A: 1 to 6 bits MPT_ASSERT(width); WriteBits(width, (1 << (width - 1))); WriteBits(Properties::fetchA, ConvertWidth(width, bwt[i])); } else if(width < Properties::defWidth) { // Mode B: 7 to 8 / 16 bits int xv = (1 << (width - 1)) + Properties::lowerB + ConvertWidth(width, bwt[i]); WriteBits(width, xv); } else { // Mode C: 9 / 17 bits MPT_ASSERT((bwt[i] - 1) >= 0); WriteBits(width, (1 << (width - 1)) + bwt[i] - 1); } width = bwt[i]; } WriteBits(width, static_cast(p[i]) & Properties::mask); } // Write last byte and update block length WriteByte(byteVal); packedData[0] = static_cast((packedLength - 2) & 0xFF); packedData[1] = static_cast((packedLength - 2) >> 8); } int8 ITCompression::GetWidthChangeSize(int8 w, bool is16) { MPT_ASSERT(w > 0 && static_cast(w) <= std::size(ITWidthChangeSize)); int8 wcs = ITWidthChangeSize[w - 1]; if(w <= 6 && is16) wcs++; return wcs; } template void ITCompression::SquishRecurse(int8 sWidth, int8 lWidth, int8 rWidth, int8 width, SmpLength offset, SmpLength length, const typename Properties::sample_t *sampleData) { if(width + 1 < 1) { for(SmpLength i = offset; i < offset + length; i++) bwt[i] = sWidth; return; } MPT_ASSERT(width >= 0 && static_cast(width) < std::size(Properties::lowerTab)); SmpLength i = offset; SmpLength end = offset + length; const typename Properties::sample_t *p = sampleData; while(i < end) { if(p[i] >= Properties::lowerTab[width] && p[i] <= Properties::upperTab[width]) { SmpLength start = i; // Check for how long we can keep this bit width while(i < end && p[i] >= Properties::lowerTab[width] && p[i] <= Properties::upperTab[width]) { i++; } const SmpLength blockLength = i - start; const int8 xlwidth = start == offset ? lWidth : sWidth; const int8 xrwidth = i == end ? rWidth : sWidth; const bool is16 = sizeof(typename Properties::sample_t) > 1; const int8 wcsl = GetWidthChangeSize(xlwidth, is16); const int8 wcss = GetWidthChangeSize(sWidth, is16); const int8 wcsw = GetWidthChangeSize(width + 1, is16); bool comparison; if(i == baseLength) { SmpLength keepDown = wcsl + (width + 1) * blockLength; SmpLength levelLeft = wcsl + sWidth * blockLength; if(xlwidth == sWidth) levelLeft -= wcsl; comparison = (keepDown <= levelLeft); } else { SmpLength keepDown = wcsl + (width + 1) * blockLength + wcsw; SmpLength levelLeft = wcsl + sWidth * blockLength + wcss; if(xlwidth == sWidth) levelLeft -= wcsl; if(xrwidth == sWidth) levelLeft -= wcss; comparison = (keepDown <= levelLeft); } SquishRecurse(comparison ? (width + 1) : sWidth, xlwidth, xrwidth, width - 1, start, blockLength, sampleData); } else { bwt[i] = sWidth; i++; } } } int8 ITCompression::ConvertWidth(int8 curWidth, int8 newWidth) { curWidth--; newWidth--; MPT_ASSERT(newWidth != curWidth); if(newWidth > curWidth) newWidth--; return newWidth; } void ITCompression::WriteBits(int8 width, int v) { while(width > remBits) { byteVal |= (v << bitPos); width -= remBits; v >>= remBits; bitPos = 0; remBits = 8; WriteByte(byteVal); byteVal = 0; } if(width > 0) { byteVal |= (v & ((1 << width) - 1)) << bitPos; remBits -= width; bitPos += width; } } void ITCompression::WriteByte(uint8 v) { if(packedLength < bufferSize) { packedData[packedLength++] = v; } else { // How could this happen, anyway? MPT_ASSERT_NOTREACHED(); } } ////////////////////////////////////////////////////////////////////////////// // IT 2.14 decompression ITDecompression::ITDecompression(FileReader &file, ModSample &sample, bool it215) : mptSample(sample) , is215(it215) { for(uint8 chn = 0; chn < mptSample.GetNumChannels(); chn++) { writtenSamples = writePos = 0; while(writtenSamples < sample.nLength && file.CanRead(sizeof(uint16))) { uint16 compressedSize = file.ReadUint16LE(); if(!compressedSize) continue; // Malformed sample? bitFile = file.ReadChunk(compressedSize); // Initialise bit reader mem1 = mem2 = 0; try { if(mptSample.GetElementarySampleSize() > 1) Uncompress(mptSample.sample16() + chn); else Uncompress(mptSample.sample8() + chn); } catch(const BitReader::eof &) { // Data is not sufficient to decode the block //AddToLog(LogWarning, "Truncated IT sample block"); } } } } template void ITDecompression::Uncompress(typename Properties::sample_t *target) { curLength = std::min(mptSample.nLength - writtenSamples, SmpLength(ITCompression::blockSize / sizeof(typename Properties::sample_t))); int width = Properties::defWidth; while(curLength > 0) { if(width > Properties::defWidth) { // Error! return; } int v = bitFile.ReadBits(width); const int topBit = (1 << (width - 1)); if(width <= 6) { // Mode A: 1 to 6 bits if(v == topBit) ChangeWidth(width, bitFile.ReadBits(Properties::fetchA)); else Write(v, topBit, target); } else if(width < Properties::defWidth) { // Mode B: 7 to 8 / 16 bits if(v >= topBit + Properties::lowerB && v <= topBit + Properties::upperB) ChangeWidth(width, v - (topBit + Properties::lowerB)); else Write(v, topBit, target); } else { // Mode C: 9 / 17 bits if(v & topBit) width = (v & ~topBit) + 1; else Write((v & ~topBit), 0, target); } } } void ITDecompression::ChangeWidth(int &curWidth, int width) { width++; if(width >= curWidth) width++; curWidth = width; } template void ITDecompression::Write(int v, int topBit, typename Properties::sample_t *target) { if(v & topBit) v -= (topBit << 1); mem1 += v; mem2 += mem1; target[writePos] = static_cast(static_cast(is215 ? mem2 : mem1)); writtenSamples++; writePos += mptSample.GetNumChannels(); curLength--; } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/ITCompression.h0000644000175000017500000000722714052666041021016 00000000000000/* * ITCompression.h * --------------- * Purpose: Code for IT sample compression and decompression. * Notes : The original Python compression code was written by GreaseMonkey and has been released into the public domain. * Authors: OpenMPT Devs * Ben "GreaseMonkey" Russell * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include #include #include "Snd_defs.h" #include "BitReader.h" OPENMPT_NAMESPACE_BEGIN struct ModSample; class ITCompression { public: ITCompression(const ModSample &sample, bool it215, std::ostream *f, SmpLength maxLength = 0); size_t GetCompressedSize() const { return packedTotalLength; } static constexpr size_t bufferSize = 2 + 0xFFFF; // Our output buffer can't be longer than this. static constexpr size_t blockSize = 0x8000; // Block size (in bytes) in which samples are being processed protected: std::vector bwt; // Bit width table for each sampling point std::vector packedData; // Compressed data for current sample block std::ostream *file = nullptr; // File to which compressed data will be written (can be nullptr if you only want to find out the sample size) std::vector sampleData8; // Pre-processed sample data for currently compressed sample block std::vector sampleData16; // Pre-processed sample data for currently compressed sample block const ModSample &mptSample; // Sample that is being processed size_t packedLength = 0; // Size of currently compressed sample block size_t packedTotalLength = 0; // Size of all compressed data so far SmpLength baseLength = 0; // Length of the currently compressed sample block (in samples) // Bit writer int8 bitPos = 0; // Current bit position in this byte int8 remBits = 0; // Remaining bits in this byte uint8 byteVal = 0; // Current byte value to be written const bool is215; // Use IT2.15 compression (double deltas) template void Compress(const typename Properties::sample_t *mptSampleData, SmpLength maxLength); template static void CopySample(T *target, const T *source, SmpLength offset, SmpLength length, SmpLength skip); template void Deltafy(T *sampleData); template void CompressBlock(const typename Properties::sample_t *data, SmpLength offset, SmpLength actualLength, typename Properties::sample_t *sampleData); static int8 GetWidthChangeSize(int8 w, bool is16); template void SquishRecurse(int8 sWidth, int8 lWidth, int8 rWidth, int8 width, SmpLength offset, SmpLength length, const typename Properties::sample_t *sampleData); static int8 ConvertWidth(int8 curWidth, int8 newWidth); void WriteBits(int8 width, int v); void WriteByte(uint8 v); }; class ITDecompression { public: ITDecompression(FileReader &file, ModSample &sample, bool it215); protected: BitReader bitFile; ModSample &mptSample; // Sample that is being processed SmpLength writtenSamples = 0; // Number of samples so far written on this channel SmpLength writePos = 0; // Absolut write position in sample (for stereo samples) SmpLength curLength = 0; // Length of currently processed block unsigned int mem1 = 0, mem2 = 0; // Integrator memory const bool is215; // Use IT2.15 compression (double deltas) template void Uncompress(typename Properties::sample_t *target); static void ChangeWidth(int &curWidth, int width); template void Write(int v, int topbit, typename Properties::sample_t *target); }; OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/ITTools.cpp0000644000175000017500000005110614175523206020144 00000000000000/* * ITTools.cpp * ----------- * Purpose: Definition of IT file structures and helper functions * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Loaders.h" #include "ITTools.h" #include "Tables.h" #include "../common/mptStringBuffer.h" #include "../common/version.h" OPENMPT_NAMESPACE_BEGIN // Convert OpenMPT's internal envelope format into an IT/MPTM envelope. void ITEnvelope::ConvertToIT(const InstrumentEnvelope &mptEnv, uint8 envOffset, uint8 envDefault) { // Envelope Flags if(mptEnv.dwFlags[ENV_ENABLED]) flags |= ITEnvelope::envEnabled; if(mptEnv.dwFlags[ENV_LOOP]) flags |= ITEnvelope::envLoop; if(mptEnv.dwFlags[ENV_SUSTAIN]) flags |= ITEnvelope::envSustain; if(mptEnv.dwFlags[ENV_CARRY]) flags |= ITEnvelope::envCarry; // Nodes and Loops num = (uint8)std::min(mptEnv.size(), uint32(25)); lpb = (uint8)mptEnv.nLoopStart; lpe = (uint8)mptEnv.nLoopEnd; slb = (uint8)mptEnv.nSustainStart; sle = (uint8)mptEnv.nSustainEnd; // Envelope Data MemsetZero(data); if(!mptEnv.empty()) { // Attention: Full MPTM envelope is stored in extended instrument properties for(uint32 ev = 0; ev < num; ev++) { data[ev].value = static_cast(mptEnv[ev].value) - envOffset; data[ev].tick = mptEnv[ev].tick; } } else { // Fix non-existing envelopes so that they can still be edited in Impulse Tracker. num = 2; data[0].value = data[1].value = envDefault - envOffset; data[1].tick = 10; } } // Convert IT/MPTM envelope data into OpenMPT's internal envelope format - To be used by ITInstrToMPT() void ITEnvelope::ConvertToMPT(InstrumentEnvelope &mptEnv, uint8 envOffset, uint8 maxNodes) const { // Envelope Flags mptEnv.dwFlags.set(ENV_ENABLED, (flags & ITEnvelope::envEnabled) != 0); mptEnv.dwFlags.set(ENV_LOOP, (flags & ITEnvelope::envLoop) != 0); mptEnv.dwFlags.set(ENV_SUSTAIN, (flags & ITEnvelope::envSustain) != 0); mptEnv.dwFlags.set(ENV_CARRY, (flags & ITEnvelope::envCarry) != 0); // Nodes and Loops mptEnv.resize(std::min(num, maxNodes)); mptEnv.nLoopStart = std::min(lpb, maxNodes); mptEnv.nLoopEnd = Clamp(lpe, mptEnv.nLoopStart, maxNodes); mptEnv.nSustainStart = std::min(slb, maxNodes); mptEnv.nSustainEnd = Clamp(sle, mptEnv.nSustainStart, maxNodes); // Envelope Data // Attention: Full MPTM envelope is stored in extended instrument properties for(uint32 ev = 0; ev < std::min(uint8(25), num); ev++) { mptEnv[ev].value = Clamp(data[ev].value + envOffset, 0, 64); mptEnv[ev].tick = data[ev].tick; if(ev > 0 && mptEnv[ev].tick < mptEnv[ev - 1].tick && !(mptEnv[ev].tick & 0xFF00)) { // Fix broken envelopes... Instruments 2 and 3 in NoGap.it by Werewolf have envelope points where the high byte of envelope nodes is missing. // NoGap.it was saved with MPT 1.07 - 1.09, which *normally* doesn't do this in IT files. // However... It turns out that MPT 1.07 omitted the high byte of envelope nodes when saving an XI instrument file, and it looks like // Instrument 2 and 3 in NoGap.it were loaded from XI files. mptEnv[ev].tick |= mptEnv[ev - 1].tick & 0xFF00; if(mptEnv[ev].tick < mptEnv[ev - 1].tick) mptEnv[ev].tick += 0x100; } } } // Convert an ITOldInstrument to OpenMPT's internal instrument representation. void ITOldInstrument::ConvertToMPT(ModInstrument &mptIns) const { // Header if(memcmp(id, "IMPI", 4)) { return; } mptIns.name = mpt::String::ReadBuf(mpt::String::spacePadded, name); mptIns.filename = mpt::String::ReadBuf(mpt::String::nullTerminated, filename); // Volume / Panning mptIns.nFadeOut = fadeout << 6; mptIns.nGlobalVol = 64; mptIns.nPan = 128; // NNA Stuff mptIns.nNNA = static_cast(nna.get()); mptIns.nDCT = static_cast(dnc.get()); // Sample Map for(size_t i = 0; i < 120; i++) { uint8 note = keyboard[i * 2]; SAMPLEINDEX ins = keyboard[i * 2 + 1]; if(ins < MAX_SAMPLES) { mptIns.Keyboard[i] = ins; } if(note < 120) { mptIns.NoteMap[i] = note + 1u; } else { mptIns.NoteMap[i] = static_cast(i + 1); } } // Volume Envelope Flags mptIns.VolEnv.dwFlags.set(ENV_ENABLED, (flags & ITOldInstrument::envEnabled) != 0); mptIns.VolEnv.dwFlags.set(ENV_LOOP, (flags & ITOldInstrument::envLoop) != 0); mptIns.VolEnv.dwFlags.set(ENV_SUSTAIN, (flags & ITOldInstrument::envSustain) != 0); // Volume Envelope Loops mptIns.VolEnv.nLoopStart = vls; mptIns.VolEnv.nLoopEnd = vle; mptIns.VolEnv.nSustainStart = sls; mptIns.VolEnv.nSustainEnd = sle; mptIns.VolEnv.resize(25); // Volume Envelope Data for(uint32 i = 0; i < 25; i++) { if((mptIns.VolEnv[i].tick = nodes[i * 2]) == 0xFF) { mptIns.VolEnv.resize(i); break; } mptIns.VolEnv[i].value = nodes[i * 2 + 1]; } if(std::max(mptIns.VolEnv.nLoopStart, mptIns.VolEnv.nLoopEnd) >= mptIns.VolEnv.size()) mptIns.VolEnv.dwFlags.reset(ENV_LOOP); if(std::max(mptIns.VolEnv.nSustainStart, mptIns.VolEnv.nSustainEnd) >= mptIns.VolEnv.size()) mptIns.VolEnv.dwFlags.reset(ENV_SUSTAIN); } // Convert OpenMPT's internal instrument representation to an ITInstrument. uint32 ITInstrument::ConvertToIT(const ModInstrument &mptIns, bool compatExport, const CSoundFile &sndFile) { MemsetZero(*this); // Header memcpy(id, "IMPI", 4); trkvers = 0x5000 | static_cast(Version::Current().GetRawVersion() >> 16); mpt::String::WriteBuf(mpt::String::nullTerminated, filename) = mptIns.filename; mpt::String::WriteBuf(mpt::String::nullTerminated, name) = mptIns.name; // Volume / Panning fadeout = static_cast(std::min(mptIns.nFadeOut >> 5, uint32(256))); gbv = static_cast(std::min(mptIns.nGlobalVol * 2u, uint32(128))); dfp = static_cast(std::min(mptIns.nPan / 4u, uint32(64))); if(!mptIns.dwFlags[INS_SETPANNING]) dfp |= ITInstrument::ignorePanning; // Random Variation rv = std::min(mptIns.nVolSwing, uint8(100)); rp = std::min(mptIns.nPanSwing, uint8(64)); // NNA Stuff nna = static_cast(mptIns.nNNA); dct = static_cast((mptIns.nDCT < DuplicateCheckType::Plugin || !compatExport) ? mptIns.nDCT : DuplicateCheckType::None); dca = static_cast(mptIns.nDNA); // Pitch / Pan Separation pps = mptIns.nPPS; ppc = mptIns.nPPC; // Filter Stuff ifc = mptIns.GetCutoff() | (mptIns.IsCutoffEnabled() ? ITInstrument::enableCutoff : 0x00); ifr = mptIns.GetResonance() | (mptIns.IsResonanceEnabled() ? ITInstrument::enableResonance : 0x00); // MIDI Setup if(mptIns.nMidiProgram > 0) mpr = mptIns.nMidiProgram - 1u; else mpr = 0xFF; if(mptIns.wMidiBank > 0) { mbank[0] = static_cast((mptIns.wMidiBank - 1) & 0x7F); mbank[1] = static_cast((mptIns.wMidiBank - 1) >> 7); } else { mbank[0] = 0xFF; mbank[1] = 0xFF; } if(mptIns.nMidiChannel != MidiNoChannel || mptIns.nMixPlug == 0 || mptIns.nMixPlug > 127 || compatExport) { // Default. Prefer MIDI channel over mixplug to keep the semantics intact. mch = mptIns.nMidiChannel; } else { // Keep compatibility with MPT 1.16's instrument format if possible, as XMPlay / BASS also uses this. mch = mptIns.nMixPlug + 128; } // Sample Map nos = 0; // Only really relevant for ITI files std::vector smpCount(sndFile.GetNumSamples(), false); for(int i = 0; i < 120; i++) { keyboard[i * 2] = (mptIns.NoteMap[i] >= NOTE_MIN && mptIns.NoteMap[i] <= NOTE_MAX) ? (mptIns.NoteMap[i] - NOTE_MIN) : static_cast(i); const SAMPLEINDEX smp = mptIns.Keyboard[i]; if(smp < MAX_SAMPLES && smp < 256) { keyboard[i * 2 + 1] = static_cast(smp); if(smp && smp <= sndFile.GetNumSamples() && !smpCount[smp - 1]) { // We haven't considered this sample yet. Update number of samples. smpCount[smp - 1] = true; nos++; } } } // Writing Volume envelope volenv.ConvertToIT(mptIns.VolEnv, 0, 64); // Writing Panning envelope panenv.ConvertToIT(mptIns.PanEnv, 32, 32); // Writing Pitch Envelope pitchenv.ConvertToIT(mptIns.PitchEnv, 32, 32); if(mptIns.PitchEnv.dwFlags[ENV_FILTER]) pitchenv.flags |= ITEnvelope::envFilter; return sizeof(ITInstrument); } // Convert an ITInstrument to OpenMPT's internal instrument representation. Returns size of the instrument data that has been read. uint32 ITInstrument::ConvertToMPT(ModInstrument &mptIns, MODTYPE modFormat) const { if(memcmp(id, "IMPI", 4)) { return 0; } mptIns.name = mpt::String::ReadBuf(mpt::String::spacePadded, name); mptIns.filename = mpt::String::ReadBuf(mpt::String::nullTerminated, filename); // Volume / Panning mptIns.nFadeOut = fadeout << 5; mptIns.nGlobalVol = gbv / 2; LimitMax(mptIns.nGlobalVol, 64u); mptIns.nPan = (dfp & 0x7F) * 4; if(mptIns.nPan > 256) mptIns.nPan = 128; mptIns.dwFlags.set(INS_SETPANNING, !(dfp & ITInstrument::ignorePanning)); // Random Variation mptIns.nVolSwing = std::min(static_cast(rv), uint8(100)); mptIns.nPanSwing = std::min(static_cast(rp), uint8(64)); // NNA Stuff mptIns.nNNA = static_cast(nna.get()); mptIns.nDCT = static_cast(dct.get()); mptIns.nDNA = static_cast(dca.get()); // Pitch / Pan Separation mptIns.nPPS = pps; mptIns.nPPC = ppc; // Filter Stuff mptIns.SetCutoff(ifc & 0x7F, (ifc & ITInstrument::enableCutoff) != 0); mptIns.SetResonance(ifr & 0x7F, (ifr & ITInstrument::enableResonance) != 0); // MIDI Setup // MPT used to have a slightly different encoding of MIDI program and banks which we are trying to fix here. // Impulse Tracker / Schism Tracker will set trkvers to 0 in IT files, // and we won't care about correctly importing MIDI programs and banks in ITI files. // Chibi Tracker sets trkvers to 0x214, but always writes mpr=mbank=0 anyway. // Old BeRoTracker versions set trkvers to 0x214 or 0x217. // <= MPT 1.07 <= MPT 1.16 OpenMPT 1.17-? <= OpenMPT 1.26 definitely not MPT if((trkvers == 0x0202 || trkvers == 0x0211 || trkvers == 0x0220 || trkvers == 0x0214) && mpr != 0xFF) { if(mpr <= 128) { mptIns.nMidiProgram = mpr; } uint16 bank = mbank[0] | (mbank[1] << 8); // These versions also ignored the high bank nibble (was only handled correctly in OpenMPT instrument extensions) if(bank <= 128) { mptIns.wMidiBank = bank; } } else { if(mpr < 128) { mptIns.nMidiProgram = mpr + 1; } uint16 bank = 0; if(mbank[0] < 128) bank = mbank[0] + 1; if(mbank[1] < 128) bank += (mbank[1] << 7); mptIns.wMidiBank = bank; } mptIns.nMidiChannel = mch; if(mptIns.nMidiChannel >= 128) { // Handle old format where MIDI channel and Plugin index are stored in the same variable mptIns.nMixPlug = mptIns.nMidiChannel - 128; mptIns.nMidiChannel = 0; } // Envelope point count. Limited to 25 in IT format. const uint8 maxNodes = (modFormat & MOD_TYPE_MPT) ? MAX_ENVPOINTS : 25; // Volume Envelope volenv.ConvertToMPT(mptIns.VolEnv, 0, maxNodes); // Panning Envelope panenv.ConvertToMPT(mptIns.PanEnv, 32, maxNodes); // Pitch Envelope pitchenv.ConvertToMPT(mptIns.PitchEnv, 32, maxNodes); mptIns.PitchEnv.dwFlags.set(ENV_FILTER, (pitchenv.flags & ITEnvelope::envFilter) != 0); // Sample Map for(int i = 0; i < 120; i++) { uint8 note = keyboard[i * 2]; SAMPLEINDEX ins = keyboard[i * 2 + 1]; if(ins < MAX_SAMPLES) { mptIns.Keyboard[i] = ins; } if(note < 120) { mptIns.NoteMap[i] = note + NOTE_MIN; } else { mptIns.NoteMap[i] = static_cast(i + NOTE_MIN); } } return sizeof(ITInstrument); } // Convert OpenMPT's internal instrument representation to an ITInstrumentEx. Returns amount of bytes that need to be written to file. uint32 ITInstrumentEx::ConvertToIT(const ModInstrument &mptIns, bool compatExport, const CSoundFile &sndFile) { uint32 instSize = iti.ConvertToIT(mptIns, compatExport, sndFile); if(compatExport) { return instSize; } // Sample Map bool usedExtension = false; iti.nos = 0; std::vector smpCount(sndFile.GetNumSamples(), false); for(int i = 0; i < 120; i++) { const SAMPLEINDEX smp = mptIns.Keyboard[i]; keyboardhi[i] = 0; if(smp < MAX_SAMPLES) { if(smp >= 256) { // We need to save the upper byte for this sample index. iti.keyboard[i * 2 + 1] = static_cast(smp & 0xFF); keyboardhi[i] = static_cast(smp >> 8); usedExtension = true; } if(smp && smp <= sndFile.GetNumSamples() && !smpCount[smp - 1]) { // We haven't considered this sample yet. Update number of samples. smpCount[smp - 1] = true; iti.nos++; } } } if(usedExtension) { // If we actually had to extend the sample map, update the magic bytes and instrument size. memcpy(iti.dummy, "XTPM", 4); instSize = sizeof(ITInstrumentEx); } return instSize; } // Convert an ITInstrumentEx to OpenMPT's internal instrument representation. Returns size of the instrument data that has been read. uint32 ITInstrumentEx::ConvertToMPT(ModInstrument &mptIns, MODTYPE fromType) const { uint32 insSize = iti.ConvertToMPT(mptIns, fromType); // Is this actually an extended instrument? // Note: OpenMPT 1.20 - 1.22 accidentally wrote "MPTX" here (since revision 1203), while previous versions wrote the reversed version, "XTPM". if(insSize == 0 || (memcmp(iti.dummy, "MPTX", 4) && memcmp(iti.dummy, "XTPM", 4))) { return insSize; } // Olivier's MPT Instrument Extension for(int i = 0; i < 120; i++) { mptIns.Keyboard[i] |= ((SAMPLEINDEX)keyboardhi[i] << 8); } return sizeof(ITInstrumentEx); } // Convert OpenMPT's internal sample representation to an ITSample. void ITSample::ConvertToIT(const ModSample &mptSmp, MODTYPE fromType, bool compress, bool compressIT215, bool allowExternal) { MemsetZero(*this); // Header memcpy(id, "IMPS", 4); mpt::String::WriteBuf(mpt::String::nullTerminated, filename) = mptSmp.filename; //mpt::String::WriteBuf(mpt::String::nullTerminated, name) = m_szNames[nsmp]; // Volume / Panning gvl = static_cast(mptSmp.nGlobalVol); vol = static_cast(mptSmp.nVolume / 4); dfp = static_cast(mptSmp.nPan / 4); if(mptSmp.uFlags[CHN_PANNING]) dfp |= ITSample::enablePanning; // Sample Format / Loop Flags if(mptSmp.HasSampleData() && !mptSmp.uFlags[CHN_ADLIB]) { flags = ITSample::sampleDataPresent; if(mptSmp.uFlags[CHN_LOOP]) flags |= ITSample::sampleLoop; if(mptSmp.uFlags[CHN_SUSTAINLOOP]) flags |= ITSample::sampleSustain; if(mptSmp.uFlags[CHN_PINGPONGLOOP]) flags |= ITSample::sampleBidiLoop; if(mptSmp.uFlags[CHN_PINGPONGSUSTAIN]) flags |= ITSample::sampleBidiSustain; if(mptSmp.uFlags[CHN_STEREO]) { flags |= ITSample::sampleStereo; } if(mptSmp.uFlags[CHN_16BIT]) { flags |= ITSample::sample16Bit; } cvt = ITSample::cvtSignedSample; if(compress) { flags |= ITSample::sampleCompressed; if(compressIT215) { cvt |= ITSample::cvtDelta; } } } else { flags = 0x00; } // Frequency C5Speed = mptSmp.nC5Speed ? mptSmp.nC5Speed : 8363; // Size and loops length = mpt::saturate_cast(mptSmp.nLength); loopbegin = mpt::saturate_cast(mptSmp.nLoopStart); loopend = mpt::saturate_cast(mptSmp.nLoopEnd); susloopbegin = mpt::saturate_cast(mptSmp.nSustainStart); susloopend = mpt::saturate_cast(mptSmp.nSustainEnd); // Auto Vibrato settings vit = AutoVibratoXM2IT[mptSmp.nVibType & 7]; vis = std::min(mptSmp.nVibRate, uint8(64)); vid = std::min(mptSmp.nVibDepth, uint8(32)); vir = std::min(mptSmp.nVibSweep, uint8(255)); if((vid | vis) != 0 && (fromType & MOD_TYPE_XM)) { // Sweep is upside down in XM if(mptSmp.nVibSweep != 0) vir = mpt::saturate_cast(Util::muldivr_unsigned(mptSmp.nVibDepth, 256, mptSmp.nVibSweep)); else vir = 255; } if(mptSmp.uFlags[CHN_ADLIB]) { length = 12; flags = ITSample::sampleDataPresent; cvt = ITSample::cvtOPLInstrument; } else if(mptSmp.uFlags[SMP_KEEPONDISK]) { #ifndef MPT_EXTERNAL_SAMPLES allowExternal = false; #endif // MPT_EXTERNAL_SAMPLES // Save external sample (filename at sample pointer) if(allowExternal && mptSmp.HasSampleData()) { cvt = ITSample::cvtExternalSample; } else { length = loopbegin = loopend = susloopbegin = susloopend = 0; } } } // Convert an ITSample to OpenMPT's internal sample representation. uint32 ITSample::ConvertToMPT(ModSample &mptSmp) const { if(memcmp(id, "IMPS", 4)) { return 0; } mptSmp.Initialize(MOD_TYPE_IT); mptSmp.SetDefaultCuePoints(); // For old IT/MPTM files mptSmp.filename = mpt::String::ReadBuf(mpt::String::nullTerminated, filename); // Volume / Panning mptSmp.nVolume = vol * 4; LimitMax(mptSmp.nVolume, uint16(256)); mptSmp.nGlobalVol = gvl; LimitMax(mptSmp.nGlobalVol, uint16(64)); mptSmp.nPan = (dfp & 0x7F) * 4; LimitMax(mptSmp.nPan, uint16(256)); if(dfp & ITSample::enablePanning) mptSmp.uFlags.set(CHN_PANNING); // Loop Flags if(flags & ITSample::sampleLoop) mptSmp.uFlags.set(CHN_LOOP); if(flags & ITSample::sampleSustain) mptSmp.uFlags.set(CHN_SUSTAINLOOP); if(flags & ITSample::sampleBidiLoop) mptSmp.uFlags.set(CHN_PINGPONGLOOP); if(flags & ITSample::sampleBidiSustain) mptSmp.uFlags.set(CHN_PINGPONGSUSTAIN); // Frequency mptSmp.nC5Speed = C5Speed; if(!mptSmp.nC5Speed) mptSmp.nC5Speed = 8363; if(mptSmp.nC5Speed < 256) mptSmp.nC5Speed = 256; // Size and loops mptSmp.nLength = length; mptSmp.nLoopStart = loopbegin; mptSmp.nLoopEnd = loopend; mptSmp.nSustainStart = susloopbegin; mptSmp.nSustainEnd = susloopend; mptSmp.SanitizeLoops(); // Auto Vibrato settings mptSmp.nVibType = static_cast(AutoVibratoIT2XM[vit & 7]); mptSmp.nVibRate = vis; mptSmp.nVibDepth = vid & 0x7F; mptSmp.nVibSweep = vir; if(cvt == ITSample::cvtOPLInstrument) { // FM instrument in MPTM mptSmp.uFlags.set(CHN_ADLIB); } else if(cvt == ITSample::cvtExternalSample) { // Read external sample (filename at sample pointer) mptSmp.uFlags.set(SMP_KEEPONDISK); } return samplepointer; } // Retrieve the internal sample format flags for this instrument. SampleIO ITSample::GetSampleFormat(uint16 cwtv) const { SampleIO sampleIO( (flags & ITSample::sample16Bit) ? SampleIO::_16bit : SampleIO::_8bit, SampleIO::mono, SampleIO::littleEndian, (cvt & ITSample::cvtSignedSample) ? SampleIO::signedPCM: SampleIO::unsignedPCM); // Some old version of IT didn't clear the stereo flag when importing samples. Luckily, all other trackers are identifying as IT 2.14+, so let's check for old IT versions. if((flags & ITSample::sampleStereo) && cwtv >= 0x214) { sampleIO |= SampleIO::stereoSplit; } if(flags & ITSample::sampleCompressed) { // IT 2.14 packed sample sampleIO |= (cvt & ITSample::cvtDelta) ? SampleIO::IT215 : SampleIO::IT214; } else { // MODPlugin :( if(!(flags & ITSample::sample16Bit) && cvt == ITSample::cvtADPCMSample) { sampleIO |= SampleIO::ADPCM; } else { // ITTECH.TXT says these convert flags are "safe to ignore". IT doesn't ignore them, though, so why should we? :) if(cvt & ITSample::cvtBigEndian) { sampleIO |= SampleIO::bigEndian; } if(cvt & ITSample::cvtDelta) { sampleIO |= SampleIO::deltaPCM; } if((cvt & ITSample::cvtPTM8to16) && (flags & ITSample::sample16Bit)) { sampleIO |= SampleIO::PTM8Dto16; } } } return sampleIO; } // Convert an ITHistoryStruct to OpenMPT's internal edit history representation void ITHistoryStruct::ConvertToMPT(FileHistory &mptHistory) const { // Decode FAT date and time MemsetZero(mptHistory.loadDate); if(fatdate != 0 || fattime != 0) { mptHistory.loadDate.tm_year = ((fatdate >> 9) & 0x7F) + 80; mptHistory.loadDate.tm_mon = Clamp((fatdate >> 5) & 0x0F, 1, 12) - 1; mptHistory.loadDate.tm_mday = Clamp(fatdate & 0x1F, 1, 31); mptHistory.loadDate.tm_hour = Clamp((fattime >> 11) & 0x1F, 0, 23); mptHistory.loadDate.tm_min = Clamp((fattime >> 5) & 0x3F, 0, 59); mptHistory.loadDate.tm_sec = Clamp((fattime & 0x1F) * 2, 0, 59); } mptHistory.openTime = static_cast(runtime * (HISTORY_TIMER_PRECISION / 18.2)); } // Convert OpenMPT's internal edit history representation to an ITHistoryStruct void ITHistoryStruct::ConvertToIT(const FileHistory &mptHistory) { // Create FAT file dates if(mptHistory.HasValidDate()) { fatdate = static_cast(mptHistory.loadDate.tm_mday | ((mptHistory.loadDate.tm_mon + 1) << 5) | ((mptHistory.loadDate.tm_year - 80) << 9)); fattime = static_cast((mptHistory.loadDate.tm_sec / 2) | (mptHistory.loadDate.tm_min << 5) | (mptHistory.loadDate.tm_hour << 11)); } else { fatdate = 0; fattime = 0; } runtime = static_cast(mptHistory.openTime * (18.2 / HISTORY_TIMER_PRECISION)); } uint32 DecodeITEditTimer(uint16 cwtv, uint32 editTime) { if((cwtv & 0xFFF) >= 0x0208) { editTime ^= 0x4954524B; // 'ITRK' editTime = mpt::rotr(editTime, 7); editTime = ~editTime + 1; editTime = mpt::rotl(editTime, 4); editTime ^= 0x4A54484C; // 'JTHL' } return editTime; } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/ITTools.h0000644000175000017500000002543114052666041017612 00000000000000/* * ITTools.h * --------- * Purpose: Definition of IT file structures and helper functions * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "../soundlib/ModInstrument.h" #include "../soundlib/ModSample.h" #include "../soundlib/SampleIO.h" OPENMPT_NAMESPACE_BEGIN struct ITFileHeader { // Header Flags enum ITHeaderFlags { useStereoPlayback = 0x01, vol0Optimisations = 0x02, instrumentMode = 0x04, linearSlides = 0x08, itOldEffects = 0x10, itCompatGxx = 0x20, useMIDIPitchController = 0x40, reqEmbeddedMIDIConfig = 0x80, extendedFilterRange = 0x1000, }; // Special Flags enum ITHeaderSpecialFlags { embedSongMessage = 0x01, embedEditHistory = 0x02, embedPatternHighlights = 0x04, embedMIDIConfiguration = 0x08, }; char id[4]; // Magic Bytes (IMPM) char songname[26]; // Song Name, null-terminated (but may also contain nulls) uint8le highlight_minor; // Rows per Beat highlight uint8le highlight_major; // Rows per Measure highlight uint16le ordnum; // Number of Orders uint16le insnum; // Number of Instruments uint16le smpnum; // Number of Samples uint16le patnum; // Number of Patterns uint16le cwtv; // "Made With" Tracker uint16le cmwt; // "Compatible With" Tracker uint16le flags; // Header Flags uint16le special; // Special Flags, for embedding extra information uint8le globalvol; // Global Volume (0...128) uint8le mv; // Master Volume (0...128), referred to as Sample Volume in OpenMPT uint8le speed; // Initial Speed (1...255) uint8le tempo; // Initial Tempo (31...255) uint8le sep; // Pan Separation (0...128) uint8le pwd; // Pitch Wheel Depth uint16le msglength; // Length of Song Message uint32le msgoffset; // Offset of Song Message in File (IT crops message after first null) uint32le reserved; // Some IT versions save an edit timer here. ChibiTracker writes "CHBI" here. OpenMPT and Schism Tracker save extended version information here. uint8le chnpan[64]; // Initial Channel Panning uint8le chnvol[64]; // Initial Channel Volume }; MPT_BINARY_STRUCT(ITFileHeader, 192) struct ITEnvelope { // Envelope Flags enum ITEnvelopeFlags { envEnabled = 0x01, envLoop = 0x02, envSustain = 0x04, envCarry = 0x08, envFilter = 0x80, }; struct Node { int8le value; uint16le tick; }; uint8 flags; // Envelope Flags uint8 num; // Number of Envelope Nodes uint8 lpb; // Loop Start uint8 lpe; // Loop End uint8 slb; // Sustain Start uint8 sle; // Sustain End Node data[25]; // Envelope Node Positions / Values uint8 reserved; // Reserved // Convert OpenMPT's internal envelope format to an IT/MPTM envelope. void ConvertToIT(const InstrumentEnvelope &mptEnv, uint8 envOffset, uint8 envDefault); // Convert IT/MPTM envelope data into OpenMPT's internal envelope format - To be used by ITInstrToMPT() void ConvertToMPT(InstrumentEnvelope &mptEnv, uint8 envOffset, uint8 maxNodes) const; }; MPT_BINARY_STRUCT(ITEnvelope::Node, 3) MPT_BINARY_STRUCT(ITEnvelope, 82) // Old Impulse Instrument Format (cmwt < 0x200) struct ITOldInstrument { enum ITOldInstrFlags { envEnabled = 0x01, envLoop = 0x02, envSustain = 0x04, }; char id[4]; // Magic Bytes (IMPI) char filename[13]; // DOS Filename, null-terminated uint8le flags; // Volume Envelope Flags uint8le vls; // Envelope Loop Start uint8le vle; // Envelope Loop End uint8le sls; // Envelope Sustain Start uint8le sle; // Envelope Sustain End char reserved1[2]; // Reserved uint16le fadeout; // Instrument Fadeout (0...128) uint8le nna; // New Note Action uint8le dnc; // Duplicate Note Check Type uint16le trkvers; // Tracker ID uint8le nos; // Number of embedded samples char reserved2; // Reserved char name[26]; // Instrument Name, null-terminated (but may also contain nulls) char reserved3[6]; // Even more reserved bytes uint8le keyboard[240]; // Sample / Transpose map uint8le volenv[200]; // This appears to be a pre-computed (interpolated) version of the volume envelope data found below. uint8le nodes[25 * 2]; // Volume Envelope Node Positions / Values // Convert an ITOldInstrument to OpenMPT's internal instrument representation. void ConvertToMPT(ModInstrument &mptIns) const; }; MPT_BINARY_STRUCT(ITOldInstrument, 554) // Impulse Instrument Format struct ITInstrument { enum ITInstrumentFlags { ignorePanning = 0x80, enableCutoff = 0x80, enableResonance = 0x80, }; char id[4]; // Magic Bytes (IMPI) char filename[13]; // DOS Filename, null-terminated uint8le nna; // New Note Action uint8le dct; // Duplicate Note Check Type uint8le dca; // Duplicate Note Check Action uint16le fadeout; // Instrument Fadeout (0...256, although values up to 1024 would be sensible. Up to IT2.07, the limit was 0...128) int8le pps; // Pitch/Pan Separatation uint8le ppc; // Pitch/Pan Centre uint8le gbv; // Global Volume uint8le dfp; // Panning uint8le rv; // Vol Swing uint8le rp; // Pan Swing uint16le trkvers; // Tracker ID uint8le nos; // Number of embedded samples char reserved1; // Reserved char name[26]; // Instrument Name, null-terminated (but may also contain nulls) uint8le ifc; // Filter Cutoff uint8le ifr; // Filter Resonance uint8le mch; // MIDI Channel uint8le mpr; // MIDI Program uint8le mbank[2]; // MIDI Bank uint8le keyboard[240]; // Sample / Transpose map ITEnvelope volenv; // Volume Envelope ITEnvelope panenv; // Pan Envelope ITEnvelope pitchenv; // Pitch / Filter Envelope char dummy[4]; // IT saves some additional padding bytes to match the size of the old instrument format for simplified loading. We use them for some hacks. // Convert OpenMPT's internal instrument representation to an ITInstrument. Returns amount of bytes that need to be written. uint32 ConvertToIT(const ModInstrument &mptIns, bool compatExport, const CSoundFile &sndFile); // Convert an ITInstrument to OpenMPT's internal instrument representation. Returns size of the instrument data that has been read. uint32 ConvertToMPT(ModInstrument &mptIns, MODTYPE fromType) const; }; MPT_BINARY_STRUCT(ITInstrument, 554) // MPT IT Instrument Extension struct ITInstrumentEx { ITInstrument iti; // Normal IT Instrument uint8 keyboardhi[120]; // High Byte of Sample map // Convert OpenMPT's internal instrument representation to an ITInstrumentEx. Returns amount of bytes that need to be written. uint32 ConvertToIT(const ModInstrument &mptIns, bool compatExport, const CSoundFile &sndFile); // Convert an ITInstrumentEx to OpenMPT's internal instrument representation. Returns size of the instrument data that has been read. uint32 ConvertToMPT(ModInstrument &mptIns, MODTYPE fromType) const; }; MPT_BINARY_STRUCT(ITInstrumentEx, sizeof(ITInstrument) + 120) // IT Sample Format struct ITSample { // Magic Bytes enum Magic { magic = 0x53504D49, // "IMPS" IT Sample Header Magic Bytes }; enum ITSampleFlags { sampleDataPresent = 0x01, sample16Bit = 0x02, sampleStereo = 0x04, sampleCompressed = 0x08, sampleLoop = 0x10, sampleSustain = 0x20, sampleBidiLoop = 0x40, sampleBidiSustain = 0x80, enablePanning = 0x80, cvtSignedSample = 0x01, cvtOPLInstrument = 0x40, // FM instrument in MPTM cvtExternalSample = 0x80, // Keep MPTM sample on disk cvtADPCMSample = 0xFF, // MODPlugin :( // ITTECH.TXT says these convert flags are "safe to ignore". IT doesn't ignore them, though, so why should we? :) cvtBigEndian = 0x02, cvtDelta = 0x04, cvtPTM8to16 = 0x08, }; char id[4]; // Magic Bytes (IMPS) char filename[13]; // DOS Filename, null-terminated uint8le gvl; // Global Volume uint8le flags; // Sample Flags uint8le vol; // Default Volume char name[26]; // Sample Name, null-terminated (but may also contain nulls) uint8le cvt; // Sample Import Format uint8le dfp; // Sample Panning uint32le length; // Sample Length (in samples) uint32le loopbegin; // Sample Loop Begin (in samples) uint32le loopend; // Sample Loop End (in samples) uint32le C5Speed; // C-5 frequency uint32le susloopbegin; // Sample Sustain Begin (in samples) uint32le susloopend; // Sample Sustain End (in samples) uint32le samplepointer; // Pointer to sample data uint8le vis; // Auto-Vibrato Rate (called Sweep in IT) uint8le vid; // Auto-Vibrato Depth uint8le vir; // Auto-Vibrato Sweep (called Rate in IT) uint8le vit; // Auto-Vibrato Type // Convert OpenMPT's internal sample representation to an ITSample. void ConvertToIT(const ModSample &mptSmp, MODTYPE fromType, bool compress, bool compressIT215, bool allowExternal); // Convert an ITSample to OpenMPT's internal sample representation. uint32 ConvertToMPT(ModSample &mptSmp) const; // Retrieve the internal sample format flags for this instrument. SampleIO GetSampleFormat(uint16 cwtv = 0x214) const; }; MPT_BINARY_STRUCT(ITSample, 80) struct FileHistory; // IT Header extension: Save history struct ITHistoryStruct { uint16le fatdate; // DOS / FAT date when the file was opened / created in the editor. For details, read https://docs.microsoft.com/de-de/windows/win32/api/winbase/nf-winbase-dosdatetimetofiletime uint16le fattime; // DOS / FAT time when the file was opened / created in the editor. uint32le runtime; // The time how long the file was open in the editor, in 1/18.2th seconds. (= ticks of the DOS timer) // Convert an ITHistoryStruct to OpenMPT's internal edit history representation void ConvertToMPT(FileHistory &mptHistory) const; // Convert OpenMPT's internal edit history representation to an ITHistoryStruct void ConvertToIT(const FileHistory &mptHistory); }; MPT_BINARY_STRUCT(ITHistoryStruct, 8) enum IT_ReaderBitMasks { // pattern row parsing, the channel data is read to obtain // number of channels active in the pattern. These bit masks are // to blank out sections of the byte of data being read. IT_bitmask_patternChanField_c = 0x7f, IT_bitmask_patternChanMask_c = 0x3f, IT_bitmask_patternChanEnabled_c = 0x80, IT_bitmask_patternChanUsed_c = 0x0f }; // Calculate Schism Tracker version field for IT / S3M header based on specified release date // Date calculation derived from https://alcor.concordia.ca/~gpkatch/gdate-algorithm.html template struct SchismVersionFromDate { private: static constexpr int32 mm = (m + 9) % 12; static constexpr int32 yy = y - mm / 10; public: static constexpr int32 date = yy * 365 + yy / 4 - yy / 100 + yy / 400 + (mm * 306 + 5) / 10 + (d - 1); }; inline constexpr int32 SchismTrackerEpoch = SchismVersionFromDate<2009, 10, 31>::date; uint32 DecodeITEditTimer(uint16 cwtv, uint32 editTime); OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Load_669.cpp0000644000175000017500000002063114047342516020072 00000000000000/* * Load_669.cpp * ------------ * Purpose: 669 Composer / UNIS 669 module loader * Notes : This is better than Schism's 669 loader :) * (some of this code is "heavily inspired" by Storlek's code from Schism Tracker, and improvements have been made where necessary.) * Authors: Olivier Lapicque * OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Loaders.h" OPENMPT_NAMESPACE_BEGIN struct _669FileHeader { char magic[2]; // 'if' (0x6669, ha ha) or 'JN' char songMessage[108]; // Song Message uint8 samples; // number of samples (1-64) uint8 patterns; // number of patterns (1-128) uint8 restartPos; uint8 orders[128]; uint8 tempoList[128]; uint8 breaks[128]; }; MPT_BINARY_STRUCT(_669FileHeader, 497) struct _669Sample { char filename[13]; uint32le length; uint32le loopStart; uint32le loopEnd; // Convert a 669 sample header to OpenMPT's internal sample header. void ConvertToMPT(ModSample &mptSmp) const { mptSmp.Initialize(); mptSmp.nC5Speed = 8363; mptSmp.nLength = length; mptSmp.nLoopStart = loopStart; mptSmp.nLoopEnd = loopEnd; if(mptSmp.nLoopEnd > mptSmp.nLength && mptSmp.nLoopStart == 0) { mptSmp.nLoopEnd = 0; } if(mptSmp.nLoopEnd != 0) { mptSmp.uFlags = CHN_LOOP; mptSmp.SanitizeLoops(); } } }; MPT_BINARY_STRUCT(_669Sample, 25) static bool ValidateHeader(const _669FileHeader &fileHeader) { if((std::memcmp(fileHeader.magic, "if", 2) && std::memcmp(fileHeader.magic, "JN", 2)) || fileHeader.samples > 64 || fileHeader.restartPos >= 128 || fileHeader.patterns > 128) { return false; } for(std::size_t i = 0; i < std::size(fileHeader.breaks); i++) { if(fileHeader.orders[i] >= 128 && fileHeader.orders[i] < 0xFE) return false; if(fileHeader.orders[i] < 128 && fileHeader.tempoList[i] == 0) return false; if(fileHeader.tempoList[i] > 15) return false; if(fileHeader.breaks[i] >= 64) return false; } return true; } static uint64 GetHeaderMinimumAdditionalSize(const _669FileHeader &fileHeader) { return fileHeader.samples * sizeof(_669Sample) + fileHeader.patterns * 1536u; } CSoundFile::ProbeResult CSoundFile::ProbeFileHeader669(MemoryFileReader file, const uint64 *pfilesize) { _669FileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return ProbeWantMoreData; } if(!ValidateHeader(fileHeader)) { return ProbeFailure; } return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); } bool CSoundFile::Read669(FileReader &file, ModLoadingFlags loadFlags) { _669FileHeader fileHeader; file.Rewind(); if(!file.ReadStruct(fileHeader)) { return false; } if(!ValidateHeader(fileHeader)) { return false; } if(loadFlags == onlyVerifyHeader) { return true; } if(!file.CanRead(mpt::saturate_cast(GetHeaderMinimumAdditionalSize(fileHeader)))) { return false; } InitializeGlobals(MOD_TYPE_669); m_nMinPeriod = 28 << 2; m_nMaxPeriod = 1712 << 3; m_nDefaultTempo.Set(78); m_nDefaultSpeed = 4; m_nChannels = 8; m_playBehaviour.set(kPeriodsAreHertz); #ifdef MODPLUG_TRACKER // 669 uses frequencies rather than periods, so linear slides mode will sound better in the higher octaves. //m_SongFlags.set(SONG_LINEARSLIDES); #endif // MODPLUG_TRACKER m_modFormat.formatName = U_("Composer 669"); m_modFormat.type = U_("669"); m_modFormat.madeWithTracker = !memcmp(fileHeader.magic, "if", 2) ? UL_("Composer 669") : UL_("UNIS 669"); m_modFormat.charset = mpt::Charset::CP437; m_nSamples = fileHeader.samples; for(SAMPLEINDEX smp = 1; smp <= m_nSamples; smp++) { _669Sample sampleHeader; file.ReadStruct(sampleHeader); // Since 669 files have very unfortunate magic bytes ("if") and can // hardly be validated, reject any file with far too big samples. if(sampleHeader.length >= 0x4000000) return false; sampleHeader.ConvertToMPT(Samples[smp]); m_szNames[smp] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, sampleHeader.filename); } // Copy first song message line into song title m_songName = mpt::String::ReadBuf(mpt::String::spacePadded, fileHeader.songMessage, 36); // Song Message m_songMessage.ReadFixedLineLength(mpt::byte_cast(fileHeader.songMessage), 108, 36, 0); // Reading Orders ReadOrderFromArray(Order(), fileHeader.orders, std::size(fileHeader.orders), 0xFF, 0xFE); if(Order()[fileHeader.restartPos] < fileHeader.patterns) Order().SetRestartPos(fileHeader.restartPos); // Set up panning for(CHANNELINDEX chn = 0; chn < 8; chn++) { ChnSettings[chn].Reset(); ChnSettings[chn].nPan = (chn & 1) ? 0xD0 : 0x30; } // Reading Patterns Patterns.ResizeArray(fileHeader.patterns); for(PATTERNINDEX pat = 0; pat < fileHeader.patterns; pat++) { if(!(loadFlags & loadPatternData) || !Patterns.Insert(pat, 64)) { file.Skip(64 * 8 * 3); continue; } static constexpr ModCommand::COMMAND effTrans[] = { CMD_PORTAMENTOUP, // Slide up (param * 80) Hz on every tick CMD_PORTAMENTODOWN, // Slide down (param * 80) Hz on every tick CMD_TONEPORTAMENTO, // Slide to note by (param * 40) Hz on every tick CMD_S3MCMDEX, // Add (param * 80) Hz to sample frequency CMD_VIBRATO, // Add (param * 669) Hz on every other tick CMD_SPEED, // Set ticks per row CMD_PANNINGSLIDE, // Extended UNIS 669 effect CMD_RETRIG, // Extended UNIS 669 effect }; uint8 effect[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; for(ROWINDEX row = 0; row < 64; row++) { PatternRow m = Patterns[pat].GetRow(row); for(CHANNELINDEX chn = 0; chn < 8; chn++, m++) { const auto [noteInstr, instrVol, effParam] = file.ReadArray(); uint8 note = noteInstr >> 2; uint8 instr = ((noteInstr & 0x03) << 4) | (instrVol >> 4); uint8 vol = instrVol & 0x0F; if(noteInstr < 0xFE) { m->note = note + 36 + NOTE_MIN; m->instr = instr + 1; effect[chn] = 0xFF; } if(noteInstr <= 0xFE) { m->volcmd = VOLCMD_VOLUME; m->vol = ((vol * 64 + 8) / 15); } if(effParam != 0xFF) { effect[chn] = effParam; } if((effParam & 0x0F) == 0 && effParam != 0x30) { // A param value of 0 resets the effect. effect[chn] = 0xFF; } if(effect[chn] == 0xFF) { continue; } m->param = effect[chn] & 0x0F; // Weird stuff happening in corehop.669 with effects > 8... they seem to do the same thing as if the high bit wasn't set, but the sample also behaves strangely. uint8 command = effect[chn] >> 4; if(command < static_cast(std::size(effTrans))) { m->command = effTrans[command]; } else { m->command = CMD_NONE; continue; } // Fix some commands switch(command) { case 3: // D - frequency adjust #ifdef MODPLUG_TRACKER // Since we convert to S3M, the finetune command will not quite do what we intend to do (it can adjust the frequency upwards and downwards), so try to approximate it using a fine slide. m->command = CMD_PORTAMENTOUP; m->param |= 0xF0; #else m->param |= 0x20; #endif effect[chn] = 0xFF; break; case 4: // E - frequency vibrato - almost like an arpeggio, but does not arpeggiate by a given note but by a frequency amount. #ifdef MODPLUG_TRACKER m->command = CMD_ARPEGGIO; #endif m->param |= (m->param << 4); break; case 5: // F - set tempo // TODO: param 0 is a "super fast tempo" in Unis 669 mode (?) effect[chn] = 0xFF; break; case 6: // G - subcommands (extended) switch(m->param) { case 0: // balance fine slide left m->param = 0x4F; break; case 1: // balance fine slide right m->param = 0xF4; break; default: m->command = CMD_NONE; } break; } } } // Write pattern break if(fileHeader.breaks[pat] < 63) { Patterns[pat].WriteEffect(EffectWriter(CMD_PATTERNBREAK, 0).Row(fileHeader.breaks[pat]).RetryNextRow()); } // And of course the speed... Patterns[pat].WriteEffect(EffectWriter(CMD_SPEED, fileHeader.tempoList[pat]).RetryNextRow()); } if(loadFlags & loadSampleData) { // Reading Samples const SampleIO sampleIO( SampleIO::_8bit, SampleIO::mono, SampleIO::littleEndian, SampleIO::unsignedPCM); for(SAMPLEINDEX n = 1; n <= m_nSamples; n++) { sampleIO.ReadSample(Samples[n], file); } } return true; } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Load_amf.cpp0000644000175000017500000004134614135023000020275 00000000000000/* * Load_amf.cpp * ------------ * Purpose: AMF module loader * Notes : There are two types of AMF files, the ASYLUM Music Format (used in Crusader: No Remorse and Crusader: No Regret) * and Advanced Music Format (DSMI / Digital Sound And Music Interface, used in various games such as Pinball World). * Both module types are handled here. * Authors: Olivier Lapicque * OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Loaders.h" #include OPENMPT_NAMESPACE_BEGIN // ASYLUM AMF File Header struct AsylumFileHeader { char signature[32]; uint8 defaultSpeed; uint8 defaultTempo; uint8 numSamples; uint8 numPatterns; uint8 numOrders; uint8 restartPos; }; MPT_BINARY_STRUCT(AsylumFileHeader, 38) // ASYLUM AMF Sample Header struct AsylumSampleHeader { char name[22]; uint8le finetune; uint8le defaultVolume; int8le transpose; uint32le length; uint32le loopStart; uint32le loopLength; // Convert an AMF sample header to OpenMPT's internal sample header. void ConvertToMPT(ModSample &mptSmp) const { mptSmp.Initialize(); mptSmp.nFineTune = MOD2XMFineTune(finetune); mptSmp.nVolume = std::min(defaultVolume.get(), uint8(64)) * 4u; mptSmp.RelativeTone = transpose; mptSmp.nLength = length; if(loopLength > 2 && loopStart + loopLength <= length) { mptSmp.uFlags.set(CHN_LOOP); mptSmp.nLoopStart = loopStart; mptSmp.nLoopEnd = loopStart + loopLength; } } }; MPT_BINARY_STRUCT(AsylumSampleHeader, 37) static bool ValidateHeader(const AsylumFileHeader &fileHeader) { if(std::memcmp(fileHeader.signature, "ASYLUM Music Format V1.0\0", 25) || fileHeader.numSamples > 64 ) { return false; } return true; } static uint64 GetHeaderMinimumAdditionalSize(const AsylumFileHeader &fileHeader) { return 256 + 64 * sizeof(AsylumSampleHeader) + 64 * 4 * 8 * fileHeader.numPatterns; } CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderAMF_Asylum(MemoryFileReader file, const uint64 *pfilesize) { AsylumFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return ProbeWantMoreData; } if(!ValidateHeader(fileHeader)) { return ProbeFailure; } return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); } bool CSoundFile::ReadAMF_Asylum(FileReader &file, ModLoadingFlags loadFlags) { file.Rewind(); AsylumFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return false; } if(!ValidateHeader(fileHeader)) { return false; } if(!file.CanRead(mpt::saturate_cast(GetHeaderMinimumAdditionalSize(fileHeader)))) { return false; } if(loadFlags == onlyVerifyHeader) { return true; } InitializeGlobals(MOD_TYPE_AMF0); InitializeChannels(); SetupMODPanning(true); m_nChannels = 8; m_nDefaultSpeed = fileHeader.defaultSpeed; m_nDefaultTempo.Set(fileHeader.defaultTempo); m_nSamples = fileHeader.numSamples; if(fileHeader.restartPos < fileHeader.numOrders) { Order().SetRestartPos(fileHeader.restartPos); } m_modFormat.formatName = U_("ASYLUM Music Format"); m_modFormat.type = U_("amf"); m_modFormat.charset = mpt::Charset::CP437; uint8 orders[256]; file.ReadArray(orders); ReadOrderFromArray(Order(), orders, fileHeader.numOrders); // Read Sample Headers for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++) { AsylumSampleHeader sampleHeader; file.ReadStruct(sampleHeader); sampleHeader.ConvertToMPT(Samples[smp]); m_szNames[smp] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, sampleHeader.name); } file.Skip((64 - fileHeader.numSamples) * sizeof(AsylumSampleHeader)); // Read Patterns Patterns.ResizeArray(fileHeader.numPatterns); for(PATTERNINDEX pat = 0; pat < fileHeader.numPatterns; pat++) { if(!(loadFlags & loadPatternData) || !Patterns.Insert(pat, 64)) { file.Skip(64 * 4 * 8); continue; } for(auto &m : Patterns[pat]) { const auto [note, instr, command, param] = file.ReadArray(); if(note && note + 12 + NOTE_MIN <= NOTE_MAX) { m.note = note + 12 + NOTE_MIN; } m.instr = instr; m.command = command; m.param = param; ConvertModCommand(m); #ifdef MODPLUG_TRACKER if(m.command == CMD_PANNING8) { // Convert 7-bit panning to 8-bit m.param = mpt::saturate_cast(m.param * 2u); } #endif } } if(loadFlags & loadSampleData) { // Read Sample Data const SampleIO sampleIO( SampleIO::_8bit, SampleIO::mono, SampleIO::littleEndian, SampleIO::signedPCM); for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++) { sampleIO.ReadSample(Samples[smp], file); } } return true; } // DSMI AMF File Header struct AMFFileHeader { char amf[3]; uint8le version; char title[32]; uint8le numSamples; uint8le numOrders; uint16le numTracks; uint8le numChannels; }; MPT_BINARY_STRUCT(AMFFileHeader, 41) // DSMI AMF Sample Header (v1-v9) struct AMFSampleHeaderOld { uint8le type; char name[32]; char filename[13]; uint32le index; uint16le length; uint16le sampleRate; uint8le volume; uint16le loopStart; uint16le loopEnd; void ConvertToMPT(ModSample &mptSmp) const { mptSmp.Initialize(); mptSmp.filename = mpt::String::ReadBuf(mpt::String::nullTerminated, filename); mptSmp.nLength = length; mptSmp.nC5Speed = sampleRate; mptSmp.nVolume = std::min(volume.get(), uint8(64)) * 4u; mptSmp.nLoopStart = loopStart; mptSmp.nLoopEnd = loopEnd; if(mptSmp.nLoopEnd == uint16_max) mptSmp.nLoopStart = mptSmp.nLoopEnd = 0; else if(type != 0 && mptSmp.nLoopEnd > mptSmp.nLoopStart + 2 && mptSmp.nLoopEnd <= mptSmp.nLength) mptSmp.uFlags.set(CHN_LOOP); } }; MPT_BINARY_STRUCT(AMFSampleHeaderOld, 59) // DSMI AMF Sample Header (v10+) struct AMFSampleHeaderNew { uint8le type; char name[32]; char filename[13]; uint32le index; uint32le length; uint16le sampleRate; uint8le volume; uint32le loopStart; uint32le loopEnd; void ConvertToMPT(ModSample &mptSmp, bool truncated) const { mptSmp.Initialize(); mptSmp.filename = mpt::String::ReadBuf(mpt::String::nullTerminated, filename); mptSmp.nLength = length; mptSmp.nC5Speed = sampleRate; mptSmp.nVolume = std::min(volume.get(), uint8(64)) * 4u; mptSmp.nLoopStart = loopStart; mptSmp.nLoopEnd = loopEnd; if(truncated && mptSmp.nLoopStart > 0) mptSmp.nLoopEnd = mptSmp.nLength; if(type != 0 && mptSmp.nLoopEnd > mptSmp.nLoopStart + 2 && mptSmp.nLoopEnd <= mptSmp.nLength) mptSmp.uFlags.set(CHN_LOOP); } // Check if sample headers might be truncated bool IsValid(uint8 numSamples) const { return type <= 1 && index <= numSamples && length <= 0x100000 && volume <= 64 && loopStart <= length && loopEnd <= length; } }; MPT_BINARY_STRUCT(AMFSampleHeaderNew, 65) // Read a single AMF track (channel) into a pattern. static void AMFReadPattern(CPattern &pattern, CHANNELINDEX chn, FileReader &fileChunk) { fileChunk.Rewind(); while(fileChunk.CanRead(3)) { const auto [row, command, value] = fileChunk.ReadArray(); if(row >= pattern.GetNumRows()) { break; } ModCommand &m = *pattern.GetpModCommand(row, chn); if(command < 0x7F) { // Note + Volume if(command == 0 && value == 0) { m.note = NOTE_NOTECUT; } else { m.note = command + NOTE_MIN; if(value != 0xFF) { m.volcmd = VOLCMD_VOLUME; m.vol = value; } } } else if(command == 0x7F) { // Instrument without note retrigger in MOD (no need to do anything here, should be preceded by 0x80 command) } else if(command == 0x80) { // Instrument m.instr = value + 1; } else { // Effect static constexpr ModCommand::COMMAND effTrans[] = { CMD_NONE, CMD_SPEED, CMD_VOLUMESLIDE, CMD_VOLUME, CMD_PORTAMENTOUP, CMD_NONE, CMD_TONEPORTAMENTO, CMD_TREMOR, CMD_ARPEGGIO, CMD_VIBRATO, CMD_TONEPORTAVOL, CMD_VIBRATOVOL, CMD_PATTERNBREAK, CMD_POSITIONJUMP, CMD_NONE, CMD_RETRIG, CMD_OFFSET, CMD_VOLUMESLIDE, CMD_PORTAMENTOUP, CMD_S3MCMDEX, CMD_S3MCMDEX, CMD_TEMPO, CMD_PORTAMENTOUP, CMD_PANNING8, }; uint8 cmd = (command & 0x7F); uint8 param = value; if(cmd < std::size(effTrans)) cmd = effTrans[cmd]; else cmd = CMD_NONE; // Fix some commands... switch(command & 0x7F) { // 02: Volume Slide // 0A: Tone Porta + Vol Slide // 0B: Vibrato + Vol Slide case 0x02: case 0x0A: case 0x0B: if(param & 0x80) param = (-static_cast(param)) & 0x0F; else param = (param & 0x0F) << 4; break; // 03: Volume case 0x03: param = std::min(param, uint8(64)); if(m.volcmd == VOLCMD_NONE || m.volcmd == VOLCMD_VOLUME) { m.volcmd = VOLCMD_VOLUME; m.vol = param; cmd = CMD_NONE; } break; // 04: Porta Up/Down case 0x04: if(param & 0x80) param = (-static_cast(param)) & 0x7F; else cmd = CMD_PORTAMENTODOWN; break; // 11: Fine Volume Slide case 0x11: if(param) { if(param & 0x80) param = 0xF0 | ((-static_cast(param)) & 0x0F); else param = 0x0F | ((param & 0x0F) << 4); } else { cmd = CMD_NONE; } break; // 12: Fine Portamento // 16: Extra Fine Portamento case 0x12: case 0x16: if(param) { cmd = static_cast((param & 0x80) ? CMD_PORTAMENTOUP : CMD_PORTAMENTODOWN); if(param & 0x80) { param = ((-static_cast(param)) & 0x0F); } param |= (command == 0x16) ? 0xE0 : 0xF0; } else { cmd = CMD_NONE; } break; // 13: Note Delay case 0x13: param = 0xD0 | (param & 0x0F); break; // 14: Note Cut case 0x14: param = 0xC0 | (param & 0x0F); break; // 17: Panning case 0x17: if(param == 100) { // History lesson intermission: According to Otto Chrons, he remembers that he added support // for 8A4 / XA4 "surround" panning in DMP for MOD and S3M files before any other trackers did, // So DSMI / DMP are most likely the original source of these 7-bit panning + surround commands! param = 0xA4; } else { param = static_cast(std::clamp(static_cast(param) + 64, 0, 128)); if(m.command != CMD_NONE) { // Move to volume column if required if(m.volcmd == VOLCMD_NONE || m.volcmd == VOLCMD_PANNING) { m.volcmd = VOLCMD_PANNING; m.vol = param / 2; } cmd = CMD_NONE; } } break; } if(cmd != CMD_NONE) { m.command = cmd; m.param = param; } } } } static bool ValidateHeader(const AMFFileHeader &fileHeader) { if(std::memcmp(fileHeader.amf, "AMF", 3) || (fileHeader.version < 8 && fileHeader.version != 1) || fileHeader.version > 14 || ((fileHeader.numChannels < 1 || fileHeader.numChannels > 32) && fileHeader.version >= 9)) { return false; } return true; } CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderAMF_DSMI(MemoryFileReader file, const uint64 *pfilesize) { AMFFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return ProbeWantMoreData; } if(!ValidateHeader(fileHeader)) { return ProbeFailure; } MPT_UNREFERENCED_PARAMETER(pfilesize); return ProbeSuccess; } bool CSoundFile::ReadAMF_DSMI(FileReader &file, ModLoadingFlags loadFlags) { file.Rewind(); AMFFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return false; } if(!ValidateHeader(fileHeader)) { return false; } if(loadFlags == onlyVerifyHeader) { return true; } InitializeGlobals(MOD_TYPE_AMF); InitializeChannels(); m_modFormat.formatName = MPT_UFORMAT("DSMI v{}")(fileHeader.version); m_modFormat.type = U_("amf"); m_modFormat.charset = mpt::Charset::CP437; m_nChannels = fileHeader.numChannels; m_nSamples = fileHeader.numSamples; m_songName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileHeader.title); if(fileHeader.version < 9) { // Old format revisions are fixed to 4 channels m_nChannels = 4; file.SkipBack(1); SetupMODPanning(true); } // Setup Channel Pan Positions if(fileHeader.version >= 11) { const CHANNELINDEX readChannels = fileHeader.version >= 12 ? 32 : 16; for(CHANNELINDEX chn = 0; chn < readChannels; chn++) { int8 pan = file.ReadInt8(); if(pan == 100) ChnSettings[chn].dwFlags = CHN_SURROUND; else ChnSettings[chn].nPan = static_cast(std::clamp((pan + 64) * 2, 0, 256)); } } else if(fileHeader.version >= 9) { uint8 panPos[16]; file.ReadArray(panPos); for(CHANNELINDEX chn = 0; chn < 16; chn++) { ChnSettings[chn].nPan = (panPos[chn] & 1) ? 0x40 : 0xC0; } } // Get Tempo/Speed if(fileHeader.version >= 13) { auto [tempo, speed] = file.ReadArray(); if(tempo < 32) tempo = 125; m_nDefaultTempo.Set(tempo); m_nDefaultSpeed = speed; } else { m_nDefaultTempo.Set(125); m_nDefaultSpeed = 6; } // Setup Order List Order().resize(fileHeader.numOrders); std::vector patternLength; const FileReader::off_t trackStartPos = file.GetPosition() + (fileHeader.version >= 14 ? 2 : 0); if(fileHeader.version >= 14) { patternLength.resize(fileHeader.numOrders); } for(ORDERINDEX ord = 0; ord < fileHeader.numOrders; ord++) { Order()[ord] = ord; if(fileHeader.version >= 14) { patternLength[ord] = file.ReadUint16LE(); } // Track positions will be read as needed. file.Skip(m_nChannels * 2); } // Read Sample Headers bool truncatedSampleHeaders = false; if(fileHeader.version == 10) { // M2AMF 1.3 included with DMP 2.32 wrote new (v10+) sample headers, but using the old struct length. const auto startPos = file.GetPosition(); for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++) { AMFSampleHeaderNew sample; if(file.ReadStruct(sample) && !sample.IsValid(fileHeader.numSamples)) { truncatedSampleHeaders = true; break; } } file.Seek(startPos); } std::vector sampleMap(GetNumSamples(), 0); for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++) { if(fileHeader.version < 10) { AMFSampleHeaderOld sample; file.ReadStruct(sample); sample.ConvertToMPT(Samples[smp]); m_szNames[smp] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, sample.name); sampleMap[smp - 1] = sample.index; } else { AMFSampleHeaderNew sample; file.ReadStructPartial(sample, truncatedSampleHeaders ? sizeof(AMFSampleHeaderOld) : sizeof(AMFSampleHeaderNew)); sample.ConvertToMPT(Samples[smp], truncatedSampleHeaders); m_szNames[smp] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, sample.name); sampleMap[smp - 1] = sample.index; } } // Read Track Mapping Table std::vector trackMap; if(!file.ReadVector(trackMap, fileHeader.numTracks)) { return false; } uint16 trackCount = 0; if(!trackMap.empty()) trackCount = *std::max_element(trackMap.cbegin(), trackMap.cend()); // Read pattern tracks std::vector trackData(trackCount); for(uint16 i = 0; i < trackCount; i++) { // Track size is a 16-Bit value describing the number of byte triplets in this track, followed by a track type byte. uint16 numEvents = file.ReadUint16LE(); file.Skip(1); if(numEvents) trackData[i] = file.ReadChunk(numEvents * 3 + (fileHeader.version == 1 ? 3 : 0)); } if(loadFlags & loadSampleData) { // Read Sample Data const SampleIO sampleIO( SampleIO::_8bit, SampleIO::mono, SampleIO::littleEndian, SampleIO::unsignedPCM); // Note: in theory a sample can be reused by several instruments and appear in a different order in the file // However, M2AMF doesn't take advantage of this and just writes instruments in the order they appear, // without de-duplicating identical sample data. for(SAMPLEINDEX smp = 1; smp <= GetNumSamples() && file.CanRead(1); smp++) { auto startPos = file.GetPosition(); for(SAMPLEINDEX target = 0; target < GetNumSamples(); target++) { if(sampleMap[target] != smp) continue; file.Seek(startPos); sampleIO.ReadSample(Samples[target + 1], file); } } } if(!(loadFlags & loadPatternData)) { return true; } // Create the patterns from the list of tracks Patterns.ResizeArray(fileHeader.numOrders); for(PATTERNINDEX pat = 0; pat < fileHeader.numOrders; pat++) { uint16 patLength = pat < patternLength.size() ? patternLength[pat] : 64; if(!Patterns.Insert(pat, patLength)) { continue; } // Get table with per-channel track assignments file.Seek(trackStartPos + pat * (GetNumChannels() * 2 + (fileHeader.version >= 14 ? 2 : 0))); std::vector tracks; if(!file.ReadVector(tracks, GetNumChannels())) { continue; } for(CHANNELINDEX chn = 0; chn < GetNumChannels(); chn++) { if(tracks[chn] > 0 && tracks[chn] <= fileHeader.numTracks) { uint16 realTrack = trackMap[tracks[chn] - 1]; if(realTrack > 0 && realTrack <= trackCount) { realTrack--; AMFReadPattern(Patterns[pat], chn, trackData[realTrack]); } } } } return true; } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Load_ams.cpp0000644000175000017500000007250614154725456020345 00000000000000/* * Load_ams.cpp * ------------ * Purpose: AMS (Extreme's Tracker / Velvet Studio) module loader * Notes : Extreme was renamed to Velvet Development at some point, * and thus they also renamed their tracker from * "Extreme's Tracker" to "Velvet Studio". * While the two programs look rather similiar, the structure of both * programs' "AMS" format is significantly different in some places - * Velvet Studio is a rather advanced tracker in comparison to Extreme's Tracker. * The source code of Velvet Studio has been released into the * public domain in 2013: https://github.com/Patosc/VelvetStudio/commits/master * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Loaders.h" OPENMPT_NAMESPACE_BEGIN // Read AMS or AMS2 (newVersion = true) pattern. At least this part of the format is more or less identical between the two trackers... static void ReadAMSPattern(CPattern &pattern, bool newVersion, FileReader &patternChunk) { enum { emptyRow = 0xFF, // No commands on row endOfRowMask = 0x80, // If set, no more commands on this row noteMask = 0x40, // If set, no note+instr in this command channelMask = 0x1F, // Mask for extracting channel // Note flags readNextCmd = 0x80, // One more command follows noteDataMask = 0x7F, // Extract note // Command flags volCommand = 0x40, // Effect is compressed volume command commandMask = 0x3F, // Command or volume mask }; // Effect translation table for extended (non-Protracker) effects static constexpr ModCommand::COMMAND effTrans[] = { CMD_S3MCMDEX, // Forward / Backward CMD_PORTAMENTOUP, // Extra fine slide up CMD_PORTAMENTODOWN, // Extra fine slide up CMD_RETRIG, // Retrigger CMD_NONE, CMD_TONEPORTAVOL, // Toneporta with fine volume slide CMD_VIBRATOVOL, // Vibrato with fine volume slide CMD_NONE, CMD_PANNINGSLIDE, CMD_NONE, CMD_VOLUMESLIDE, // Two times finder volume slide than Axx CMD_NONE, CMD_CHANNELVOLUME, // Channel volume (0...127) CMD_PATTERNBREAK, // Long pattern break (in hex) CMD_S3MCMDEX, // Fine slide commands CMD_NONE, // Fractional BPM CMD_KEYOFF, // Key off at tick xx CMD_PORTAMENTOUP, // Porta up, but uses all octaves (?) CMD_PORTAMENTODOWN, // Porta down, but uses all octaves (?) CMD_NONE, CMD_NONE, CMD_NONE, CMD_NONE, CMD_NONE, CMD_NONE, CMD_NONE, CMD_GLOBALVOLSLIDE, // Global volume slide CMD_NONE, CMD_GLOBALVOLUME, // Global volume (0... 127) }; ModCommand dummy; for(ROWINDEX row = 0; row < pattern.GetNumRows(); row++) { PatternRow baseRow = pattern.GetRow(row); while(patternChunk.CanRead(1)) { const uint8 flags = patternChunk.ReadUint8(); if(flags == emptyRow) { break; } const CHANNELINDEX chn = (flags & channelMask); ModCommand &m = chn < pattern.GetNumChannels() ? baseRow[chn] : dummy; bool moreCommands = true; if(!(flags & noteMask)) { // Read note + instr uint8 note = patternChunk.ReadUint8(); moreCommands = (note & readNextCmd) != 0; note &= noteDataMask; if(note == 1) { m.note = NOTE_KEYOFF; } else if(note >= 2 && note <= 121 && newVersion) { m.note = note - 2 + NOTE_MIN; } else if(note >= 12 && note <= 108 && !newVersion) { m.note = note + 12 + NOTE_MIN; } m.instr = patternChunk.ReadUint8(); } while(moreCommands) { // Read one more effect command ModCommand origCmd = m; const uint8 command = patternChunk.ReadUint8(), effect = (command & commandMask); moreCommands = (command & readNextCmd) != 0; if(command & volCommand) { m.volcmd = VOLCMD_VOLUME; m.vol = effect; } else { m.param = patternChunk.ReadUint8(); if(effect < 0x10) { // PT commands m.command = effect; CSoundFile::ConvertModCommand(m); // Post-fix some commands switch(m.command) { case CMD_PANNING8: // 4-Bit panning m.command = CMD_PANNING8; m.param = (m.param & 0x0F) * 0x11; break; case CMD_VOLUME: m.command = CMD_NONE; m.volcmd = VOLCMD_VOLUME; m.vol = static_cast(std::min((m.param + 1) / 2, 64)); break; case CMD_MODCMDEX: if(m.param == 0x80) { // Break sample loop (cut after loop) m.command = CMD_NONE; } else { m.ExtendedMODtoS3MEffect(); } break; } } else if(effect < 0x10 + mpt::array_size::size) { // Extended commands m.command = effTrans[effect - 0x10]; // Post-fix some commands switch(effect) { case 0x10: // Play sample forwards / backwards if(m.param <= 0x01) { m.param |= 0x9E; } else { m.command = CMD_NONE; } break; case 0x11: case 0x12: // Extra fine slides m.param = static_cast(std::min(uint8(0x0F), m.param) | 0xE0); break; case 0x15: case 0x16: // Fine slides m.param = static_cast((std::min(0x10, m.param + 1) / 2) | 0xF0); break; case 0x1E: // More fine slides switch(m.param >> 4) { case 0x1: // Fine porta up m.command = CMD_PORTAMENTOUP; m.param |= 0xF0; break; case 0x2: // Fine porta down m.command = CMD_PORTAMENTODOWN; m.param |= 0xF0; break; case 0xA: // Extra fine volume slide up m.command = CMD_VOLUMESLIDE; m.param = ((((m.param & 0x0F) + 1) / 2) << 4) | 0x0F; break; case 0xB: // Extra fine volume slide down m.command = CMD_VOLUMESLIDE; m.param = (((m.param & 0x0F) + 1) / 2) | 0xF0; break; default: m.command = CMD_NONE; break; } break; case 0x1C: // Adjust channel volume range m.param = static_cast(std::min((m.param + 1) / 2, 64)); break; } } // Try merging commands first ModCommand::CombineEffects(m.command, m.param, origCmd.command, origCmd.param); if(ModCommand::GetEffectWeight(origCmd.command) > ModCommand::GetEffectWeight(m.command)) { if(m.volcmd == VOLCMD_NONE && ModCommand::ConvertVolEffect(m.command, m.param, true)) { // Volume column to the rescue! m.volcmd = m.command; m.vol = m.param; } m.command = origCmd.command; m.param = origCmd.param; } } } if(flags & endOfRowMask) { // End of row break; } } } } ///////////////////////////////////////////////////////////////////// // AMS (Extreme's Tracker) 1.x loader // AMS File Header struct AMSFileHeader { uint8le versionLow; uint8le versionHigh; uint8le channelConfig; uint8le numSamps; uint16le numPats; uint16le numOrds; uint8le midiChannels; uint16le extraSize; }; MPT_BINARY_STRUCT(AMSFileHeader, 11) // AMS Sample Header struct AMSSampleHeader { enum SampleFlags { smp16BitOld = 0x04, // AMS 1.0 (at least according to docs, I yet have to find such a file) smp16Bit = 0x80, // AMS 1.1+ smpPacked = 0x03, }; uint32le length; uint32le loopStart; uint32le loopEnd; uint8le panFinetune; // High nibble = pan position, low nibble = finetune value uint16le sampleRate; uint8le volume; // 0...127 uint8le flags; // See SampleFlags // Convert sample header to OpenMPT's internal format. void ConvertToMPT(ModSample &mptSmp) const { mptSmp.Initialize(); mptSmp.nLength = length; mptSmp.nLoopStart = std::min(loopStart, length); mptSmp.nLoopEnd = std::min(loopEnd, length); mptSmp.nVolume = (std::min(uint8(127), volume.get()) * 256 + 64) / 127; if(panFinetune & 0xF0) { mptSmp.nPan = (panFinetune & 0xF0); mptSmp.uFlags = CHN_PANNING; } mptSmp.nC5Speed = 2 * sampleRate; if(sampleRate == 0) { mptSmp.nC5Speed = 2 * 8363; } uint32 newC4speed = ModSample::TransposeToFrequency(0, MOD2XMFineTune(panFinetune & 0x0F)); mptSmp.nC5Speed = (mptSmp.nC5Speed * newC4speed) / 8363; if(mptSmp.nLoopStart < mptSmp.nLoopEnd) { mptSmp.uFlags.set(CHN_LOOP); } if(flags & (smp16Bit | smp16BitOld)) { mptSmp.uFlags.set(CHN_16BIT); } } }; MPT_BINARY_STRUCT(AMSSampleHeader, 17) static bool ValidateHeader(const AMSFileHeader &fileHeader) { if(fileHeader.versionHigh != 0x01) { return false; } return true; } static uint64 GetHeaderMinimumAdditionalSize(const AMSFileHeader &fileHeader) { return fileHeader.extraSize + 3u + fileHeader.numSamps * (1u + sizeof(AMSSampleHeader)) + fileHeader.numOrds * 2u + fileHeader.numPats * 4u; } CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderAMS(MemoryFileReader file, const uint64 *pfilesize) { if(!file.CanRead(7)) { return ProbeWantMoreData; } if(!file.ReadMagic("Extreme")) { return ProbeFailure; } AMSFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return ProbeWantMoreData; } if(!ValidateHeader(fileHeader)) { return ProbeFailure; } return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); } bool CSoundFile::ReadAMS(FileReader &file, ModLoadingFlags loadFlags) { file.Rewind(); if(!file.ReadMagic("Extreme")) { return false; } AMSFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return false; } if(!ValidateHeader(fileHeader)) { return false; } if(!file.CanRead(mpt::saturate_cast(GetHeaderMinimumAdditionalSize(fileHeader)))) { return false; } if(!file.Skip(fileHeader.extraSize)) { return false; } if(loadFlags == onlyVerifyHeader) { return true; } InitializeGlobals(MOD_TYPE_AMS); m_SongFlags = SONG_ITCOMPATGXX | SONG_ITOLDEFFECTS; m_nChannels = (fileHeader.channelConfig & 0x1F) + 1; m_nSamples = fileHeader.numSamps; SetupMODPanning(true); m_modFormat.formatName = U_("Extreme's Tracker"); m_modFormat.type = U_("ams"); m_modFormat.madeWithTracker = MPT_UFORMAT("Extreme's Tracker {}.{}")(fileHeader.versionHigh, fileHeader.versionLow); m_modFormat.charset = mpt::Charset::CP437; std::vector packSample(fileHeader.numSamps); static_assert(MAX_SAMPLES > 255); for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++) { AMSSampleHeader sampleHeader; file.ReadStruct(sampleHeader); sampleHeader.ConvertToMPT(Samples[smp]); packSample[smp - 1] = (sampleHeader.flags & AMSSampleHeader::smpPacked) != 0; } // Texts file.ReadSizedString(m_songName); // Read sample names for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++) { file.ReadSizedString(m_szNames[smp]); } // Read channel names for(CHANNELINDEX chn = 0; chn < GetNumChannels(); chn++) { ChnSettings[chn].Reset(); file.ReadSizedString(ChnSettings[chn].szName); } // Read pattern names and create patterns Patterns.ResizeArray(fileHeader.numPats); for(PATTERNINDEX pat = 0; pat < fileHeader.numPats; pat++) { char name[11]; const bool ok = file.ReadSizedString(name); // Create pattern now, so name won't be reset later. if(Patterns.Insert(pat, 64) && ok) { Patterns[pat].SetName(name); } } // Read packed song message const uint16 packedLength = file.ReadUint16LE(); if(packedLength && file.CanRead(packedLength)) { std::vector textIn; file.ReadVector(textIn, packedLength); std::string textOut; textOut.reserve(packedLength); for(auto c : textIn) { if(c & 0x80) { textOut.insert(textOut.end(), (c & 0x7F), ' '); } else { textOut.push_back(c); } } textOut = mpt::ToCharset(mpt::Charset::CP437, mpt::Charset::CP437AMS, textOut); // Packed text doesn't include any line breaks! m_songMessage.ReadFixedLineLength(mpt::byte_cast(textOut.c_str()), textOut.length(), 76, 0); } // Read Order List ReadOrderFromFile(Order(), file, fileHeader.numOrds); // Read patterns for(PATTERNINDEX pat = 0; pat < fileHeader.numPats && file.CanRead(4); pat++) { uint32 patLength = file.ReadUint32LE(); FileReader patternChunk = file.ReadChunk(patLength); if((loadFlags & loadPatternData) && Patterns.IsValidPat(pat)) { ReadAMSPattern(Patterns[pat], false, patternChunk); } } if(loadFlags & loadSampleData) { // Read Samples for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++) { SampleIO( Samples[smp].uFlags[CHN_16BIT] ? SampleIO::_16bit : SampleIO::_8bit, SampleIO::mono, SampleIO::littleEndian, packSample[smp - 1] ? SampleIO::AMS : SampleIO::signedPCM) .ReadSample(Samples[smp], file); } } return true; } ///////////////////////////////////////////////////////////////////// // AMS (Velvet Studio) 2.0 - 2.02 loader // AMS2 File Header struct AMS2FileHeader { enum FileFlags { linearSlides = 0x40, }; uint8le versionLow; // Version of format (Hi = MainVer, Low = SubVer e.g. 0202 = 2.02) uint8le versionHigh; // ditto uint8le numIns; // Nr of Instruments (0-255) uint16le numPats; // Nr of Patterns (1-1024) uint16le numOrds; // Nr of Positions (1-65535) // Rest of header differs between format revision 2.01 and 2.02 }; MPT_BINARY_STRUCT(AMS2FileHeader, 7) // AMS2 Instument Envelope struct AMS2Envelope { uint8 speed; // Envelope speed (currently not supported, always the same as current BPM) uint8 sustainPoint; // Envelope sustain point uint8 loopStart; // Envelope loop Start uint8 loopEnd; // Envelope loop End uint8 numPoints; // Envelope length // Read envelope and do partial conversion. void ConvertToMPT(InstrumentEnvelope &mptEnv, FileReader &file) { file.ReadStruct(*this); // Read envelope points uint8 data[64][3]; file.ReadStructPartial(data, numPoints * 3); if(numPoints <= 1) { // This is not an envelope. return; } static_assert(MAX_ENVPOINTS >= std::size(data)); mptEnv.resize(std::min(numPoints, mpt::saturate_cast(std::size(data)))); mptEnv.nLoopStart = loopStart; mptEnv.nLoopEnd = loopEnd; mptEnv.nSustainStart = mptEnv.nSustainEnd = sustainPoint; for(uint32 i = 0; i < mptEnv.size(); i++) { if(i != 0) { mptEnv[i].tick = mptEnv[i - 1].tick + static_cast(std::max(1, data[i][0] | ((data[i][1] & 0x01) << 8))); } mptEnv[i].value = data[i][2]; } } }; MPT_BINARY_STRUCT(AMS2Envelope, 5) // AMS2 Instrument Data struct AMS2Instrument { enum EnvelopeFlags { envLoop = 0x01, envSustain = 0x02, envEnabled = 0x04, // Flag shift amounts volEnvShift = 0, panEnvShift = 1, vibEnvShift = 2, vibAmpMask = 0x3000, vibAmpShift = 12, fadeOutMask = 0xFFF, }; uint8le shadowInstr; // Shadow Instrument. If non-zero, the value=the shadowed inst. uint16le vibampFadeout; // Vib.Amplify + Volume fadeout in one variable! uint16le envFlags; // See EnvelopeFlags void ApplyFlags(InstrumentEnvelope &mptEnv, EnvelopeFlags shift) const { const int flags = envFlags >> (shift * 3); mptEnv.dwFlags.set(ENV_ENABLED, (flags & envEnabled) != 0); mptEnv.dwFlags.set(ENV_LOOP, (flags & envLoop) != 0); mptEnv.dwFlags.set(ENV_SUSTAIN, (flags & envSustain) != 0); // "Break envelope" should stop the envelope loop when encountering a note-off... We can only use the sustain loop to emulate this behaviour. if(!(flags & envSustain) && (flags & envLoop) != 0 && (flags & (1 << (9 - shift * 2))) != 0) { mptEnv.nSustainStart = mptEnv.nLoopStart; mptEnv.nSustainEnd = mptEnv.nLoopEnd; mptEnv.dwFlags.set(ENV_SUSTAIN); mptEnv.dwFlags.reset(ENV_LOOP); } } }; MPT_BINARY_STRUCT(AMS2Instrument, 5) // AMS2 Sample Header struct AMS2SampleHeader { enum SampleFlags { smpPacked = 0x03, smp16Bit = 0x04, smpLoop = 0x08, smpBidiLoop = 0x10, smpReverse = 0x40, }; uint32le length; uint32le loopStart; uint32le loopEnd; uint16le sampledRate; // Whyyyy? uint8le panFinetune; // High nibble = pan position, low nibble = finetune value uint16le c4speed; // Why is all of this so redundant? int8le relativeTone; // q.e.d. uint8le volume; // 0...127 uint8le flags; // See SampleFlags // Convert sample header to OpenMPT's internal format. void ConvertToMPT(ModSample &mptSmp) const { mptSmp.Initialize(); mptSmp.nLength = length; mptSmp.nLoopStart = std::min(loopStart, length); mptSmp.nLoopEnd = std::min(loopEnd, length); mptSmp.nC5Speed = c4speed * 2; if(c4speed == 0) { mptSmp.nC5Speed = 8363 * 2; } // Why, oh why, does this format need a c5speed and transpose/finetune at the same time... uint32 newC4speed = ModSample::TransposeToFrequency(relativeTone, MOD2XMFineTune(panFinetune & 0x0F)); mptSmp.nC5Speed = (mptSmp.nC5Speed * newC4speed) / 8363; mptSmp.nVolume = (std::min(volume.get(), uint8(127)) * 256 + 64) / 127; if(panFinetune & 0xF0) { mptSmp.nPan = (panFinetune & 0xF0); mptSmp.uFlags = CHN_PANNING; } if(flags & smp16Bit) mptSmp.uFlags.set(CHN_16BIT); if((flags & smpLoop) && mptSmp.nLoopStart < mptSmp.nLoopEnd) { mptSmp.uFlags.set(CHN_LOOP); if(flags & smpBidiLoop) mptSmp.uFlags.set(CHN_PINGPONGLOOP); if(flags & smpReverse) mptSmp.uFlags.set(CHN_REVERSE); } } }; MPT_BINARY_STRUCT(AMS2SampleHeader, 20) // AMS2 Song Description Header struct AMS2Description { uint32le packedLen; // Including header uint32le unpackedLen; uint8le packRoutine; // 01 uint8le preProcessing; // None! uint8le packingMethod; // RLE }; MPT_BINARY_STRUCT(AMS2Description, 11) static bool ValidateHeader(const AMS2FileHeader &fileHeader) { if(fileHeader.versionHigh != 2 || fileHeader.versionLow > 2) { return false; } return true; } static uint64 GetHeaderMinimumAdditionalSize(const AMS2FileHeader &fileHeader) { return 36u + sizeof(AMS2Description) + fileHeader.numIns * 2u + fileHeader.numOrds * 2u + fileHeader.numPats * 4u; } CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderAMS2(MemoryFileReader file, const uint64 *pfilesize) { if(!file.CanRead(7)) { return ProbeWantMoreData; } if(!file.ReadMagic("AMShdr\x1A")) { return ProbeFailure; } if(!file.CanRead(1)) { return ProbeWantMoreData; } const uint8 songNameLength = file.ReadUint8(); if(!file.Skip(songNameLength)) { return ProbeWantMoreData; } AMS2FileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return ProbeWantMoreData; } if(!ValidateHeader(fileHeader)) { return ProbeFailure; } return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); } bool CSoundFile::ReadAMS2(FileReader &file, ModLoadingFlags loadFlags) { file.Rewind(); if(!file.ReadMagic("AMShdr\x1A")) { return false; } std::string songName; if(!file.ReadSizedString(songName)) { return false; } AMS2FileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return false; } if(!ValidateHeader(fileHeader)) { return false; } if(!file.CanRead(mpt::saturate_cast(GetHeaderMinimumAdditionalSize(fileHeader)))) { return false; } if(loadFlags == onlyVerifyHeader) { return true; } InitializeGlobals(MOD_TYPE_AMS); m_songName = songName; m_nInstruments = fileHeader.numIns; m_nChannels = 32; SetupMODPanning(true); m_modFormat.formatName = U_("Velvet Studio"); m_modFormat.type = U_("ams"); m_modFormat.madeWithTracker = MPT_UFORMAT("Velvet Studio {}.{}")(fileHeader.versionHigh.get(), mpt::ufmt::dec0<2>(fileHeader.versionLow.get())); m_modFormat.charset = mpt::Charset::CP437; uint16 headerFlags; if(fileHeader.versionLow >= 2) { uint16 tempo = std::max(uint16(32 << 8), file.ReadUint16LE()); // 8.8 tempo m_nDefaultTempo.SetRaw((tempo * TEMPO::fractFact) >> 8); m_nDefaultSpeed = std::max(uint8(1), file.ReadUint8()); file.Skip(3); // Default values for pattern editor headerFlags = file.ReadUint16LE(); } else { m_nDefaultTempo.Set(std::max(uint8(32), file.ReadUint8())); m_nDefaultSpeed = std::max(uint8(1), file.ReadUint8()); headerFlags = file.ReadUint8(); } m_SongFlags = SONG_ITCOMPATGXX | SONG_ITOLDEFFECTS | ((headerFlags & AMS2FileHeader::linearSlides) ? SONG_LINEARSLIDES : SongFlags(0)); // Instruments std::vector firstSample; // First sample of instrument std::vector sampleSettings; // Shadow sample map... Lo byte = Instrument, Hi byte, lo nibble = Sample index in instrument, Hi byte, hi nibble = Sample pack status enum { instrIndexMask = 0xFF, // Shadow instrument sampleIndexMask = 0x7F00, // Sample index in instrument sampleIndexShift = 8, packStatusMask = 0x8000, // If bit is set, sample is packed }; static_assert(MAX_INSTRUMENTS > 255); for(INSTRUMENTINDEX ins = 1; ins <= m_nInstruments; ins++) { ModInstrument *instrument = AllocateInstrument(ins); if(instrument == nullptr || !file.ReadSizedString(instrument->name)) { break; } uint8 numSamples = file.ReadUint8(); uint8 sampleAssignment[120]; MemsetZero(sampleAssignment); // Only really needed for v2.0, where the lowest and highest octave aren't cleared. if(numSamples == 0 || (fileHeader.versionLow > 0 && !file.ReadArray(sampleAssignment)) // v2.01+: 120 Notes || (fileHeader.versionLow == 0 && !file.ReadRaw(mpt::span(sampleAssignment + 12, 96)).size())) // v2.0: 96 Notes { continue; } static_assert(mpt::array_sizeKeyboard)>::size >= std::size(sampleAssignment)); for(size_t i = 0; i < 120; i++) { instrument->Keyboard[i] = sampleAssignment[i] + GetNumSamples() + 1; } AMS2Envelope volEnv, panEnv, vibratoEnv; volEnv.ConvertToMPT(instrument->VolEnv, file); panEnv.ConvertToMPT(instrument->PanEnv, file); vibratoEnv.ConvertToMPT(instrument->PitchEnv, file); AMS2Instrument instrHeader; file.ReadStruct(instrHeader); instrument->nFadeOut = (instrHeader.vibampFadeout & AMS2Instrument::fadeOutMask); const int16 vibAmp = 1 << ((instrHeader.vibampFadeout & AMS2Instrument::vibAmpMask) >> AMS2Instrument::vibAmpShift); instrHeader.ApplyFlags(instrument->VolEnv, AMS2Instrument::volEnvShift); instrHeader.ApplyFlags(instrument->PanEnv, AMS2Instrument::panEnvShift); instrHeader.ApplyFlags(instrument->PitchEnv, AMS2Instrument::vibEnvShift); // Scale envelopes to correct range for(auto &p : instrument->VolEnv) { p.value = std::min(uint8(ENVELOPE_MAX), static_cast((p.value * ENVELOPE_MAX + 64u) / 127u)); } for(auto &p : instrument->PanEnv) { p.value = std::min(uint8(ENVELOPE_MAX), static_cast((p.value * ENVELOPE_MAX + 128u) / 255u)); } for(auto &p : instrument->PitchEnv) { #ifdef MODPLUG_TRACKER p.value = std::min(uint8(ENVELOPE_MAX), static_cast(32 + Util::muldivrfloor(static_cast(p.value - 128), vibAmp, 255))); #else // Try to keep as much precision as possible... divide by 8 since that's the highest possible vibAmp factor. p.value = static_cast(128 + Util::muldivrfloor(static_cast(p.value - 128), vibAmp, 8)); #endif } // Sample headers - we will have to read them even for shadow samples, and we will have to load them several times, // as it is possible that shadow samples use different sample settings like base frequency or panning. const SAMPLEINDEX firstSmp = GetNumSamples() + 1; for(SAMPLEINDEX smp = 0; smp < numSamples; smp++) { if(firstSmp + smp >= MAX_SAMPLES) { file.Skip(sizeof(AMS2SampleHeader)); break; } file.ReadSizedString(m_szNames[firstSmp + smp]); AMS2SampleHeader sampleHeader; file.ReadStruct(sampleHeader); sampleHeader.ConvertToMPT(Samples[firstSmp + smp]); uint16 settings = (instrHeader.shadowInstr & instrIndexMask) | ((smp << sampleIndexShift) & sampleIndexMask) | ((sampleHeader.flags & AMS2SampleHeader::smpPacked) ? packStatusMask : 0); sampleSettings.push_back(settings); } firstSample.push_back(firstSmp); m_nSamples = static_cast(std::min(MAX_SAMPLES - 1, GetNumSamples() + numSamples)); } // Text // Read composer name if(std::string composer; file.ReadSizedString(composer)) { m_songArtist = mpt::ToUnicode(mpt::Charset::CP437AMS2, composer); } // Channel names for(CHANNELINDEX chn = 0; chn < 32; chn++) { ChnSettings[chn].Reset(); file.ReadSizedString(ChnSettings[chn].szName); } // RLE-Packed description text AMS2Description descriptionHeader; if(!file.ReadStruct(descriptionHeader)) { return true; } if(descriptionHeader.packedLen > sizeof(descriptionHeader) && file.CanRead(descriptionHeader.packedLen - sizeof(descriptionHeader))) { const uint32 textLength = descriptionHeader.packedLen - static_cast(sizeof(descriptionHeader)); std::vector textIn; file.ReadVector(textIn, textLength); // In the best case, every byte triplet can decode to 255 bytes, which is a ratio of exactly 1:85 const uint32 maxLength = std::min(textLength, Util::MaxValueOfType(textLength) / 85u) * 85u; std::string textOut; textOut.reserve(std::min(maxLength, descriptionHeader.unpackedLen.get())); size_t readLen = 0; while(readLen < textLength) { uint8 c = textIn[readLen++]; if(c == 0xFF && textLength - readLen >= 2) { c = textIn[readLen++]; uint32 count = textIn[readLen++]; textOut.insert(textOut.end(), count, c); } else { textOut.push_back(c); } } textOut = mpt::ToCharset(mpt::Charset::CP437, mpt::Charset::CP437AMS2, textOut); // Packed text doesn't include any line breaks! m_songMessage.ReadFixedLineLength(mpt::byte_cast(textOut.c_str()), textOut.length(), 74, 0); } // Read Order List ReadOrderFromFile(Order(), file, fileHeader.numOrds); // Read Patterns if(loadFlags & loadPatternData) Patterns.ResizeArray(fileHeader.numPats); for(PATTERNINDEX pat = 0; pat < fileHeader.numPats && file.CanRead(4); pat++) { uint32 patLength = file.ReadUint32LE(); FileReader patternChunk = file.ReadChunk(patLength); if(loadFlags & loadPatternData) { const ROWINDEX numRows = patternChunk.ReadUint8() + 1; // We don't need to know the number of channels or commands. patternChunk.Skip(1); if(!Patterns.Insert(pat, numRows)) { continue; } char patternName[11]; if(patternChunk.ReadSizedString(patternName)) Patterns[pat].SetName(patternName); ReadAMSPattern(Patterns[pat], true, patternChunk); } } if(!(loadFlags & loadSampleData)) { return true; } // Read Samples for(SAMPLEINDEX smp = 0; smp < GetNumSamples(); smp++) { if((sampleSettings[smp] & instrIndexMask) == 0) { // Only load samples that aren't part of a shadow instrument SampleIO( (Samples[smp + 1].uFlags & CHN_16BIT) ? SampleIO::_16bit : SampleIO::_8bit, SampleIO::mono, SampleIO::littleEndian, (sampleSettings[smp] & packStatusMask) ? SampleIO::AMS : SampleIO::signedPCM) .ReadSample(Samples[smp + 1], file); } } // Copy shadow samples for(SAMPLEINDEX smp = 0; smp < GetNumSamples(); smp++) { INSTRUMENTINDEX sourceInstr = (sampleSettings[smp] & instrIndexMask); if(sourceInstr == 0 || --sourceInstr >= firstSample.size()) { continue; } SAMPLEINDEX sourceSample = ((sampleSettings[smp] & sampleIndexMask) >> sampleIndexShift) + firstSample[sourceInstr]; if(sourceSample > GetNumSamples() || !Samples[sourceSample].HasSampleData()) { continue; } // Copy over original sample ModSample &sample = Samples[smp + 1]; ModSample &source = Samples[sourceSample]; sample.uFlags.set(CHN_16BIT, source.uFlags[CHN_16BIT]); sample.nLength = source.nLength; if(sample.AllocateSample()) { memcpy(sample.sampleb(), source.sampleb(), source.GetSampleSizeInBytes()); } } return true; } ///////////////////////////////////////////////////////////////////// // AMS Sample unpacking void AMSUnpack(const int8 * const source, size_t sourceSize, void * const dest, const size_t destSize, char packCharacter) { std::vector tempBuf(destSize, 0); size_t depackSize = destSize; // Unpack Loop { const int8 *in = source; int8 *out = tempBuf.data(); size_t i = sourceSize, j = destSize; while(i != 0 && j != 0) { int8 ch = *(in++); if(--i != 0 && ch == packCharacter) { uint8 repCount = *(in++); repCount = static_cast(std::min(static_cast(repCount), j)); if(--i != 0 && repCount) { ch = *(in++); i--; while(repCount-- != 0) { *(out++) = ch; j--; } } else { *(out++) = packCharacter; j--; } } else { *(out++) = ch; j--; } } // j should only be non-zero for truncated samples depackSize -= j; } // Bit Unpack Loop { int8 *out = tempBuf.data(); uint16 bitcount = 0x80; size_t k = 0; uint8 *dst = static_cast(dest); for(size_t i = 0; i < depackSize; i++) { uint8 al = *out++; uint16 dh = 0; for(uint16 count = 0; count < 8; count++) { uint16 bl = al & bitcount; bl = ((bl | (bl << 8)) >> ((dh + 8 - count) & 7)) & 0xFF; bitcount = ((bitcount | (bitcount << 8)) >> 1) & 0xFF; dst[k++] |= bl; if(k >= destSize) { k = 0; dh++; } } bitcount = ((bitcount | (bitcount << 8)) >> dh) & 0xFF; } } // Delta Unpack { int8 old = 0; int8 *out = static_cast(dest); for(size_t i = depackSize; i != 0; i--) { int pos = *reinterpret_cast(out); if(pos != 128 && (pos & 0x80) != 0) { pos = -(pos & 0x7F); } old -= static_cast(pos); *(out++) = old; } } } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Load_c67.cpp0000644000175000017500000001603514047535106020147 00000000000000/* * Load_c67.cpp * ------------ * Purpose: C67 (CDFM Composer) module loader * Notes : C67 is the composer format; 670 files can be converted back to C67 using the converter that comes with CDFM. * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Loaders.h" OPENMPT_NAMESPACE_BEGIN struct C67SampleHeader { uint32le unknown; // Probably placeholder for in-memory address, 0 on disk uint32le length; uint32le loopStart; uint32le loopEnd; }; MPT_BINARY_STRUCT(C67SampleHeader, 16) struct C67FileHeader { uint8 speed; uint8 restartPos; char sampleNames[32][13]; C67SampleHeader samples[32]; char fmInstrNames[32][13]; uint8 fmInstr[32][11]; uint8 orders[256]; }; MPT_BINARY_STRUCT(C67FileHeader, 1954) static bool ValidateHeader(const C67FileHeader &fileHeader) { if(fileHeader.speed < 1 || fileHeader.speed > 15) return false; for(auto ord : fileHeader.orders) { if(ord >= 128 && ord != 0xFF) return false; } bool anyNonSilent = false; for(SAMPLEINDEX smp = 0; smp < 32; smp++) { if(fileHeader.sampleNames[smp][12] != 0 || fileHeader.samples[smp].unknown != 0 || fileHeader.samples[smp].length > 0xFFFFF || fileHeader.fmInstrNames[smp][12] != 0 || (fileHeader.fmInstr[smp][0] & 0xF0) // No OPL3 || (fileHeader.fmInstr[smp][5] & 0xFC) // No OPL3 || (fileHeader.fmInstr[smp][10] & 0xFC)) // No OPL3 { return false; } if(fileHeader.samples[smp].length != 0 && fileHeader.samples[smp].loopEnd < 0xFFFFF) { if(fileHeader.samples[smp].loopEnd > fileHeader.samples[smp].length || fileHeader.samples[smp].loopStart > fileHeader.samples[smp].loopEnd) { return false; } } if(!anyNonSilent && (fileHeader.samples[smp].length != 0 || memcmp(fileHeader.fmInstr[smp], "\0\0\0\0\0\0\0\0\0\0\0", 11))) { anyNonSilent = true; } } return anyNonSilent; } static uint64 GetHeaderMinimumAdditionalSize(const C67FileHeader &) { return 1024; // Pattern offsets and lengths } CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderC67(MemoryFileReader file, const uint64 *pfilesize) { C67FileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return ProbeWantMoreData; } if(!ValidateHeader(fileHeader)) { return ProbeFailure; } return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); } static void TranslateVolume(ModCommand &m, uint8 volume, bool isFM) { // CDFM uses a linear volume scale for FM instruments. // ScreamTracker, on the other hand, directly uses the OPL chip's logarithmic volume scale. // Neither FM nor PCM instruments can be fully muted in CDFM. static constexpr uint8 fmVolume[16] = { 0x08, 0x10, 0x18, 0x20, 0x28, 0x2C, 0x30, 0x34, 0x36, 0x38, 0x3A, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, }; volume &= 0x0F; m.volcmd = VOLCMD_VOLUME; m.vol = isFM ? fmVolume[volume] : (4u + volume * 4u); } bool CSoundFile::ReadC67(FileReader &file, ModLoadingFlags loadFlags) { C67FileHeader fileHeader; file.Rewind(); if(!file.ReadStruct(fileHeader)) { return false; } if(!ValidateHeader(fileHeader)) { return false; } if(loadFlags == onlyVerifyHeader) { return true; } if(!file.CanRead(mpt::saturate_cast(GetHeaderMinimumAdditionalSize(fileHeader)))) { return false; } // Validate pattern offsets and lengths uint32le patOffsets[128], patLengths[128]; file.ReadArray(patOffsets); file.ReadArray(patLengths); for(PATTERNINDEX pat = 0; pat < 128; pat++) { if(patOffsets[pat] > 0xFFFFFF || patLengths[pat] < 3 // Smallest well-formed pattern consists of command 0x40 followed by command 0x60 || patLengths[pat] > 0x1000 // Any well-formed pattern is smaller than this || !file.LengthIsAtLeast(2978 + patOffsets[pat] + patLengths[pat])) { return false; } } InitializeGlobals(MOD_TYPE_S3M); InitializeChannels(); m_modFormat.formatName = U_("CDFM"); m_modFormat.type = U_("c67"); m_modFormat.madeWithTracker = U_("Composer 670"); m_modFormat.charset = mpt::Charset::CP437; m_nDefaultSpeed = fileHeader.speed; m_nDefaultTempo.Set(143); Order().SetRestartPos(fileHeader.restartPos); m_nSamples = 64; m_nChannels = 4 + 9; m_playBehaviour.set(kOPLBeatingOscillators); m_SongFlags.set(SONG_IMPORTED); // Pan PCM channels only for(CHANNELINDEX chn = 0; chn < 4; chn++) { ChnSettings[chn].nPan = (chn & 1) ? 192 : 64; } // PCM instruments for(SAMPLEINDEX smp = 0; smp < 32; smp++) { ModSample &mptSmp = Samples[smp + 1]; mptSmp.Initialize(MOD_TYPE_S3M); m_szNames[smp + 1] = mpt::String::ReadBuf(mpt::String::nullTerminated, fileHeader.sampleNames[smp]); mptSmp.nLength = fileHeader.samples[smp].length; if(fileHeader.samples[smp].loopEnd <= fileHeader.samples[smp].length) { mptSmp.nLoopStart = fileHeader.samples[smp].loopStart; mptSmp.nLoopEnd = fileHeader.samples[smp].loopEnd; mptSmp.uFlags = CHN_LOOP; } mptSmp.nC5Speed = 8287; } // OPL instruments for(SAMPLEINDEX smp = 0; smp < 32; smp++) { ModSample &mptSmp = Samples[smp + 33]; mptSmp.Initialize(MOD_TYPE_S3M); m_szNames[smp + 33] = mpt::String::ReadBuf(mpt::String::nullTerminated, fileHeader.fmInstrNames[smp]); // Reorder OPL patch bytes (interleave modulator and carrier) const auto &fm = fileHeader.fmInstr[smp]; OPLPatch patch{{}}; patch[0] = fm[1]; patch[1] = fm[6]; patch[2] = fm[2]; patch[3] = fm[7]; patch[4] = fm[3]; patch[5] = fm[8]; patch[6] = fm[4]; patch[7] = fm[9]; patch[8] = fm[5]; patch[9] = fm[10]; patch[10] = fm[0]; mptSmp.SetAdlib(true, patch); } ReadOrderFromArray(Order(), fileHeader.orders, 256, 0xFF); Patterns.ResizeArray(128); for(PATTERNINDEX pat = 0; pat < 128; pat++) { file.Seek(2978 + patOffsets[pat]); FileReader patChunk = file.ReadChunk(patLengths[pat]); if(!(loadFlags & loadPatternData) || !Patterns.Insert(pat, 64)) { continue; } CPattern &pattern = Patterns[pat]; ROWINDEX row = 0; while(row < 64 && patChunk.CanRead(1)) { uint8 cmd = patChunk.ReadUint8(); if(cmd <= 0x0C) { // Note, instrument, volume ModCommand &m = *pattern.GetpModCommand(row, cmd); const auto [note, instrVol] = patChunk.ReadArray(); bool fmChn = (cmd >= 4); m.note = NOTE_MIN + (fmChn ? 12 : 36) + (note & 0x0F) + ((note >> 4) & 0x07) * 12; m.instr = (fmChn ? 33 : 1) + (instrVol >> 4) + ((note & 0x80) >> 3); TranslateVolume(m, instrVol, fmChn); } else if(cmd >= 0x20 && cmd <= 0x2C) { // Volume TranslateVolume(*pattern.GetpModCommand(row, cmd - 0x20), patChunk.ReadUint8(), cmd >= 0x24); } else if(cmd == 0x40) { // Delay (row done) row += patChunk.ReadUint8(); } else if(cmd == 0x60) { // End of pattern if(row > 0) { pattern.GetpModCommand(row - 1, 0)->command = CMD_PATTERNBREAK; } break; } else { return false; } } } if(loadFlags & loadSampleData) { for(SAMPLEINDEX smp = 1; smp <= 32; smp++) { SampleIO(SampleIO::_8bit, SampleIO::mono, SampleIO::littleEndian, SampleIO::unsignedPCM).ReadSample(Samples[smp], file); } } return true; } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Load_dbm.cpp0000644000175000017500000004551114175042045020310 00000000000000/* * Load_dbm.cpp * ------------ * Purpose: DigiBooster Pro module Loader (DBM) * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Loaders.h" #include "../common/mptStringBuffer.h" #ifndef NO_PLUGINS #include "plugins/DigiBoosterEcho.h" #endif // NO_PLUGINS #ifdef LIBOPENMPT_BUILD #define MPT_DBM_USE_REAL_SUBSONGS #endif OPENMPT_NAMESPACE_BEGIN struct DBMFileHeader { char dbm0[4]; uint8 trkVerHi; uint8 trkVerLo; char reserved[2]; }; MPT_BINARY_STRUCT(DBMFileHeader, 8) // IFF-style Chunk struct DBMChunk { // 32-Bit chunk identifiers enum ChunkIdentifiers { idNAME = MagicBE("NAME"), idINFO = MagicBE("INFO"), idSONG = MagicBE("SONG"), idINST = MagicBE("INST"), idVENV = MagicBE("VENV"), idPENV = MagicBE("PENV"), idPATT = MagicBE("PATT"), idPNAM = MagicBE("PNAM"), idSMPL = MagicBE("SMPL"), idDSPE = MagicBE("DSPE"), idMPEG = MagicBE("MPEG"), }; uint32be id; uint32be length; size_t GetLength() const { return length; } ChunkIdentifiers GetID() const { return static_cast(id.get()); } }; MPT_BINARY_STRUCT(DBMChunk, 8) struct DBMInfoChunk { uint16be instruments; uint16be samples; uint16be songs; uint16be patterns; uint16be channels; }; MPT_BINARY_STRUCT(DBMInfoChunk, 10) // Instrument header struct DBMInstrument { enum DBMInstrFlags { smpLoop = 0x01, smpPingPongLoop = 0x02, }; char name[30]; uint16be sample; // Sample reference uint16be volume; // 0...64 uint32be sampleRate; uint32be loopStart; uint32be loopLength; int16be panning; // -128...128 uint16be flags; // See DBMInstrFlags }; MPT_BINARY_STRUCT(DBMInstrument, 50) // Volume or panning envelope struct DBMEnvelope { enum DBMEnvelopeFlags { envEnabled = 0x01, envSustain = 0x02, envLoop = 0x04, }; uint16be instrument; uint8be flags; // See DBMEnvelopeFlags uint8be numSegments; // Number of envelope points - 1 uint8be sustain1; uint8be loopBegin; uint8be loopEnd; uint8be sustain2; // Second sustain point uint16be data[2 * 32]; }; MPT_BINARY_STRUCT(DBMEnvelope, 136) // Note: Unlike in MOD, 1Fx, 2Fx, 5Fx / 5xF, 6Fx / 6xF and AFx / AxF are fine slides. static constexpr ModCommand::COMMAND dbmEffects[] = { CMD_ARPEGGIO, CMD_PORTAMENTOUP, CMD_PORTAMENTODOWN, CMD_TONEPORTAMENTO, CMD_VIBRATO, CMD_TONEPORTAVOL, CMD_VIBRATOVOL, CMD_TREMOLO, CMD_PANNING8, CMD_OFFSET, CMD_VOLUMESLIDE, CMD_POSITIONJUMP, CMD_VOLUME, CMD_PATTERNBREAK, CMD_MODCMDEX, CMD_TEMPO, CMD_GLOBALVOLUME, CMD_GLOBALVOLSLIDE, CMD_NONE, CMD_NONE, CMD_KEYOFF, CMD_SETENVPOSITION, CMD_NONE, CMD_NONE, CMD_NONE, CMD_PANNINGSLIDE, CMD_NONE, CMD_NONE, CMD_NONE, CMD_NONE, CMD_NONE, #ifndef NO_PLUGINS CMD_DBMECHO, // Toggle DSP CMD_MIDI, // Wxx Echo Delay CMD_MIDI, // Xxx Echo Feedback CMD_MIDI, // Yxx Echo Mix CMD_MIDI, // Zxx Echo Cross #endif // NO_PLUGINS }; static void ConvertDBMEffect(uint8 &command, uint8 ¶m) { uint8 oldCmd = command; if(command < std::size(dbmEffects)) command = dbmEffects[command]; else command = CMD_NONE; switch(command) { case CMD_ARPEGGIO: if(param == 0) command = CMD_NONE; break; #ifdef MODPLUG_TRACKER case CMD_VIBRATO: if(param & 0x0F) { // DBM vibrato is half as deep as most other trackers. Convert it to IT fine vibrato range if possible. uint8 depth = (param & 0x0F) * 2u; param &= 0xF0; if(depth < 16) command = CMD_FINEVIBRATO; else depth = (depth + 2u) / 4u; param |= depth; } break; #endif // Volume slide nibble priority - first nibble (slide up) has precedence. case CMD_VOLUMESLIDE: case CMD_TONEPORTAVOL: case CMD_VIBRATOVOL: if((param & 0xF0) != 0x00 && (param & 0xF0) != 0xF0 && (param & 0x0F) != 0x0F) param &= 0xF0; break; case CMD_GLOBALVOLUME: if(param <= 64) param *= 2; else param = 128; break; case CMD_MODCMDEX: switch(param & 0xF0) { case 0x30: // Play backwards command = CMD_S3MCMDEX; param = 0x9F; break; case 0x40: // Turn off sound in channel (volume / portamento commands after this can't pick up the note anymore) command = CMD_S3MCMDEX; param = 0xC0; break; case 0x50: // Turn on/off channel // TODO: Apparently this should also kill the playing note. if((param & 0x0F) <= 0x01) { command = CMD_CHANNELVOLUME; param = (param == 0x50) ? 0x00 : 0x40; } break; case 0x70: // Coarse offset command = CMD_S3MCMDEX; param = 0xA0 | (param & 0x0F); break; default: // Rest will be converted later from CMD_MODCMDEX to CMD_S3MCMDEX. break; } break; case CMD_TEMPO: if(param <= 0x1F) command = CMD_SPEED; break; case CMD_KEYOFF: if (param == 0) { // TODO key off at tick 0 } break; case CMD_MIDI: // Encode echo parameters into fixed MIDI macros param = 128 + (oldCmd - 32) * 32 + param / 8; } } // Read a chunk of volume or panning envelopes static void ReadDBMEnvelopeChunk(FileReader chunk, EnvelopeType envType, CSoundFile &sndFile, bool scaleEnv) { uint16 numEnvs = chunk.ReadUint16BE(); for(uint16 env = 0; env < numEnvs; env++) { DBMEnvelope dbmEnv; chunk.ReadStruct(dbmEnv); uint16 dbmIns = dbmEnv.instrument; if(dbmIns > 0 && dbmIns <= sndFile.GetNumInstruments() && (sndFile.Instruments[dbmIns] != nullptr)) { ModInstrument *mptIns = sndFile.Instruments[dbmIns]; InstrumentEnvelope &mptEnv = mptIns->GetEnvelope(envType); if(dbmEnv.numSegments) { if(dbmEnv.flags & DBMEnvelope::envEnabled) mptEnv.dwFlags.set(ENV_ENABLED); if(dbmEnv.flags & DBMEnvelope::envSustain) mptEnv.dwFlags.set(ENV_SUSTAIN); if(dbmEnv.flags & DBMEnvelope::envLoop) mptEnv.dwFlags.set(ENV_LOOP); } uint8 numPoints = std::min(dbmEnv.numSegments.get(), uint8(31)) + 1; mptEnv.resize(numPoints); mptEnv.nLoopStart = dbmEnv.loopBegin; mptEnv.nLoopEnd = dbmEnv.loopEnd; mptEnv.nSustainStart = mptEnv.nSustainEnd = dbmEnv.sustain1; for(uint8 i = 0; i < numPoints; i++) { mptEnv[i].tick = dbmEnv.data[i * 2]; uint16 val = dbmEnv.data[i * 2 + 1]; if(scaleEnv) { // Panning envelopes are -128...128 in DigiBooster Pro 3.x val = (val + 128) / 4; } LimitMax(val, uint16(64)); mptEnv[i].value = static_cast(val); } } } } static bool ValidateHeader(const DBMFileHeader &fileHeader) { if(std::memcmp(fileHeader.dbm0, "DBM0", 4) || fileHeader.trkVerHi > 3) { return false; } return true; } CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderDBM(MemoryFileReader file, const uint64 *pfilesize) { DBMFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return ProbeWantMoreData; } if(!ValidateHeader(fileHeader)) { return ProbeFailure; } MPT_UNREFERENCED_PARAMETER(pfilesize); return ProbeSuccess; } bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags) { file.Rewind(); DBMFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return false; } if(!ValidateHeader(fileHeader)) { return false; } if(loadFlags == onlyVerifyHeader) { return true; } ChunkReader chunkFile(file); auto chunks = chunkFile.ReadChunks(1); // Globals DBMInfoChunk infoData; if(!chunks.GetChunk(DBMChunk::idINFO).ReadStruct(infoData)) { return false; } InitializeGlobals(MOD_TYPE_DBM); InitializeChannels(); m_SongFlags = SONG_ITCOMPATGXX | SONG_ITOLDEFFECTS; m_nChannels = Clamp(infoData.channels, 1, MAX_BASECHANNELS); // note: MAX_BASECHANNELS is currently 127, but DBPro 2 supports up to 128 channels, DBPro 3 apparently up to 254. m_nInstruments = std::min(static_cast(infoData.instruments), static_cast(MAX_INSTRUMENTS - 1)); m_nSamples = std::min(static_cast(infoData.samples), static_cast(MAX_SAMPLES - 1)); m_playBehaviour.set(kSlidesAtSpeed1); m_playBehaviour.reset(kITVibratoTremoloPanbrello); m_playBehaviour.reset(kITArpeggio); m_modFormat.formatName = U_("DigiBooster Pro"); m_modFormat.type = U_("dbm"); m_modFormat.madeWithTracker = MPT_UFORMAT("DigiBooster Pro {}.{}")(mpt::ufmt::hex(fileHeader.trkVerHi), mpt::ufmt::hex(fileHeader.trkVerLo)); m_modFormat.charset = mpt::Charset::ISO8859_1; // Name chunk FileReader nameChunk = chunks.GetChunk(DBMChunk::idNAME); nameChunk.ReadString(m_songName, nameChunk.GetLength()); // Song chunk FileReader songChunk = chunks.GetChunk(DBMChunk::idSONG); Order().clear(); uint16 numSongs = infoData.songs; for(uint16 i = 0; i < numSongs && songChunk.CanRead(46); i++) { char name[44]; songChunk.ReadString(name, 44); if(m_songName.empty()) { m_songName = name; } uint16 numOrders = songChunk.ReadUint16BE(); #ifdef MPT_DBM_USE_REAL_SUBSONGS if(!Order().empty()) { // Add a new sequence for this song if(Order.AddSequence() == SEQUENCEINDEX_INVALID) break; } Order().SetName(mpt::ToUnicode(mpt::Charset::ISO8859_1, name)); ReadOrderFromFile(Order(), songChunk, numOrders); #else const ORDERINDEX startIndex = Order().GetLength(); if(startIndex < MAX_ORDERS && songChunk.CanRead(numOrders * 2u)) { LimitMax(numOrders, static_cast(MAX_ORDERS - startIndex - 1)); Order().resize(startIndex + numOrders + 1); for(uint16 ord = 0; ord < numOrders; ord++) { Order()[startIndex + ord] = static_cast(songChunk.ReadUint16BE()); } } #endif // MPT_DBM_USE_REAL_SUBSONGS } #ifdef MPT_DBM_USE_REAL_SUBSONGS Order.SetSequence(0); #endif // MPT_DBM_USE_REAL_SUBSONGS // Read instruments if(FileReader instChunk = chunks.GetChunk(DBMChunk::idINST)) { for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++) { DBMInstrument instrHeader; instChunk.ReadStruct(instrHeader); ModInstrument *mptIns = AllocateInstrument(i, instrHeader.sample); if(mptIns == nullptr || instrHeader.sample >= MAX_SAMPLES) { continue; } mptIns->name = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, instrHeader.name); m_szNames[instrHeader.sample] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, instrHeader.name); mptIns->nFadeOut = 0; mptIns->nPan = static_cast(instrHeader.panning + 128); LimitMax(mptIns->nPan, uint32(256)); mptIns->dwFlags.set(INS_SETPANNING); // Sample Info ModSample &mptSmp = Samples[instrHeader.sample]; mptSmp.Initialize(); mptSmp.nVolume = std::min(static_cast(instrHeader.volume), uint16(64)) * 4u; mptSmp.nC5Speed = instrHeader.sampleRate; if(instrHeader.loopLength && (instrHeader.flags & (DBMInstrument::smpLoop | DBMInstrument::smpPingPongLoop))) { mptSmp.nLoopStart = instrHeader.loopStart; mptSmp.nLoopEnd = mptSmp.nLoopStart + instrHeader.loopLength; mptSmp.uFlags.set(CHN_LOOP); if(instrHeader.flags & DBMInstrument::smpPingPongLoop) mptSmp.uFlags.set(CHN_PINGPONGLOOP); } } // Read envelopes ReadDBMEnvelopeChunk(chunks.GetChunk(DBMChunk::idVENV), ENV_VOLUME, *this, false); ReadDBMEnvelopeChunk(chunks.GetChunk(DBMChunk::idPENV), ENV_PANNING, *this, fileHeader.trkVerHi > 2); // Note-Off cuts samples if there's no envelope. for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++) { if(Instruments[i] != nullptr && !Instruments[i]->VolEnv.dwFlags[ENV_ENABLED]) { Instruments[i]->nFadeOut = 32767; } } } // Patterns FileReader patternChunk = chunks.GetChunk(DBMChunk::idPATT); #ifndef NO_PLUGINS bool hasEchoEnable = false, hasEchoParams = false; #endif // NO_PLUGINS if(patternChunk.IsValid() && (loadFlags & loadPatternData)) { FileReader patternNameChunk = chunks.GetChunk(DBMChunk::idPNAM); patternNameChunk.Skip(1); // Encoding, should be UTF-8 or ASCII Patterns.ResizeArray(infoData.patterns); std::vector> lostGlobalCommands; for(PATTERNINDEX pat = 0; pat < infoData.patterns; pat++) { uint16 numRows = patternChunk.ReadUint16BE(); uint32 packedSize = patternChunk.ReadUint32BE(); FileReader chunk = patternChunk.ReadChunk(packedSize); if(!Patterns.Insert(pat, numRows)) continue; std::string patName; patternNameChunk.ReadSizedString(patName); Patterns[pat].SetName(patName); PatternRow patRow = Patterns[pat].GetRow(0); ROWINDEX row = 0; lostGlobalCommands.clear(); while(chunk.CanRead(1)) { const uint8 ch = chunk.ReadUint8(); if(!ch) { // End Of Row for(const auto &cmd : lostGlobalCommands) { Patterns[pat].WriteEffect(EffectWriter(cmd.first, cmd.second).Row(row)); } lostGlobalCommands.clear(); if(++row >= numRows) break; patRow = Patterns[pat].GetRow(row); continue; } ModCommand dummy = ModCommand::Empty(); ModCommand &m = ch <= GetNumChannels() ? patRow[ch - 1] : dummy; const uint8 b = chunk.ReadUint8(); if(b & 0x01) { uint8 note = chunk.ReadUint8(); if(note == 0x1F) m.note = NOTE_KEYOFF; else if(note > 0 && note < 0xFE) m.note = ((note >> 4) * 12) + (note & 0x0F) + 13; } if(b & 0x02) { m.instr = chunk.ReadUint8(); } if(b & 0x3C) { uint8 cmd1 = 0, cmd2 = 0, param1 = 0, param2 = 0; if(b & 0x04) cmd2 = chunk.ReadUint8(); if(b & 0x08) param2 = chunk.ReadUint8(); if(b & 0x10) cmd1 = chunk.ReadUint8(); if(b & 0x20) param1 = chunk.ReadUint8(); ConvertDBMEffect(cmd1, param1); ConvertDBMEffect(cmd2, param2); if (cmd2 == CMD_VOLUME || (cmd2 == CMD_NONE && cmd1 != CMD_VOLUME)) { std::swap(cmd1, cmd2); std::swap(param1, param2); } const auto lostCommand = ModCommand::TwoRegularCommandsToMPT(cmd1, param1, cmd2, param2); if(ModCommand::IsGlobalCommand(lostCommand.first, lostCommand.second)) lostGlobalCommands.insert(lostGlobalCommands.begin(), lostCommand); // Insert at front so that the last command of same type "wins" m.volcmd = cmd1; m.vol = param1; m.command = cmd2; m.param = param2; #ifdef MODPLUG_TRACKER m.ExtendedMODtoS3MEffect(); #endif // MODPLUG_TRACKER #ifndef NO_PLUGINS if(m.command == CMD_DBMECHO) hasEchoEnable = true; else if(m.command == CMD_MIDI) hasEchoParams = true; #endif // NO_PLUGINS } } } } #ifndef NO_PLUGINS // Echo DSP if(loadFlags & loadPluginData) { if(hasEchoEnable) { // If there are any Vxx effects to dynamically enable / disable echo, use the CHN_NOFX flag. for(CHANNELINDEX i = 0; i < m_nChannels; i++) { ChnSettings[i].nMixPlugin = 1; ChnSettings[i].dwFlags.set(CHN_NOFX); } } bool anyEnabled = hasEchoEnable; // DBP 3 Documentation says that the defaults are 64/128/128/255, but they appear to be 80/150/80/255 in DBP 2.21 uint8 settings[8] = { 0, 80, 0, 150, 0, 80, 0, 255 }; if(FileReader dspChunk = chunks.GetChunk(DBMChunk::idDSPE)) { uint16 maskLen = dspChunk.ReadUint16BE(); for(uint16 i = 0; i < maskLen; i++) { bool enabled = (dspChunk.ReadUint8() == 0); if(i < m_nChannels) { if(hasEchoEnable) { // If there are any Vxx effects to dynamically enable / disable echo, use the CHN_NOFX flag. ChnSettings[i].dwFlags.set(CHN_NOFX, !enabled); } else if(enabled) { ChnSettings[i].nMixPlugin = 1; anyEnabled = true; } } } dspChunk.ReadArray(settings); } if(anyEnabled) { // Note: DigiBooster Pro 3 has a more versatile per-channel echo effect. // In this case, we'd have to create one plugin per channel. SNDMIXPLUGIN &plugin = m_MixPlugins[0]; plugin.Destroy(); memcpy(&plugin.Info.dwPluginId1, "DBM0", 4); memcpy(&plugin.Info.dwPluginId2, "Echo", 4); plugin.Info.routingFlags = SNDMIXPLUGININFO::irAutoSuspend; plugin.Info.mixMode = 0; plugin.Info.gain = 10; plugin.Info.reserved = 0; plugin.Info.dwOutputRouting = 0; std::fill(plugin.Info.dwReserved, plugin.Info.dwReserved + std::size(plugin.Info.dwReserved), 0); plugin.Info.szName = "Echo"; plugin.Info.szLibraryName = "DigiBooster Pro Echo"; plugin.pluginData.resize(sizeof(DigiBoosterEcho::PluginChunk)); DigiBoosterEcho::PluginChunk chunk = DigiBoosterEcho::PluginChunk::Create(settings[1], settings[3], settings[5], settings[7]); new (plugin.pluginData.data()) DigiBoosterEcho::PluginChunk(chunk); } } // Encode echo parameters into fixed MIDI macros if(hasEchoParams) { for(uint32 i = 0; i < 32; i++) { uint32 param = (i * 127u) / 32u; m_MidiCfg.Zxx[i ] = MPT_AFORMAT("F0F080{}")(mpt::afmt::HEX0<2>(param)); m_MidiCfg.Zxx[i + 32] = MPT_AFORMAT("F0F081{}")(mpt::afmt::HEX0<2>(param)); m_MidiCfg.Zxx[i + 64] = MPT_AFORMAT("F0F082{}")(mpt::afmt::HEX0<2>(param)); m_MidiCfg.Zxx[i + 96] = MPT_AFORMAT("F0F083{}")(mpt::afmt::HEX0<2>(param)); } } #endif // NO_PLUGINS // Samples FileReader sampleChunk = chunks.GetChunk(DBMChunk::idSMPL); if(sampleChunk.IsValid() && (loadFlags & loadSampleData)) { for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++) { uint32 sampleFlags = sampleChunk.ReadUint32BE(); uint32 sampleLength = sampleChunk.ReadUint32BE(); if(sampleFlags & 7) { ModSample &sample = Samples[smp]; sample.nLength = sampleLength; SampleIO( (sampleFlags & 4) ? SampleIO::_32bit : ((sampleFlags & 2) ? SampleIO::_16bit : SampleIO::_8bit), SampleIO::mono, SampleIO::bigEndian, SampleIO::signedPCM) .ReadSample(sample, sampleChunk); } } } #if defined(MPT_ENABLE_MP3_SAMPLES) && 0 // Compressed samples - this does not quite work yet... FileReader mpegChunk = chunks.GetChunk(DBMChunk::idMPEG); if(mpegChunk.IsValid() && (loadFlags & loadSampleData)) { for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++) { Samples[smp].nLength = mpegChunk.ReadUint32BE(); } mpegChunk.Skip(2); // 0x00 0x40 // Read whole MPEG stream into one sample and then split it up. FileReader chunk = mpegChunk.GetChunk(mpegChunk.BytesLeft()); if(ReadMP3Sample(0, chunk, true)) { ModSample &srcSample = Samples[0]; const std::byte *smpData = srcSample.sampleb(); SmpLength predelay = Util::muldiv_unsigned(20116, srcSample.nC5Speed, 100000); LimitMax(predelay, srcSample.nLength); smpData += predelay * srcSample.GetBytesPerSample(); srcSample.nLength -= predelay; for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++) { ModSample &sample = Samples[smp]; sample.uFlags.set(srcSample.uFlags); LimitMax(sample.nLength, srcSample.nLength); if(sample.nLength) { sample.AllocateSample(); memcpy(sample.sampleb(), smpData, sample.GetSampleSizeInBytes()); smpData += sample.GetSampleSizeInBytes(); srcSample.nLength -= sample.nLength; SmpLength gap = Util::muldiv_unsigned(454, srcSample.nC5Speed, 10000); LimitMax(gap, srcSample.nLength); smpData += gap * srcSample.GetBytesPerSample(); srcSample.nLength -= gap; } } srcSample.FreeSample(); } } #endif // MPT_ENABLE_MP3_SAMPLES return true; } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Load_digi.cpp0000644000175000017500000001302114174037566020464 00000000000000/* * Load_digi.cpp * ------------- * Purpose: Digi Booster module loader * Notes : Basically these are like ProTracker MODs with a few extra features such as more channels, longer samples and a few more effects. * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Loaders.h" OPENMPT_NAMESPACE_BEGIN // DIGI File Header struct DIGIFileHeader { char signature[20]; char versionStr[4]; // Supposed to be "V1.6" or similar, but other values like "TAP!" have been found as well. uint8be versionInt; // e.g. 0x16 = 1.6 uint8be numChannels; uint8be packEnable; char unknown[19]; uint8be lastPatIndex; uint8be lastOrdIndex; uint8be orders[128]; uint32be smpLength[31]; uint32be smpLoopStart[31]; uint32be smpLoopLength[31]; uint8be smpVolume[31]; uint8be smpFinetune[31]; }; MPT_BINARY_STRUCT(DIGIFileHeader, 610) static void ReadDIGIPatternEntry(FileReader &file, ModCommand &m) { CSoundFile::ReadMODPatternEntry(file, m); CSoundFile::ConvertModCommand(m); if(m.command == CMD_MODCMDEX) { switch(m.param & 0xF0) { case 0x30: // E3x: Play sample backwards (E30 stops sample when it reaches the beginning, any other value plays it from the beginning including regular loop) // The play direction is also reset if a new note is played on the other channel linked to this channel. // The behaviour is rather broken when there is no note next to the ommand. m.command = CMD_DIGIREVERSESAMPLE; m.param &= 0x0F; break; case 0x40: // E40: Stop playing sample if(m.param == 0x40) { m.note = NOTE_NOTECUT; m.command = CMD_NONE; } break; case 0x80: // E8x: High sample offset m.command = CMD_S3MCMDEX; m.param = 0xA0 | (m.param & 0x0F); } } else if(m.command == CMD_PANNING8) { // 8xx "Robot" effect (not supported) m.command = CMD_NONE; } } static bool ValidateHeader(const DIGIFileHeader &fileHeader) { if(std::memcmp(fileHeader.signature, "DIGI Booster module\0", 20) || !fileHeader.numChannels || fileHeader.numChannels > 8 || fileHeader.lastOrdIndex > 127) { return false; } return true; } CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderDIGI(MemoryFileReader file, const uint64 *pfilesize) { DIGIFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return ProbeWantMoreData; } if(!ValidateHeader(fileHeader)) { return ProbeFailure; } MPT_UNREFERENCED_PARAMETER(pfilesize); return ProbeSuccess; } bool CSoundFile::ReadDIGI(FileReader &file, ModLoadingFlags loadFlags) { file.Rewind(); DIGIFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return false; } if(!ValidateHeader(fileHeader)) { return false; } if(loadFlags == onlyVerifyHeader) { return true; } // Globals InitializeGlobals(MOD_TYPE_DIGI); InitializeChannels(); m_nChannels = fileHeader.numChannels; m_nSamples = 31; m_nSamplePreAmp = 256 / m_nChannels; m_modFormat.formatName = U_("DigiBooster"); m_modFormat.type = U_("digi"); m_modFormat.madeWithTracker = MPT_UFORMAT("Digi Booster {}.{}")(fileHeader.versionInt >> 4, fileHeader.versionInt & 0x0F); m_modFormat.charset = mpt::Charset::ISO8859_1; ReadOrderFromArray(Order(), fileHeader.orders, fileHeader.lastOrdIndex + 1); // Read sample headers for(SAMPLEINDEX smp = 0; smp < 31; smp++) { ModSample &sample = Samples[smp + 1]; sample.Initialize(MOD_TYPE_MOD); sample.nLength = fileHeader.smpLength[smp]; sample.nLoopStart = fileHeader.smpLoopStart[smp]; sample.nLoopEnd = sample.nLoopStart + fileHeader.smpLoopLength[smp]; if(fileHeader.smpLoopLength[smp]) { sample.uFlags.set(CHN_LOOP); } sample.SanitizeLoops(); sample.nVolume = std::min(fileHeader.smpVolume[smp].get(), uint8(64)) * 4; sample.nFineTune = MOD2XMFineTune(fileHeader.smpFinetune[smp]); } // Read song + sample names file.ReadString(m_songName, 32); for(SAMPLEINDEX smp = 1; smp <= 31; smp++) { file.ReadString(m_szNames[smp], 30); } if(loadFlags & loadPatternData) Patterns.ResizeArray(fileHeader.lastPatIndex + 1); for(PATTERNINDEX pat = 0; pat <= fileHeader.lastPatIndex; pat++) { FileReader patternChunk; if(fileHeader.packEnable) { patternChunk = file.ReadChunk(file.ReadUint16BE()); } else { patternChunk = file.ReadChunk(4 * 64 * GetNumChannels()); } if(!(loadFlags & loadPatternData) || !Patterns.Insert(pat, 64)) { continue; } if(fileHeader.packEnable) { uint8 eventMask[64]; patternChunk.ReadArray(eventMask); // Compressed patterns are stored in row-major order... for(ROWINDEX row = 0; row < 64; row++) { PatternRow patRow = Patterns[pat].GetRow(row); uint8 bit = 0x80; for(CHANNELINDEX chn = 0; chn < GetNumChannels(); chn++, bit >>= 1) { if(eventMask[row] & bit) { ModCommand &m = patRow[chn]; ReadDIGIPatternEntry(patternChunk, m); } } } } else { // ...but uncompressed patterns are stored in column-major order. WTF! for(CHANNELINDEX chn = 0; chn < GetNumChannels(); chn++) { for(ROWINDEX row = 0; row < 64; row++) { ReadDIGIPatternEntry(patternChunk, *Patterns[pat].GetpModCommand(row, chn)); } } } } if(loadFlags & loadSampleData) { // Reading Samples const SampleIO sampleIO( SampleIO::_8bit, SampleIO::mono, SampleIO::bigEndian, SampleIO::signedPCM); for(SAMPLEINDEX smp = 1; smp <= 31; smp++) { sampleIO.ReadSample(Samples[smp], file); } } return true; } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Load_dmf.cpp0000644000175000017500000010570714137251202020313 00000000000000/* * load_dmf.cpp * ------------ * Purpose: DMF module loader (X-Tracker by D-LUSiON). * Notes : If it wasn't already outdated when the tracker left beta state, this would be a rather interesting * and in some parts even sophisticated format - effect columns are separated by effect type, an easy to * understand BPM tempo mode, effect durations are always divided into a 256th row, vibrato effects are * specified by period length and the same 8-Bit granularity is used for both volume and panning. * Unluckily, this format does not offer any envelopes or multi-sample instruments, and bidi sample loops * are missing as well, so it was already well behind FT2 back then. * Authors: Johannes Schultz (mostly based on DMF.TXT, DMF_EFFC.TXT, trial and error and some invaluable hints by Zatzen) * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Loaders.h" #include "BitReader.h" OPENMPT_NAMESPACE_BEGIN // DMF header struct DMFFileHeader { char signature[4]; // "DDMF" uint8 version; // 1 - 7 are beta versions, 8 is the official thing, 10 is xtracker32 char tracker[8]; // "XTRACKER" char songname[30]; char composer[20]; uint8 creationDay; uint8 creationMonth; uint8 creationYear; }; MPT_BINARY_STRUCT(DMFFileHeader, 66) struct DMFChunk { // 32-Bit chunk identifiers enum ChunkIdentifiers { idCMSG = MagicLE("CMSG"), // Song message idSEQU = MagicLE("SEQU"), // Order list idPATT = MagicLE("PATT"), // Patterns idSMPI = MagicLE("SMPI"), // Sample headers idSMPD = MagicLE("SMPD"), // Sample data idSMPJ = MagicLE("SMPJ"), // Sample jump table (XTracker 32 only) idENDE = MagicLE("ENDE"), // Last four bytes of DMF file idSETT = MagicLE("SETT"), // Probably contains GUI settings }; uint32le id; uint32le length; size_t GetLength() const { return length; } ChunkIdentifiers GetID() const { return static_cast(id.get()); } }; MPT_BINARY_STRUCT(DMFChunk, 8) // Pattern header (global) struct DMFPatterns { uint16le numPatterns; // 1..1024 patterns uint8le numTracks; // 1..32 channels }; MPT_BINARY_STRUCT(DMFPatterns, 3) // Pattern header (for each pattern) struct DMFPatternHeader { uint8le numTracks; // 1..32 channels uint8le beat; // [hi|lo] -> hi = rows per beat, lo = reserved uint16le numRows; uint32le patternLength; // patttern data follows here ... }; MPT_BINARY_STRUCT(DMFPatternHeader, 8) // Sample header struct DMFSampleHeader { enum SampleFlags { // Sample flags smpLoop = 0x01, smp16Bit = 0x02, smpCompMask = 0x0C, smpComp1 = 0x04, // Compression type 1 smpComp2 = 0x08, // Compression type 2 (unused) smpComp3 = 0x0C, // Compression type 3 (ditto) smpLibrary = 0x80, // Sample is stored in a library }; uint32le length; uint32le loopStart; uint32le loopEnd; uint16le c3freq; // 1000..45000hz uint8le volume; // 0 = ignore uint8le flags; // Convert an DMFSampleHeader to OpenMPT's internal sample representation. void ConvertToMPT(ModSample &mptSmp) const { mptSmp.Initialize(); mptSmp.nLength = length; mptSmp.nSustainStart = loopStart; mptSmp.nSustainEnd = loopEnd; mptSmp.nC5Speed = c3freq; mptSmp.nGlobalVol = 64; if(volume) mptSmp.nVolume = volume + 1; else mptSmp.nVolume = 256; mptSmp.uFlags.set(SMP_NODEFAULTVOLUME, volume == 0); if((flags & smpLoop) != 0 && mptSmp.nSustainEnd > mptSmp.nSustainStart) { mptSmp.uFlags.set(CHN_SUSTAINLOOP); } if((flags & smp16Bit) != 0) { mptSmp.uFlags.set(CHN_16BIT); mptSmp.nLength /= 2; mptSmp.nSustainStart /= 2; mptSmp.nSustainEnd /= 2; } } }; MPT_BINARY_STRUCT(DMFSampleHeader, 16) // Pattern translation memory struct DMFPatternSettings { struct ChannelState { ModCommand::NOTE noteBuffer = NOTE_NONE; // Note buffer ModCommand::NOTE lastNote = NOTE_NONE; // Last played note on channel uint8 vibratoType = 8; // Last used vibrato type on channel uint8 tremoloType = 4; // Last used tremolo type on channel uint8 highOffset = 6; // Last used high offset on channel bool playDir = false; // Sample play direction... false = forward (default) }; std::vector channels; // Memory for each channel's state bool realBPMmode = false; // true = BPM mode uint8 beat = 0; // Rows per beat uint8 tempoTicks = 32; // Tick mode param uint8 tempoBPM = 120; // BPM mode param uint8 internalTicks = 6; // Ticks per row in final pattern DMFPatternSettings(CHANNELINDEX numChannels) : channels(numChannels) { } }; // Convert portamento value (not very accurate due to X-Tracker's higher granularity, to say the least) static uint8 DMFporta2MPT(uint8 val, const uint8 internalTicks, const bool hasFine) { if(val == 0) return 0; else if((val <= 0x0F && hasFine) || internalTicks < 2) return (val | 0xF0); else return std::max(uint8(1), static_cast((val / (internalTicks - 1)))); // no porta on first tick! } // Convert portamento / volume slide value (not very accurate due to X-Tracker's higher granularity, to say the least) static uint8 DMFslide2MPT(uint8 val, const uint8 internalTicks, const bool up) { val = std::max(uint8(1), static_cast(val / 4)); const bool isFine = (val < 0x0F) || (internalTicks < 2); if(!isFine) val = std::max(uint8(1), static_cast((val + internalTicks - 2) / (internalTicks - 1))); // no slides on first tick! "+ internalTicks - 2" for rounding precision if(up) return (isFine ? 0x0F : 0x00) | (val << 4); else return (isFine ? 0xF0 : 0x00) | (val & 0x0F); } // Calculate tremor on/off param static uint8 DMFtremor2MPT(uint8 val, const uint8 internalTicks) { uint8 ontime = (val >> 4); uint8 offtime = (val & 0x0F); ontime = static_cast(Clamp(ontime * internalTicks / 15, 1, 15)); offtime = static_cast(Clamp(offtime * internalTicks / 15, 1, 15)); return (ontime << 4) | offtime; } // Calculate delay parameter for note cuts / delays static uint8 DMFdelay2MPT(uint8 val, const uint8 internalTicks) { int newval = (int)val * (int)internalTicks / 255; Limit(newval, 0, 15); return (uint8)newval; } // Convert vibrato-style command parameters static uint8 DMFvibrato2MPT(uint8 val, const uint8 internalTicks) { // MPT: 1 vibrato period == 64 ticks... we have internalTicks ticks per row. // X-Tracker: Period length specified in rows! const int periodInTicks = std::max(1, (val >> 4)) * internalTicks; const uint8 matchingPeriod = static_cast(Clamp((128 / periodInTicks), 1, 15)); return (matchingPeriod << 4) | std::max(uint8(1), static_cast(val & 0x0F)); } // Try using effect memory (zero paramer) to give the effect swapper some optimization hints. static void ApplyEffectMemory(const ModCommand *m, ROWINDEX row, CHANNELINDEX numChannels, uint8 effect, uint8 ¶m) { if(effect == CMD_NONE || param == 0) return; const bool isTonePortaEffect = (effect == CMD_PORTAMENTOUP || effect == CMD_PORTAMENTODOWN || effect == CMD_TONEPORTAMENTO); const bool isVolSlideEffect = (effect == CMD_VOLUMESLIDE || effect == CMD_TONEPORTAVOL || effect == CMD_VIBRATOVOL); while(row > 0) { m -= numChannels; row--; // First, keep some extra rules in mind for portamento, where effect memory is shared between various commands. bool isSame = (effect == m->command); if(isTonePortaEffect && (m->command == CMD_PORTAMENTOUP || m->command == CMD_PORTAMENTODOWN || m->command == CMD_TONEPORTAMENTO)) { if(m->param < 0xE0) { // Avoid effect param for fine slides, or else we could accidentally put this command in the volume column, where fine slides won't work! isSame = true; } else { return; } } else if(isVolSlideEffect && (m->command == CMD_VOLUMESLIDE || m->command == CMD_TONEPORTAVOL || m->command == CMD_VIBRATOVOL)) { isSame = true; } if(isTonePortaEffect && (m->volcmd == VOLCMD_PORTAUP || m->volcmd == VOLCMD_PORTADOWN || m->volcmd == VOLCMD_TONEPORTAMENTO) && m->vol != 0) { // Uuh... Don't even try return; } else if(isVolSlideEffect && (m->volcmd == VOLCMD_FINEVOLUP || m->volcmd == VOLCMD_FINEVOLDOWN || m->volcmd == VOLCMD_VOLSLIDEUP || m->volcmd == VOLCMD_VOLSLIDEDOWN) && m->vol != 0) { // Same! return; } if(isSame) { if(param != m->param && m->param != 0) { // No way to optimize this return; } else if(param == m->param) { // Yay! param = 0; return; } } } } static PATTERNINDEX ConvertDMFPattern(FileReader &file, const uint8 fileVersion, DMFPatternSettings &settings, CSoundFile &sndFile) { // Pattern flags enum PatternFlags { // Global Track patGlobPack = 0x80, // Pack information for global track follows patGlobMask = 0x3F, // Mask for global effects // Note tracks patCounter = 0x80, // Pack information for current channel follows patInstr = 0x40, // Instrument number present patNote = 0x20, // Note present patVolume = 0x10, // Volume present patInsEff = 0x08, // Instrument effect present patNoteEff = 0x04, // Note effect present patVolEff = 0x02, // Volume effect stored }; file.Rewind(); DMFPatternHeader patHead; if(fileVersion < 3) { patHead.numTracks = file.ReadUint8(); file.Skip(2); // not sure what this is, later X-Tracker versions just skip over it patHead.numRows = file.ReadUint16LE(); patHead.patternLength = file.ReadUint32LE(); } else { file.ReadStruct(patHead); } if(fileVersion < 6) patHead.beat = 0; const ROWINDEX numRows = Clamp(ROWINDEX(patHead.numRows), ROWINDEX(1), MAX_PATTERN_ROWS); const PATTERNINDEX pat = sndFile.Patterns.InsertAny(numRows); if(pat == PATTERNINDEX_INVALID) { return pat; } PatternRow m = sndFile.Patterns[pat].GetRow(0); const CHANNELINDEX numChannels = std::min(static_cast(sndFile.GetNumChannels() - 1), static_cast(patHead.numTracks)); // When breaking to a pattern with less channels that the previous pattern, // all voices in the now unused channels are killed: for(CHANNELINDEX chn = numChannels + 1; chn < sndFile.GetNumChannels(); chn++) { m[chn].note = NOTE_NOTECUT; } // Initialize tempo stuff settings.beat = (patHead.beat >> 4); bool tempoChange = settings.realBPMmode; uint8 writeDelay = 0; // Counters for channel packing (including global track) std::vector channelCounter(numChannels + 1, 0); for(ROWINDEX row = 0; row < numRows; row++) { // Global track info counter reached 0 => read global track data if(channelCounter[0] == 0) { uint8 globalInfo = file.ReadUint8(); // 0x80: Packing counter (if not present, counter stays at 0) if((globalInfo & patGlobPack) != 0) { channelCounter[0] = file.ReadUint8(); } globalInfo &= patGlobMask; uint8 globalData = 0; if(globalInfo != 0) { globalData = file.ReadUint8(); } switch(globalInfo) { case 1: // Set Tick Frame Speed settings.realBPMmode = false; settings.tempoTicks = std::max(uint8(1), globalData); // Tempo in 1/4 rows per second settings.tempoBPM = 0; // Automatically updated by X-Tracker tempoChange = true; break; case 2: // Set BPM Speed (real BPM mode) if(globalData) // DATA = 0 doesn't do anything { settings.realBPMmode = true; settings.tempoBPM = globalData; // Tempo in real BPM (depends on rows per beat) if(settings.beat != 0) { settings.tempoTicks = (globalData * settings.beat * 15); // Automatically updated by X-Tracker } tempoChange = true; } break; case 3: // Set Beat settings.beat = (globalData >> 4); if(settings.beat != 0) { // Tempo changes only if we're in real BPM mode tempoChange = settings.realBPMmode; } else { // If beat is 0, change to tick speed mode, but keep current tempo settings.realBPMmode = false; } break; case 4: // Tick Delay writeDelay = globalData; break; case 5: // Set External Flag break; case 6: // Slide Speed Up if(globalData > 0) { uint8 &tempoData = (settings.realBPMmode) ? settings.tempoBPM : settings.tempoTicks; if(tempoData < 256 - globalData) { tempoData += globalData; } else { tempoData = 255; } tempoChange = true; } break; case 7: // Slide Speed Down if(globalData > 0) { uint8 &tempoData = (settings.realBPMmode) ? settings.tempoBPM : settings.tempoTicks; if(tempoData > 1 + globalData) { tempoData -= globalData; } else { tempoData = 1; } tempoChange = true; } break; } } else { channelCounter[0]--; } // These will eventually be written to the pattern int speed = 0, tempo = 0; if(tempoChange) { // Can't do anything if we're in BPM mode and there's no rows per beat set... if(!settings.realBPMmode || settings.beat) { // My approach to convert X-Tracker's "tick speed" (1/4 rows per second): // Tempo * 6 / Speed = Beats per Minute // => Tempo * 6 / (Speed * 60) = Beats per Second // => Tempo * 24 / (Speed * 60) = Rows per Second (4 rows per beat at tempo 6) // => Tempo = 60 * Rows per Second * Speed / 24 // For some reason, using settings.tempoTicks + 1 gives more accurate results than just settings.tempoTicks... (same problem in the old libmodplug DMF loader) // Original unoptimized formula: //const int tickspeed = (tempoRealBPMmode) ? std::max(1, (tempoData * beat * 4) / 60) : tempoData; const int tickspeed = (settings.realBPMmode) ? std::max(1, settings.tempoBPM * settings.beat * 2) : ((settings.tempoTicks + 1) * 30); // Try to find matching speed - try higher speeds first, so that effects like arpeggio and tremor work better. for(speed = 255; speed >= 1; speed--) { // Original unoptimized formula: // tempo = 30 * tickspeed * speed / 48; tempo = tickspeed * speed / 48; if(tempo >= 32 && tempo <= 255) break; } Limit(tempo, 32, 255); settings.internalTicks = static_cast(std::max(1, speed)); } else { tempoChange = false; } } m = sndFile.Patterns[pat].GetpModCommand(row, 1); // Reserve first channel for global effects for(CHANNELINDEX chn = 1; chn <= numChannels; chn++, m++) { // Track info counter reached 0 => read track data if(channelCounter[chn] == 0) { const uint8 channelInfo = file.ReadUint8(); //////////////////////////////////////////////////////////////// // 0x80: Packing counter (if not present, counter stays at 0) if((channelInfo & patCounter) != 0) { channelCounter[chn] = file.ReadUint8(); } //////////////////////////////////////////////////////////////// // 0x40: Instrument bool slideNote = true; // If there is no instrument number next to a note, the note is not retriggered! if((channelInfo & patInstr) != 0) { m->instr = file.ReadUint8(); if(m->instr != 0) { slideNote = false; } } //////////////////////////////////////////////////////////////// // 0x20: Note if((channelInfo & patNote) != 0) { m->note = file.ReadUint8(); if(m->note >= 1 && m->note <= 108) { m->note = static_cast(Clamp(m->note + 24, NOTE_MIN, NOTE_MAX)); settings.channels[chn].lastNote = m->note; } else if(m->note >= 129 && m->note <= 236) { // "Buffer notes" for portamento (and other effects?) that are actually not played, but just "queued"... m->note = static_cast(Clamp((m->note & 0x7F) + 24, NOTE_MIN, NOTE_MAX)); settings.channels[chn].noteBuffer = m->note; m->note = NOTE_NONE; } else if(m->note == 255) { m->note = NOTE_NOTECUT; } } // If there's just an instrument number, but no note, retrigger sample. if(m->note == NOTE_NONE && m->instr > 0) { m->note = settings.channels[chn].lastNote; m->instr = 0; } if(m->IsNote()) { settings.channels[chn].playDir = false; } uint8 effect1 = CMD_NONE, effect2 = CMD_NONE, effect3 = CMD_NONE; uint8 effectParam1 = 0, effectParam2 = 0, effectParam3 = 0; bool useMem2 = false, useMem3 = false; // Effect can use memory if necessary //////////////////////////////////////////////////////////////// // 0x10: Volume if((channelInfo & patVolume) != 0) { m->volcmd = VOLCMD_VOLUME; m->vol = (file.ReadUint8() + 2) / 4; // Should be + 3 instead of + 2, but volume 1 is silent in X-Tracker. } //////////////////////////////////////////////////////////////// // 0x08: Instrument effect if((channelInfo & patInsEff) != 0) { effect1 = file.ReadUint8(); effectParam1 = file.ReadUint8(); switch(effect1) { case 1: // Stop Sample m->note = NOTE_NOTECUT; effect1 = CMD_NONE; break; case 2: // Stop Sample Loop m->note = NOTE_KEYOFF; effect1 = CMD_NONE; break; case 3: // Instrument Volume Override (aka "Restart") m->note = settings.channels[chn].lastNote; settings.channels[chn].playDir = false; effect1 = CMD_NONE; break; case 4: // Sample Delay effectParam1 = DMFdelay2MPT(effectParam1, settings.internalTicks); if(effectParam1) { effect1 = CMD_S3MCMDEX; effectParam1 = 0xD0 | (effectParam1); } else { effect1 = CMD_NONE; } if(m->note == NOTE_NONE) { m->note = settings.channels[chn].lastNote; settings.channels[chn].playDir = false; } break; case 5: // Tremolo Retrig Sample (who invented those stupid effect names?) effectParam1 = std::max(uint8(1), DMFdelay2MPT(effectParam1, settings.internalTicks)); effect1 = CMD_RETRIG; settings.channels[chn].playDir = false; break; case 6: // Offset case 7: // Offset + 64k case 8: // Offset + 128k case 9: // Offset + 192k // Put high offset on previous row if(row > 0 && effect1 != settings.channels[chn].highOffset) { if(sndFile.Patterns[pat].WriteEffect(EffectWriter(CMD_S3MCMDEX, (0xA0 | (effect1 - 6))).Row(row - 1).Channel(chn).RetryPreviousRow())) { settings.channels[chn].highOffset = effect1; } } effect1 = CMD_OFFSET; if(m->note == NOTE_NONE) { // Offset without note does also work in DMF. m->note = settings.channels[chn].lastNote; } settings.channels[chn].playDir = false; break; case 10: // Invert Sample play direction ("Tekkno Invert") effect1 = CMD_S3MCMDEX; if(settings.channels[chn].playDir == false) effectParam1 = 0x9F; else effectParam1 = 0x9E; settings.channels[chn].playDir = !settings.channels[chn].playDir; break; default: effect1 = CMD_NONE; break; } } //////////////////////////////////////////////////////////////// // 0x04: Note effect if((channelInfo & patNoteEff) != 0) { effect2 = file.ReadUint8(); effectParam2 = file.ReadUint8(); switch(effect2) { case 1: // Note Finetune (1/16th of a semitone signed 8-bit value, not 1/128th as the interface claims) { const auto fine = std::div(static_cast(effectParam2) * 8, 128); if(m->IsNote()) m->note = static_cast(Clamp(m->note + fine.quot, NOTE_MIN, NOTE_MAX)); effect2 = CMD_FINETUNE; effectParam2 = static_cast(fine.rem) ^ 0x80; } break; case 2: // Note Delay (wtf is the difference to Sample Delay?) effectParam2 = DMFdelay2MPT(effectParam2, settings.internalTicks); if(effectParam2) { effect2 = CMD_S3MCMDEX; effectParam2 = 0xD0 | (effectParam2); } else { effect2 = CMD_NONE; } useMem2 = true; break; case 3: // Arpeggio effect2 = CMD_ARPEGGIO; useMem2 = true; break; case 4: // Portamento Up case 5: // Portamento Down effectParam2 = DMFporta2MPT(effectParam2, settings.internalTicks, true); effect2 = (effect2 == 4) ? CMD_PORTAMENTOUP : CMD_PORTAMENTODOWN; useMem2 = true; break; case 6: // Portamento to Note if(m->note == NOTE_NONE) { m->note = settings.channels[chn].noteBuffer; } effectParam2 = DMFporta2MPT(effectParam2, settings.internalTicks, false); effect2 = CMD_TONEPORTAMENTO; useMem2 = true; break; case 7: // Scratch to Note (neat! but we don't have such an effect...) m->note = static_cast(Clamp(effectParam2 + 25, NOTE_MIN, NOTE_MAX)); effect2 = CMD_TONEPORTAMENTO; effectParam2 = 0xFF; useMem2 = true; break; case 8: // Vibrato Sine case 9: // Vibrato Triangle (ramp down should be close enough) case 10: // Vibrato Square // Put vibrato type on previous row if(row > 0 && effect2 != settings.channels[chn].vibratoType) { if(sndFile.Patterns[pat].WriteEffect(EffectWriter(CMD_S3MCMDEX, (0x30 | (effect2 - 8))).Row(row - 1).Channel(chn).RetryPreviousRow())) { settings.channels[chn].vibratoType = effect2; } } effect2 = CMD_VIBRATO; effectParam2 = DMFvibrato2MPT(effectParam2, settings.internalTicks); useMem2 = true; break; case 11: // Note Tremolo effectParam2 = DMFtremor2MPT(effectParam2, settings.internalTicks); effect2 = CMD_TREMOR; useMem2 = true; break; case 12: // Note Cut effectParam2 = DMFdelay2MPT(effectParam2, settings.internalTicks); if(effectParam2) { effect2 = CMD_S3MCMDEX; effectParam2 = 0xC0 | (effectParam2); } else { effect2 = CMD_NONE; m->note = NOTE_NOTECUT; } useMem2 = true; break; default: effect2 = CMD_NONE; break; } } //////////////////////////////////////////////////////////////// // 0x02: Volume effect if((channelInfo & patVolEff) != 0) { effect3 = file.ReadUint8(); effectParam3 = file.ReadUint8(); switch(effect3) { case 1: // Volume Slide Up case 2: // Volume Slide Down effectParam3 = DMFslide2MPT(effectParam3, settings.internalTicks, (effect3 == 1)); effect3 = CMD_VOLUMESLIDE; useMem3 = true; break; case 3: // Volume Tremolo (actually this is Tremor) effectParam3 = DMFtremor2MPT(effectParam3, settings.internalTicks); effect3 = CMD_TREMOR; useMem3 = true; break; case 4: // Tremolo Sine case 5: // Tremolo Triangle (ramp down should be close enough) case 6: // Tremolo Square // Put tremolo type on previous row if(row > 0 && effect3 != settings.channels[chn].tremoloType) { if(sndFile.Patterns[pat].WriteEffect(EffectWriter(CMD_S3MCMDEX, (0x40 | (effect3 - 4))).Row(row - 1).Channel(chn).RetryPreviousRow())) { settings.channels[chn].tremoloType = effect3; } } effect3 = CMD_TREMOLO; effectParam3 = DMFvibrato2MPT(effectParam3, settings.internalTicks); useMem3 = true; break; case 7: // Set Balance effect3 = CMD_PANNING8; break; case 8: // Slide Balance Left case 9: // Slide Balance Right effectParam3 = DMFslide2MPT(effectParam3, settings.internalTicks, (effect3 == 8)); effect3 = CMD_PANNINGSLIDE; useMem3 = true; break; case 10: // Balance Vibrato Left/Right (always sine modulated) effect3 = CMD_PANBRELLO; effectParam3 = DMFvibrato2MPT(effectParam3, settings.internalTicks); useMem3 = true; break; default: effect3 = CMD_NONE; break; } } // Let's see if we can help the effect swapper by reducing some effect parameters to "continue" parameters. if(useMem2) ApplyEffectMemory(m, row, sndFile.GetNumChannels(), effect2, effectParam2); if(useMem3) ApplyEffectMemory(m, row, sndFile.GetNumChannels(), effect3, effectParam3); // I guess this is close enough to "not retriggering the note" if(slideNote && m->IsNote()) { if(effect2 == CMD_NONE) { effect2 = CMD_TONEPORTAMENTO; effectParam2 = 0xFF; } else if(effect3 == CMD_NONE && effect2 != CMD_TONEPORTAMENTO) // Tone portamentos normally go in effect #2 { effect3 = CMD_TONEPORTAMENTO; effectParam3 = 0xFF; } } // If one of the effects is unused, temporarily put volume commands in there if(m->volcmd == VOLCMD_VOLUME) { if(effect2 == CMD_NONE) { effect2 = CMD_VOLUME; effectParam2 = m->vol; m->volcmd = VOLCMD_NONE; } else if(effect3 == CMD_NONE) { effect3 = CMD_VOLUME; effectParam3 = m->vol; m->volcmd = VOLCMD_NONE; } } ModCommand::TwoRegularCommandsToMPT(effect2, effectParam2, effect3, effectParam3); if(m->volcmd == VOLCMD_NONE && effect2 != VOLCMD_NONE) { m->volcmd = effect2; m->vol = effectParam2; } // Prefer instrument effects over any other effects if(effect1 != CMD_NONE) { ModCommand::TwoRegularCommandsToMPT(effect3, effectParam3, effect1, effectParam1); if(m->volcmd == VOLCMD_NONE && effect3 != VOLCMD_NONE) { m->volcmd = effect3; m->vol = effectParam3; } m->command = effect1; m->param = effectParam1; } else if(effect3 != CMD_NONE) { m->command = effect3; m->param = effectParam3; } } else { channelCounter[chn]--; } } // End for all channels // Now we can try to write tempo information. if(tempoChange) { tempoChange = false; sndFile.Patterns[pat].WriteEffect(EffectWriter(CMD_TEMPO, static_cast(tempo)).Row(row).Channel(0).RetryNextRow()); sndFile.Patterns[pat].WriteEffect(EffectWriter(CMD_SPEED, static_cast(speed)).Row(row).RetryNextRow()); } // Try to put delay effects somewhere as well if(writeDelay & 0xF0) { sndFile.Patterns[pat].WriteEffect(EffectWriter(CMD_S3MCMDEX, 0xE0 | (writeDelay >> 4)).Row(row).AllowMultiple()); } if(writeDelay & 0x0F) { const uint8 param = (writeDelay & 0x0F) * settings.internalTicks / 15; sndFile.Patterns[pat].WriteEffect(EffectWriter(CMD_S3MCMDEX, 0x60u | Clamp(param, uint8(1), uint8(15))).Row(row).AllowMultiple()); } writeDelay = 0; } // End for all rows return pat; } static bool ValidateHeader(const DMFFileHeader &fileHeader) { if(std::memcmp(fileHeader.signature, "DDMF", 4) || !fileHeader.version || fileHeader.version > 10) { return false; } return true; } CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderDMF(MemoryFileReader file, const uint64 *pfilesize) { DMFFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return ProbeWantMoreData; } if(!ValidateHeader(fileHeader)) { return ProbeFailure; } MPT_UNREFERENCED_PARAMETER(pfilesize); return ProbeSuccess; } bool CSoundFile::ReadDMF(FileReader &file, ModLoadingFlags loadFlags) { file.Rewind(); DMFFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return false; } if(!ValidateHeader(fileHeader)) { return false; } if(loadFlags == onlyVerifyHeader) { return true; } InitializeGlobals(MOD_TYPE_DMF); m_modFormat.formatName = MPT_UFORMAT("X-Tracker v{}")(fileHeader.version); m_modFormat.type = U_("dmf"); m_modFormat.charset = mpt::Charset::CP437; m_songName = mpt::String::ReadBuf(mpt::String::spacePadded, fileHeader.songname); m_songArtist = mpt::ToUnicode(mpt::Charset::CP437, mpt::String::ReadBuf(mpt::String::spacePadded, fileHeader.composer)); FileHistory mptHistory; mptHistory.loadDate.tm_mday = Clamp(fileHeader.creationDay, uint8(1), uint8(31)); mptHistory.loadDate.tm_mon = Clamp(fileHeader.creationMonth, uint8(1), uint8(12)) - 1; mptHistory.loadDate.tm_year = fileHeader.creationYear; m_FileHistory.clear(); m_FileHistory.push_back(mptHistory); // Go through all chunks now... cannot use our standard IFF chunk reader here because early X-Tracker versions write some malformed chunk headers... fun code ahead! ChunkReader::ChunkList chunks; while(file.CanRead(sizeof(DMFChunk))) { DMFChunk chunkHeader; file.Read(chunkHeader); uint32 chunkLength = chunkHeader.length, chunkSkip = 0; // When loop start was added to version 3, the chunk size was not updated... if(fileHeader.version == 3 && chunkHeader.GetID() == DMFChunk::idSEQU) chunkSkip = 2; // ...and when the loop end was added to version 4, it was also note updated! Luckily they fixed it in version 5. else if(fileHeader.version == 4 && chunkHeader.GetID() == DMFChunk::idSEQU) chunkSkip = 4; // Earlier X-Tracker versions also write a garbage length for the SMPD chunk if samples are compressed. // I don't know when exactly this stopped, but I have no version 5-7 files to check (and no X-Tracker version that writes those versions). // Since this is practically always the last chunk in the file, the following code is safe for those versions, though. else if(fileHeader.version < 8 && chunkHeader.GetID() == DMFChunk::idSMPD) chunkLength = uint32_max; chunks.chunks.push_back(ChunkReader::Item{chunkHeader, file.ReadChunk(chunkLength)}); file.Skip(chunkSkip); } FileReader chunk; // Read order list chunk = chunks.GetChunk(DMFChunk::idSEQU); ORDERINDEX seqLoopStart = 0, seqLoopEnd = ORDERINDEX_MAX; if(fileHeader.version >= 3) seqLoopStart = chunk.ReadUint16LE(); if(fileHeader.version >= 4) seqLoopEnd = chunk.ReadUint16LE(); // HIPOMATK.DMF has a loop end of 0, other v4 files have proper loop ends. Later X-Tracker versions import it as-is but it cannot be intentional. // We just assume that this feature might have been buggy in early v4 versions and ignore the loop end in that case. if(fileHeader.version == 4 && seqLoopEnd == 0) seqLoopEnd = ORDERINDEX_MAX; ReadOrderFromFile(Order(), chunk, chunk.BytesLeft() / 2); LimitMax(seqLoopStart, Order().GetLastIndex()); LimitMax(seqLoopEnd, Order().GetLastIndex()); // Read patterns chunk = chunks.GetChunk(DMFChunk::idPATT); if(chunk.IsValid() && (loadFlags & loadPatternData)) { DMFPatterns patHeader; chunk.ReadStruct(patHeader); m_nChannels = Clamp(patHeader.numTracks, 1, 32) + 1; // + 1 for global track (used for tempo stuff) // First, find out where all of our patterns are... std::vector patternChunks(patHeader.numPatterns); for(auto &patternChunk : patternChunks) { const uint8 headerSize = fileHeader.version < 3 ? 9 : 8; chunk.Skip(headerSize - sizeof(uint32le)); const uint32 patLength = chunk.ReadUint32LE(); chunk.SkipBack(headerSize); patternChunk = chunk.ReadChunk(headerSize + patLength); } // Now go through the order list and load them. DMFPatternSettings settings(GetNumChannels()); Patterns.ResizeArray(Order().GetLength()); for(PATTERNINDEX &pat : Order()) { // Create one pattern for each order item, as the same pattern can be played with different settings if(pat < patternChunks.size()) { pat = ConvertDMFPattern(patternChunks[pat], fileHeader.version, settings, *this); } } // Write loop end if necessary if(Order().IsValidPat(seqLoopEnd) && (seqLoopStart > 0 || seqLoopEnd < Order().GetLastIndex())) { PATTERNINDEX pat = Order()[seqLoopEnd]; Patterns[pat].WriteEffect(EffectWriter(CMD_POSITIONJUMP, static_cast(seqLoopStart)).Row(Patterns[pat].GetNumRows() - 1).RetryPreviousRow()); } } // Read song message chunk = chunks.GetChunk(DMFChunk::idCMSG); if(chunk.IsValid()) { // The song message seems to start at a 1 byte offset. // The skipped byte seems to always be 0. // This also matches how XT 1.03 itself displays the song message. chunk.Skip(1); m_songMessage.ReadFixedLineLength(chunk, chunk.BytesLeft(), 40, 0); } // Read sample headers + data FileReader sampleDataChunk = chunks.GetChunk(DMFChunk::idSMPD); chunk = chunks.GetChunk(DMFChunk::idSMPI); m_nSamples = chunk.ReadUint8(); for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++) { const uint8 nameLength = (fileHeader.version < 2) ? 30 : chunk.ReadUint8(); chunk.ReadString(m_szNames[smp], nameLength); DMFSampleHeader sampleHeader; ModSample &sample = Samples[smp]; chunk.ReadStruct(sampleHeader); sampleHeader.ConvertToMPT(sample); // Read library name in version 8 files if(fileHeader.version >= 8) chunk.ReadString(sample.filename, 8); // Filler + CRC chunk.Skip(fileHeader.version > 1 ? 6 : 2); // Now read the sample data from the data chunk FileReader sampleData = sampleDataChunk.ReadChunk(sampleDataChunk.ReadUint32LE()); if(sampleData.IsValid() && (loadFlags & loadSampleData)) { SampleIO( sample.uFlags[CHN_16BIT] ? SampleIO::_16bit : SampleIO::_8bit, SampleIO::mono, SampleIO::littleEndian, (sampleHeader.flags & DMFSampleHeader::smpCompMask) == DMFSampleHeader::smpComp1 ? SampleIO::DMF : SampleIO::signedPCM) .ReadSample(sample, sampleData); } } InitializeChannels(); m_SongFlags = SONG_LINEARSLIDES | SONG_ITCOMPATGXX; // this will be converted to IT format by MPT. SONG_ITOLDEFFECTS is not set because of tremor and vibrato. m_nDefaultSpeed = 6; m_nDefaultTempo.Set(120); m_nDefaultGlobalVolume = 256; m_nSamplePreAmp = m_nVSTiVolume = 48; m_playBehaviour.set(kApplyOffsetWithoutNote); return true; } /////////////////////////////////////////////////////////////////////// // DMF Compression struct DMFHNode { int16 left, right; uint8 value; }; struct DMFHTree { BitReader file; int lastnode = 0, nodecount = 0; DMFHNode nodes[256]{}; DMFHTree(FileReader &file) : file(file) { } // // tree: [8-bit value][12-bit index][12-bit index] = 32-bit // void DMFNewNode() { int actnode = nodecount; if(actnode > 255) return; nodes[actnode].value = static_cast(file.ReadBits(7)); bool isLeft = file.ReadBits(1) != 0; bool isRight = file.ReadBits(1) != 0; actnode = lastnode; if(actnode > 255) return; nodecount++; lastnode = nodecount; if(isLeft) { nodes[actnode].left = (int16)lastnode; DMFNewNode(); } else { nodes[actnode].left = -1; } lastnode = nodecount; if(isRight) { nodes[actnode].right = (int16)lastnode; DMFNewNode(); } else { nodes[actnode].right = -1; } } }; uintptr_t DMFUnpack(FileReader &file, uint8 *psample, uint32 maxlen) { DMFHTree tree(file); uint8 value = 0, delta = 0; try { tree.DMFNewNode(); if(tree.nodes[0].left < 0 || tree.nodes[0].right < 0) return tree.file.GetPosition(); for(uint32 i = 0; i < maxlen; i++) { int actnode = 0; bool sign = tree.file.ReadBits(1) != 0; do { if(tree.file.ReadBits(1)) actnode = tree.nodes[actnode].right; else actnode = tree.nodes[actnode].left; if(actnode > 255) break; delta = tree.nodes[actnode].value; } while((tree.nodes[actnode].left >= 0) && (tree.nodes[actnode].right >= 0)); if(sign) delta ^= 0xFF; value += delta; psample[i] = value; } } catch(const BitReader::eof &) { //AddToLog(LogWarning, "Truncated DMF sample block"); } return tree.file.GetPosition(); } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Load_dsm.cpp0000644000175000017500000002031313741412136020322 00000000000000/* * Load_dsm.cpp * ------------ * Purpose: Digisound Interface Kit (DSIK) Internal Format (DSM v2 / RIFF) module loader * Notes : 1. There is also another fundamentally different DSIK DSM v1 module format, not handled here. * MilkyTracker can load it, but the only files of this format seen in the wild are also * available in their original format, so I did not bother implementing it so far. * * 2. Using both PLAY.EXE v1.02 and v2.00, commands not supported in MOD do not seem to do * anything at all. * In particular commands 0x11-0x13 handled below are ignored, and no files have been spotted * in the wild using any commands > 0x0F at all. * S3M-style retrigger does not seem to exist - it is translated to volume slides by CONV.EXE, * and J00 in S3M files is not converted either. S3M pattern loops (SBx) are not converted * properly by CONV.EXE and completely ignored by PLAY.EXE. * Command 8 (set panning) uses 00-80 for regular panning and A4 for surround, probably * making DSIK one of the first applications to use this particular encoding scheme still * used in "extended" S3Ms today. * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Loaders.h" OPENMPT_NAMESPACE_BEGIN struct DSMChunk { char magic[4]; uint32le size; }; MPT_BINARY_STRUCT(DSMChunk, 8) struct DSMSongHeader { char songName[28]; uint16le fileVersion; uint16le flags; uint16le orderPos; uint16le restartPos; uint16le numOrders; uint16le numSamples; uint16le numPatterns; uint16le numChannels; uint8le globalVol; uint8le mastervol; uint8le speed; uint8le bpm; uint8le panPos[16]; uint8le orders[128]; }; MPT_BINARY_STRUCT(DSMSongHeader, 192) struct DSMSampleHeader { char filename[13]; uint16le flags; uint8le volume; uint32le length; uint32le loopStart; uint32le loopEnd; uint32le dataPtr; // Interal sample pointer during playback in DSIK uint32le sampleRate; char sampleName[28]; // Convert a DSM sample header to OpenMPT's internal sample header. void ConvertToMPT(ModSample &mptSmp) const { mptSmp.Initialize(); mptSmp.filename = mpt::String::ReadBuf(mpt::String::nullTerminated, filename); mptSmp.nC5Speed = sampleRate; mptSmp.uFlags.set(CHN_LOOP, (flags & 1) != 0); mptSmp.nLength = length; mptSmp.nLoopStart = loopStart; mptSmp.nLoopEnd = loopEnd; mptSmp.nVolume = std::min(volume.get(), uint8(64)) * 4; } // Retrieve the internal sample format flags for this sample. SampleIO GetSampleFormat() const { SampleIO sampleIO( SampleIO::_8bit, SampleIO::mono, SampleIO::littleEndian, SampleIO::unsignedPCM); if(flags & 0x40) sampleIO |= SampleIO::deltaPCM; // fairlight.dsm by Comrade J else if(flags & 0x02) sampleIO |= SampleIO::signedPCM; if(flags & 0x04) sampleIO |= SampleIO::_16bit; return sampleIO; } }; MPT_BINARY_STRUCT(DSMSampleHeader, 64) struct DSMHeader { char fileMagic0[4]; char fileMagic1[4]; char fileMagic2[4]; }; MPT_BINARY_STRUCT(DSMHeader, 12) static bool ValidateHeader(const DSMHeader &fileHeader) { if(!std::memcmp(fileHeader.fileMagic0, "RIFF", 4) && !std::memcmp(fileHeader.fileMagic2, "DSMF", 4)) { // "Normal" DSM files with RIFF header // return true; } else if(!std::memcmp(fileHeader.fileMagic0, "DSMF", 4)) { // DSM files with alternative header // <4 bytes, usually 4x NUL or RIFF> <4 bytes, usually DSMF but not always> return true; } else { return false; } } CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderDSM(MemoryFileReader file, const uint64 *pfilesize) { DSMHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return ProbeWantMoreData; } if(!ValidateHeader(fileHeader)) { return ProbeFailure; } if(std::memcmp(fileHeader.fileMagic0, "DSMF", 4) == 0) { if(!file.Skip(4)) { return ProbeWantMoreData; } } DSMChunk chunkHeader; if(!file.ReadStruct(chunkHeader)) { return ProbeWantMoreData; } if(std::memcmp(chunkHeader.magic, "SONG", 4)) { return ProbeFailure; } MPT_UNREFERENCED_PARAMETER(pfilesize); return ProbeSuccess; } bool CSoundFile::ReadDSM(FileReader &file, ModLoadingFlags loadFlags) { file.Rewind(); DSMHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return false; } if(!ValidateHeader(fileHeader)) { return false; } if(std::memcmp(fileHeader.fileMagic0, "DSMF", 4) == 0) { file.Skip(4); } DSMChunk chunkHeader; if(!file.ReadStruct(chunkHeader)) { return false; } // Technically, the song chunk could be anywhere in the file, but we're going to simplify // things by not using a chunk header here and just expect it to be right at the beginning. if(std::memcmp(chunkHeader.magic, "SONG", 4)) { return false; } if(loadFlags == onlyVerifyHeader) { return true; } DSMSongHeader songHeader; file.ReadStructPartial(songHeader, chunkHeader.size); if(songHeader.numOrders > 128 || songHeader.numChannels > 16 || songHeader.numPatterns > 256 || songHeader.restartPos > 128) { return false; } InitializeGlobals(MOD_TYPE_DSM); m_modFormat.formatName = U_("DSIK Format"); m_modFormat.type = U_("dsm"); m_modFormat.charset = mpt::Charset::CP437; m_songName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, songHeader.songName); m_nChannels = std::max(songHeader.numChannels.get(), uint16(1)); m_nDefaultSpeed = songHeader.speed; m_nDefaultTempo.Set(songHeader.bpm); m_nDefaultGlobalVolume = std::min(songHeader.globalVol.get(), uint8(64)) * 4u; if(!m_nDefaultGlobalVolume) m_nDefaultGlobalVolume = MAX_GLOBAL_VOLUME; if(songHeader.mastervol == 0x80) { m_nSamplePreAmp = std::min(256u / m_nChannels, 128u); } else { m_nSamplePreAmp = songHeader.mastervol & 0x7F; } // Read channel panning for(CHANNELINDEX chn = 0; chn < 16; chn++) { ChnSettings[chn].Reset(); if(songHeader.panPos[chn] <= 0x80) { ChnSettings[chn].nPan = songHeader.panPos[chn] * 2; } } ReadOrderFromArray(Order(), songHeader.orders, songHeader.numOrders, 0xFF, 0xFE); if(songHeader.restartPos < songHeader.numOrders) Order().SetRestartPos(songHeader.restartPos); // Read pattern and sample chunks PATTERNINDEX patNum = 0; while(file.ReadStruct(chunkHeader)) { FileReader chunk = file.ReadChunk(chunkHeader.size); if(!memcmp(chunkHeader.magic, "PATT", 4) && (loadFlags & loadPatternData)) { // Read pattern if(!Patterns.Insert(patNum, 64)) { continue; } chunk.Skip(2); ModCommand dummy = ModCommand::Empty(); ROWINDEX row = 0; while(chunk.CanRead(1) && row < 64) { uint8 flag = chunk.ReadUint8(); if(!flag) { row++; continue; } CHANNELINDEX chn = (flag & 0x0F); ModCommand &m = (chn < GetNumChannels() ? *Patterns[patNum].GetpModCommand(row, chn) : dummy); if(flag & 0x80) { uint8 note = chunk.ReadUint8(); if(note) { if(note <= 12 * 9) note += 11 + NOTE_MIN; m.note = note; } } if(flag & 0x40) { m.instr = chunk.ReadUint8(); } if (flag & 0x20) { m.volcmd = VOLCMD_VOLUME; m.vol = std::min(chunk.ReadUint8(), uint8(64)); } if(flag & 0x10) { auto [command, param] = chunk.ReadArray(); switch(command) { // Portamentos case 0x11: case 0x12: command &= 0x0F; break; // 3D Sound (?) case 0x13: command = 'X' - 55; param = 0x91; break; default: // Volume + Offset (?) if(command > 0x10) command = ((command & 0xF0) == 0x20) ? 0x09 : 0xFF; } m.command = command; m.param = param; ConvertModCommand(m); } } patNum++; } else if(!memcmp(chunkHeader.magic, "INST", 4) && CanAddMoreSamples()) { // Read sample m_nSamples++; ModSample &sample = Samples[m_nSamples]; DSMSampleHeader sampleHeader; chunk.ReadStruct(sampleHeader); sampleHeader.ConvertToMPT(sample); m_szNames[m_nSamples] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, sampleHeader.sampleName); if(loadFlags & loadSampleData) { sampleHeader.GetSampleFormat().ReadSample(sample, chunk); } } } return true; } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Load_dsym.cpp0000644000175000017500000004267314166574204020537 00000000000000/* * Load_dsym.cpp * ------------- * Purpose: Digital Symphony module loader * Notes : Based on information from the DSym_Info file and sigma-delta decompression code from TimPlayer. * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Loaders.h" #include "BitReader.h" OPENMPT_NAMESPACE_BEGIN struct DSymFileHeader { char magic[8]; uint8le version; // 0 / 1 uint8le numChannels; // 1...8 uint16le numOrders; // 0...4096 uint16le numTracks; // 0...4096 uint16le infoLenLo; uint8le infoLenHi; bool Validate() const { return !std::memcmp(magic, "\x02\x01\x13\x13\x14\x12\x01\x0B", 8) && version <= 1 && numChannels >= 1 && numChannels <= 8 && numOrders <= 4096 && numTracks <= 4096; } uint64 GetHeaderMinimumAdditionalSize() const { return 72u; } }; MPT_BINARY_STRUCT(DSymFileHeader, 17) static std::vector DecompressDSymLZW(FileReader &file, uint32 size) { BitReader bitFile(file); const auto startPos = bitFile.GetPosition(); // In the best case, 13 bits decode 8192 bytes, a ratio of approximately 1:5042. // Too much for reserving memory in case of malformed files, just choose an arbitrary but realistic upper limit. std::vector output; output.reserve(std::min(size, std::min(mpt::saturate_cast(file.BytesLeft()), Util::MaxValueOfType(size) / 50u) * 50u)); static constexpr uint16 lzwBits = 13, MaxNodes = 1 << lzwBits; static constexpr uint16 ResetDict = 256, EndOfStream = 257; struct LZWEntry { uint16 prev; std::byte value; }; std::vector dictionary(MaxNodes); std::vector match(MaxNodes); // Initialize dictionary for(int i = 0; i < 256; i++) { dictionary[i].prev = MaxNodes; dictionary[i].value = static_cast(i); } uint8 codeSize = 9; uint16 prevCode = 0; uint16 nextIndex = 257; while(true) { // Read next code const auto newCode = static_cast(bitFile.ReadBits(codeSize)); if(newCode == EndOfStream || newCode > nextIndex || output.size() >= size) break; // Reset dictionary if(newCode == ResetDict) { codeSize = 9; prevCode = 0; nextIndex = 257; continue; } // Output auto code = (newCode < nextIndex) ? newCode : prevCode; auto writeOffset = MaxNodes; do { match[--writeOffset] = dictionary[code].value; code = dictionary[code].prev; } while(code < MaxNodes); output.insert(output.end(), match.begin() + writeOffset, match.end()); // Handling for KwKwK problem if(newCode == nextIndex) output.push_back(match[writeOffset]); // Add to dictionary if(nextIndex < MaxNodes) { // Special case for FULLEFFECT, NARCOSIS and NEWDANCE, which end with a dictionary size of 512 // right before the end-of-stream token, but the code size is expected to be 9 if(output.size() >= size) continue; dictionary[nextIndex].value = match[writeOffset]; dictionary[nextIndex].prev = prevCode; nextIndex++; if(nextIndex != MaxNodes && nextIndex == (1u << codeSize)) codeSize++; } prevCode = newCode; } MPT_ASSERT(output.size() == size); // Align length to 4 bytes file.Seek(startPos + ((bitFile.GetPosition() - startPos + 3u) & ~FileReader::off_t(3))); return output; } static std::vector DecompressDSymSigmaDelta(FileReader &file, uint32 size) { const uint8 maxRunLength = std::max(file.ReadUint8(), uint8(1)); BitReader bitFile(file); const auto startPos = bitFile.GetPosition(); // In the best case, sigma-delta compression represents each sample point as one bit. // As a result, if we have a file length of n, we know that the sample can be at most n*8 sample points long. LimitMax(size, std::min(mpt::saturate_cast(file.BytesLeft()), Util::MaxValueOfType(size) / 8u) * 8u); std::vector output(size); uint32 pos = 0; uint8 runLength = maxRunLength; uint8 numBits = 8; uint8 accum = static_cast(bitFile.ReadBits(numBits)); output[pos++] = mpt::byte_cast(accum); while(pos < size) { const uint32 value = bitFile.ReadBits(numBits); // Increase bit width if(value == 0) { if(numBits >= 9) break; numBits++; runLength = maxRunLength; continue; } if(value & 1) accum -= static_cast(value >> 1); else accum += static_cast(value >> 1); output[pos++] = mpt::byte_cast(accum); // Reset run length if high bit is set if((value >> (numBits - 1u)) != 0) { runLength = maxRunLength; continue; } // Decrease bit width if(--runLength == 0) { if(numBits > 1) numBits--; runLength = maxRunLength; } } // Align length to 4 bytes file.Seek(startPos + ((bitFile.GetPosition() - startPos + 3u) & ~FileReader::off_t(3))); return output; } static bool ReadDSymChunk(FileReader &file, std::vector &data, uint32 size) { const uint8 packingType = file.ReadUint8(); if(packingType > 1) return false; if(packingType) { try { data = DecompressDSymLZW(file, size); } catch(const BitReader::eof &) { return false; } } else { if(!file.CanRead(size)) return false; file.ReadVector(data, size); } return data.size() >= size; } CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderDSym(MemoryFileReader file, const uint64 *pfilesize) { DSymFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) return ProbeWantMoreData; if(!fileHeader.Validate()) return ProbeFailure; return ProbeAdditionalSize(file, pfilesize, fileHeader.GetHeaderMinimumAdditionalSize()); } bool CSoundFile::ReadDSym(FileReader &file, ModLoadingFlags loadFlags) { DSymFileHeader fileHeader; file.Rewind(); if(!file.ReadStruct(fileHeader) || !fileHeader.Validate()) return false; if(!file.CanRead(mpt::saturate_cast(fileHeader.GetHeaderMinimumAdditionalSize()))) return false; if(loadFlags == onlyVerifyHeader) return true; InitializeGlobals(MOD_TYPE_MOD); m_SongFlags.set(SONG_IMPORTED | SONG_AMIGALIMITS); m_SongFlags.reset(SONG_ISAMIGA); m_nChannels = fileHeader.numChannels; m_nSamples = 63; for(CHANNELINDEX chn = 0; chn < m_nChannels; chn++) { InitChannel(chn); ChnSettings[chn].nPan = (((chn & 3) == 1) || ((chn & 3) == 2)) ? 64 : 192; } uint8 sampleNameLength[64]; for(SAMPLEINDEX smp = 1; smp <= m_nSamples; smp++) { Samples[smp].Initialize(MOD_TYPE_MOD); sampleNameLength[smp] = file.ReadUint8(); if(!(sampleNameLength[smp] & 0x80)) Samples[smp].nLength = file.ReadUint24LE() << 1; } file.ReadSizedString(m_songName); const auto allowedCommands = file.ReadArray(); std::vector sequenceData; if(fileHeader.numOrders) { const uint32 sequenceSize = fileHeader.numOrders * fileHeader.numChannels * 2u; if(!ReadDSymChunk(file, sequenceData, sequenceSize)) return false; } const auto sequence = mpt::as_span(reinterpret_cast(sequenceData.data()), sequenceData.size() / 2u); std::vector trackData; trackData.reserve(fileHeader.numTracks * 256u); // For some reason, patterns are stored in 512K chunks for(uint16 offset = 0; offset < fileHeader.numTracks; offset += 2000) { const uint32 chunkSize = std::min(fileHeader.numTracks - offset, 2000) * 256; std::vector chunk; if(!ReadDSymChunk(file, chunk, chunkSize)) return false; trackData.insert(trackData.end(), chunk.begin(), chunk.end()); } const auto tracks = mpt::byte_cast>(mpt::as_span(trackData)); Order().resize(fileHeader.numOrders); for(ORDERINDEX pat = 0; pat < fileHeader.numOrders; pat++) { Order()[pat] = pat; if(!(loadFlags & loadPatternData) || !Patterns.Insert(pat, 64)) continue; for(CHANNELINDEX chn = 0; chn < m_nChannels; chn++) { const uint16 track = sequence[pat * m_nChannels + chn]; if(track >= fileHeader.numTracks) continue; ModCommand *m = Patterns[pat].GetpModCommand(0, chn); for(ROWINDEX row = 0; row < 64; row++, m += m_nChannels) { const auto data = tracks.subspan(track * 256 + row * 4, 4); m->note = data[0] & 0x3F; if(m->note) m->note += 47 + NOTE_MIN; else m->note = NOTE_NONE; m->instr = (data[0] >> 6) | ((data[1] & 0x0F) << 2); const uint8 command = (data[1] >> 6) | ((data[2] & 0x0F) << 2); const uint16 param = (data[2] >> 4) | (data[3] << 4); if(!(allowedCommands[command >> 3u] & (1u << (command & 7u)))) continue; if(command == 0 && param == 0) continue; m->command = command; m->param = static_cast(param); m->vol = static_cast(param >> 8); switch(command) { case 0x00: // 00 xyz Normal play or Arpeggio + Volume Slide Up case 0x01: // 01 xyy Slide Up + Volume Slide Up case 0x02: // 01 xyy Slide Up + Volume Slide Up case 0x20: // 20 xyz Normal play or Arpeggio + Volume Slide Down case 0x21: // 21 xyy Slide Up + Volume Slide Down case 0x22: // 22 xyy Slide Down + Volume Slide Down m->command &= 0x0F; ConvertModCommand(*m); if(m->vol) m->volcmd = (command < 0x20) ? VOLCMD_VOLSLIDEUP : VOLCMD_VOLSLIDEDOWN; break; case 0x03: // 03 xyy Tone Portamento case 0x04: // 04 xyz Vibrato case 0x05: // 05 xyz Tone Portamento + Volume Slide case 0x06: // 06 xyz Vibrato + Volume Slide case 0x07: // 07 xyz Tremolo case 0x0C: // 0C xyy Set Volume ConvertModCommand(*m); break; case 0x09: // 09 xxx Set Sample Offset m->command = CMD_OFFSET; m->param = static_cast(param >> 1); if(param >= 0x200) { m->volcmd = VOLCMD_OFFSET; m->vol >>= 1; } break; case 0x0A: // 0A xyz Volume Slide + Fine Slide Up case 0x2A: // 2A xyz Volume Slide + Fine Slide Down if(param < 0xFF) { m->command &= 0x0F; ConvertModCommand(*m); } else { m->command = CMD_MODCMDEX; m->param = static_cast(((command < 0x20) ? 0x10 : 0x20) | (param >> 8)); if(param & 0xF0) { m->volcmd = VOLCMD_VOLSLIDEUP; m->vol = static_cast((param >> 4) & 0x0F); } else { m->volcmd = VOLCMD_VOLSLIDEDOWN; m->vol = static_cast(param & 0x0F); } } break; case 0x0B: // 0B xxx Position Jump case 0x0F: // 0F xxx Set Speed m->command = (command == 0x0B) ? CMD_POSITIONJUMP : CMD_SPEED; m->param = mpt::saturate_cast(param); break; case 0x0D: // 0D xyy Pattern Break (not BCD-encoded like in MOD) m->command = CMD_PATTERNBREAK; if(m->param > 63) m->param = 0; break; case 0x10: // 10 xxy Filter Control (not implemented in Digital Symphony) case 0x13: // 13 xxy Glissando Control case 0x14: // 14 xxy Set Vibrato Waveform case 0x15: // 15 xxy Set Fine Tune case 0x17: // 17 xxy Set Tremolo Waveform case 0x1F: // 1F xxy Invert Loop m->command = CMD_MODCMDEX; m->param = (command << 4) | (m->param & 0x0F); break; case 0x16: // 16 xxx Jump to Loop case 0x19: // 19 xxx Retrig Note case 0x1C: // 1C xxx Note Cut case 0x1D: // 1D xxx Note Delay case 0x1E: // 1E xxx Pattern Delay m->command = CMD_MODCMDEX; m->param = (command << 4) | static_cast(std::min(param, uint16(0x0F))); break; case 0x11: // 11 xyy Fine Slide Up + Fine Volume Slide Up case 0x12: // 12 xyy Fine Slide Down + Fine Volume Slide Up case 0x1A: // 1A xyy Fine Slide Up + Fine Volume Slide Down case 0x1B: // 1B xyy Fine Slide Down + Fine Volume Slide Down m->command = CMD_MODCMDEX; if(m->param & 0xFF) { m->param = static_cast(((command == 0x11 || command == 0x1A) ? 0x10 : 0x20) | (param & 0x0F)); if(param & 0xF00) m->volcmd = (command >= 0x1A) ? VOLCMD_FINEVOLDOWN : VOLCMD_FINEVOLUP; } else { m->param = static_cast(((command >= 0x1A) ? 0xB0 : 0xA0) | (param >> 8)); } break; case 0x2F: // 2F xxx Set Tempo if(param > 0) { m->command = CMD_TEMPO; m->param = mpt::saturate_cast(std::max(8, param + 4) / 8); #ifdef MODPLUG_TRACKER m->param = std::max(m->param, ModCommand::PARAM(0x20)); #endif } else { m->command = CMD_NONE; } break; case 0x2B: // 2B xyy Line Jump m->command = CMD_PATTERNBREAK; for(CHANNELINDEX brkChn = 0; brkChn < m_nChannels; brkChn++) { ModCommand &cmd = *(m - chn + brkChn); if(cmd.command != CMD_NONE) continue; cmd.command = CMD_POSITIONJUMP; cmd.param = mpt::saturate_cast(pat); } break; case 0x30: // 30 xxy Set Stereo m->command = CMD_PANNING8; if(param & 7) { static constexpr uint8 panning[8] = {0x00, 0x00, 0x2B, 0x56, 0x80, 0xAA, 0xD4, 0xFF}; m->param = panning[param & 7]; } else if((param >> 4) != 0x80) { m->param = static_cast(param >> 4); if(m->param < 0x80) m->param += 0x80; else m->param = 0xFF - m->param; } else { m->command = CMD_NONE; } break; case 0x32: // 32 xxx Unset Sample Repeat m->command = CMD_NONE; m->param = 0; if(m->note == NOTE_NONE) m->note = NOTE_KEYOFF; else m->command = CMD_KEYOFF; break; case 0x31: // 31 xxx Song Upcall default: m->command = CMD_NONE; break; } } } } for(SAMPLEINDEX smp = 1; smp <= m_nSamples; smp++) { file.ReadString(m_szNames[smp], sampleNameLength[smp] & 0x3F); if(sampleNameLength[smp] & 0x80) continue; ModSample &mptSmp = Samples[smp]; mptSmp.nSustainStart = file.ReadUint24LE() << 1; if(const auto loopLen = file.ReadUint24LE() << 1; loopLen > 2) { mptSmp.nSustainEnd = mptSmp.nSustainStart + loopLen; mptSmp.uFlags.set(CHN_SUSTAINLOOP); } mptSmp.nVolume = std::min(file.ReadUint8(), uint8(64)) * 4u; mptSmp.nFineTune = MOD2XMFineTune(file.ReadUint8()); mptSmp.Set16BitCuePoints(); if(!mptSmp.nLength) continue; const uint8 packingType = file.ReadUint8(); switch(packingType) { case 0: // Modified u-Law if(loadFlags & loadSampleData) { std::vector sampleData; if(!file.CanRead(mptSmp.nLength)) return false; file.ReadVector(sampleData, mptSmp.nLength); for(auto &b : sampleData) { uint8 v = mpt::byte_cast(b); v = (v << 7) | (static_cast(~v) >> 1); b = mpt::byte_cast(v); } FileReader sampleDataFile = FileReader(mpt::as_span(sampleData)); SampleIO( SampleIO::_16bit, SampleIO::mono, SampleIO::littleEndian, SampleIO::uLaw) .ReadSample(mptSmp, sampleDataFile); } else { file.Skip(mptSmp.nLength); } break; case 1: // 13-bit LZW applied to linear sample data differences { std::vector sampleData; try { sampleData = DecompressDSymLZW(file, mptSmp.nLength); } catch(const BitReader::eof &) { return false; } if(!(loadFlags & loadSampleData)) break; FileReader sampleDataFile = FileReader(mpt::as_span(sampleData)); SampleIO( SampleIO::_8bit, SampleIO::mono, SampleIO::littleEndian, SampleIO::deltaPCM) .ReadSample(mptSmp, sampleDataFile); } break; case 2: // 8-bit signed case 3: // 16-bit signed if(loadFlags & loadSampleData) { SampleIO( (packingType == 2) ? SampleIO::_8bit : SampleIO::_16bit, SampleIO::mono, SampleIO::littleEndian, SampleIO::signedPCM) .ReadSample(mptSmp, file); } else { file.Skip(mptSmp.nLength * (packingType - 1)); } break; case 4: // Sigma-Delta compression applied to linear sample differences case 5: // Sigma-Delta compression applied to logarithmic sample differences { std::vector sampleData; try { sampleData = DecompressDSymSigmaDelta(file, mptSmp.nLength); } catch(const BitReader::eof &) { return false; } if(!(loadFlags & loadSampleData)) break; if(packingType == 5) { static constexpr uint8 xorMask[] = {0x00, 0x7F}; for(auto &b : sampleData) { uint8 v = mpt::byte_cast(b); v ^= xorMask[v >> 7]; b = mpt::byte_cast(v); } } FileReader sampleDataFile = FileReader(mpt::as_span(sampleData)); SampleIO( (packingType == 5) ? SampleIO::_16bit : SampleIO::_8bit, SampleIO::mono, SampleIO::littleEndian, (packingType == 5) ? SampleIO::uLaw : SampleIO::unsignedPCM) .ReadSample(mptSmp, sampleDataFile); } break; default: return false; } } if(const uint32 infoLen = fileHeader.infoLenLo | (fileHeader.infoLenHi << 16); infoLen > 0) { std::vector infoData; if(!ReadDSymChunk(file, infoData, infoLen)) return false; FileReader infoChunk = FileReader(mpt::as_span(infoData)); m_songMessage.Read(infoChunk, infoLen, SongMessage::leLF); } m_modFormat.formatName = MPT_UFORMAT("Digital Symphony v{}")(fileHeader.version); m_modFormat.type = U_("dsym"); // RISC OS doesn't use file extensions but this is a common abbreviation used for this tracker m_modFormat.madeWithTracker = U_("Digital Symphony"); m_modFormat.charset = mpt::Charset::ISO8859_1; // Close enough (RISC OS uses slightly customized variant) return true; } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Load_dtm.cpp0000644000175000017500000003733714072320411020331 00000000000000/* * Load_dtm.cpp * ------------ * Purpose: Digital Tracker / Digital Home Studio module Loader (DTM) * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Loaders.h" OPENMPT_NAMESPACE_BEGIN enum PatternFormats : uint32 { DTM_PT_PATTERN_FORMAT = 0, DTM_204_PATTERN_FORMAT = MagicBE("2.04"), DTM_206_PATTERN_FORMAT = MagicBE("2.06"), }; struct DTMFileHeader { char magic[4]; uint32be headerSize; uint16be type; // 0 = module uint8be stereoMode; // FF = panoramic stereo, 00 = old stereo uint8be bitDepth; // Typically 8, sometimes 16, but is not actually used anywhere? uint16be reserved; // Usually 0, but not in unknown title 1.dtm and unknown title 2.dtm uint16be speed; uint16be tempo; uint32be forcedSampleRate; // Seems to be ignored in newer files }; MPT_BINARY_STRUCT(DTMFileHeader, 22) // IFF-style Chunk struct DTMChunk { // 32-Bit chunk identifiers enum ChunkIdentifiers { idS_Q_ = MagicBE("S.Q."), idPATT = MagicBE("PATT"), idINST = MagicBE("INST"), idIENV = MagicBE("IENV"), idDAPT = MagicBE("DAPT"), idDAIT = MagicBE("DAIT"), idTEXT = MagicBE("TEXT"), idPATN = MagicBE("PATN"), idTRKN = MagicBE("TRKN"), idVERS = MagicBE("VERS"), idSV19 = MagicBE("SV19"), }; uint32be id; uint32be length; size_t GetLength() const { return length; } ChunkIdentifiers GetID() const { return static_cast(id.get()); } }; MPT_BINARY_STRUCT(DTMChunk, 8) struct DTMSample { uint32be reserved; // 0x204 for first sample, 0x208 for second, etc... uint32be length; // in bytes uint8be finetune; // -8....7 uint8be volume; // 0...64 uint32be loopStart; // in bytes uint32be loopLength; // ditto char name[22]; uint8be stereo; uint8be bitDepth; uint16be transpose; uint16be unknown; uint32be sampleRate; void ConvertToMPT(ModSample &mptSmp, uint32 forcedSampleRate, uint32 formatVersion) const { mptSmp.Initialize(MOD_TYPE_IT); mptSmp.nLength = length; mptSmp.nLoopStart = loopStart; mptSmp.nLoopEnd = mptSmp.nLoopStart + loopLength; // In revolution to come.dtm, the file header says samples rate is 24512 Hz, but samples say it's 50000 Hz // Digital Home Studio ignores the header setting in 2.04-/2.06-style modules mptSmp.nC5Speed = (formatVersion == DTM_PT_PATTERN_FORMAT && forcedSampleRate > 0) ? forcedSampleRate : sampleRate; int32 transposeAmount = MOD2XMFineTune(finetune); if(formatVersion == DTM_206_PATTERN_FORMAT && transpose > 0 && transpose != 48) { // Digital Home Studio applies this unconditionally, but some old songs sound wrong then (delirium.dtm). // Digital Tracker 2.03 ignores the setting. // Maybe this should not be applied for "real" Digital Tracker modules? transposeAmount += (48 - transpose) * 128; } mptSmp.Transpose(transposeAmount * (1.0 / (12.0 * 128.0))); mptSmp.nVolume = std::min(volume.get(), uint8(64)) * 4u; if(stereo & 1) { mptSmp.uFlags.set(CHN_STEREO); mptSmp.nLength /= 2u; mptSmp.nLoopStart /= 2u; mptSmp.nLoopEnd /= 2u; } if(bitDepth > 8) { mptSmp.uFlags.set(CHN_16BIT); mptSmp.nLength /= 2u; mptSmp.nLoopStart /= 2u; mptSmp.nLoopEnd /= 2u; } if(mptSmp.nLoopEnd > mptSmp.nLoopStart + 1) { mptSmp.uFlags.set(CHN_LOOP); } else { mptSmp.nLoopStart = mptSmp.nLoopEnd = 0; } } }; MPT_BINARY_STRUCT(DTMSample, 50) struct DTMInstrument { uint16be insNum; uint8be unknown1; uint8be envelope; // 0xFF = none uint8be sustain; // 0xFF = no sustain point uint16be fadeout; uint8be vibRate; uint8be vibDepth; uint8be modulationRate; uint8be modulationDepth; uint8be breathRate; uint8be breathDepth; uint8be volumeRate; uint8be volumeDepth; }; MPT_BINARY_STRUCT(DTMInstrument, 15) struct DTMEnvelope { struct DTMEnvPoint { uint8be value; uint8be tick; }; uint16be numPoints; DTMEnvPoint points[16]; }; MPT_BINARY_STRUCT(DTMEnvelope::DTMEnvPoint, 2) MPT_BINARY_STRUCT(DTMEnvelope, 34) struct DTMText { uint16be textType; // 0 = pattern, 1 = free, 2 = song uint32be textLength; uint16be tabWidth; uint16be reserved; uint16be oddLength; }; MPT_BINARY_STRUCT(DTMText, 12) static bool ValidateHeader(const DTMFileHeader &fileHeader) { if(std::memcmp(fileHeader.magic, "D.T.", 4) || fileHeader.headerSize < sizeof(fileHeader) - 8u || fileHeader.headerSize > 256 // Excessively long song title? || fileHeader.type != 0) { return false; } return true; } CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderDTM(MemoryFileReader file, const uint64 *pfilesize) { DTMFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return ProbeWantMoreData; } if(!ValidateHeader(fileHeader)) { return ProbeFailure; } MPT_UNREFERENCED_PARAMETER(pfilesize); return ProbeSuccess; } bool CSoundFile::ReadDTM(FileReader &file, ModLoadingFlags loadFlags) { file.Rewind(); DTMFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return false; } if(!ValidateHeader(fileHeader)) { return false; } if(loadFlags == onlyVerifyHeader) { return true; } InitializeGlobals(MOD_TYPE_DTM); InitializeChannels(); m_SongFlags.set(SONG_ITCOMPATGXX | SONG_ITOLDEFFECTS); m_playBehaviour.reset(kITVibratoTremoloPanbrello); // Various files have a default speed or tempo of 0 if(fileHeader.tempo) m_nDefaultTempo.Set(fileHeader.tempo); if(fileHeader.speed) m_nDefaultSpeed = fileHeader.speed; if(fileHeader.stereoMode == 0) SetupMODPanning(true); file.ReadString(m_songName, fileHeader.headerSize - (sizeof(fileHeader) - 8u)); auto chunks = ChunkReader(file).ReadChunks(1); // Read order list if(FileReader chunk = chunks.GetChunk(DTMChunk::idS_Q_)) { uint16 ordLen = chunk.ReadUint16BE(); uint16 restartPos = chunk.ReadUint16BE(); chunk.Skip(4); // Reserved ReadOrderFromFile(Order(), chunk, ordLen); Order().SetRestartPos(restartPos); } else { return false; } // Read pattern properties uint32 patternFormat; if(FileReader chunk = chunks.GetChunk(DTMChunk::idPATT)) { m_nChannels = chunk.ReadUint16BE(); if(m_nChannels < 1 || m_nChannels > 32) { return false; } Patterns.ResizeArray(chunk.ReadUint16BE()); // Number of stored patterns, may be lower than highest pattern number patternFormat = chunk.ReadUint32BE(); if(patternFormat != DTM_PT_PATTERN_FORMAT && patternFormat != DTM_204_PATTERN_FORMAT && patternFormat != DTM_206_PATTERN_FORMAT) { return false; } } else { return false; } // Read global info if(FileReader chunk = chunks.GetChunk(DTMChunk::idSV19)) { chunk.Skip(2); // Ticks per quarter note, typically 24 uint32 fractionalTempo = chunk.ReadUint32BE(); m_nDefaultTempo = TEMPO(m_nDefaultTempo.GetInt() + fractionalTempo / 4294967296.0); uint16be panning[32]; chunk.ReadArray(panning); for(CHANNELINDEX chn = 0; chn < 32 && chn < GetNumChannels(); chn++) { // Panning is in range 0...180, 90 = center ChnSettings[chn].nPan = static_cast(128 + Util::muldivr(std::min(static_cast(panning[chn]), int(180)) - 90, 128, 90)); } chunk.Skip(16); // Chunk ends here for old DTM modules if(chunk.CanRead(2)) { m_nDefaultGlobalVolume = std::min(chunk.ReadUint16BE(), static_cast(MAX_GLOBAL_VOLUME)); } chunk.Skip(128); uint16be volume[32]; if(chunk.ReadArray(volume)) { for(CHANNELINDEX chn = 0; chn < 32 && chn < GetNumChannels(); chn++) { // Volume is in range 0...128, 64 = normal ChnSettings[chn].nVolume = static_cast(std::min(static_cast(volume[chn]), int(128)) / 2); } m_nSamplePreAmp *= 2; // Compensate for channel volume range } } // Read song message if(FileReader chunk = chunks.GetChunk(DTMChunk::idTEXT)) { DTMText text; chunk.ReadStruct(text); if(text.oddLength == 0xFFFF) { chunk.Skip(1); } m_songMessage.Read(chunk, chunk.BytesLeft(), SongMessage::leCRLF); } // Read sample headers if(FileReader chunk = chunks.GetChunk(DTMChunk::idINST)) { uint16 numSamples = chunk.ReadUint16BE(); bool newSamples = (numSamples >= 0x8000); numSamples &= 0x7FFF; if(numSamples >= MAX_SAMPLES || !chunk.CanRead(numSamples * (sizeof(DTMSample) + (newSamples ? 2u : 0u)))) { return false; } m_nSamples = numSamples; for(SAMPLEINDEX smp = 1; smp <= numSamples; smp++) { SAMPLEINDEX realSample = newSamples ? (chunk.ReadUint16BE() + 1u) : smp; DTMSample dtmSample; chunk.ReadStruct(dtmSample); if(realSample < 1 || realSample >= MAX_SAMPLES) { continue; } m_nSamples = std::max(m_nSamples, realSample); ModSample &mptSmp = Samples[realSample]; dtmSample.ConvertToMPT(mptSmp, fileHeader.forcedSampleRate, patternFormat); m_szNames[realSample] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, dtmSample.name); } if(chunk.ReadUint16BE() == 0x0004) { // Digital Home Studio instruments m_nInstruments = std::min(static_cast(m_nSamples), static_cast(MAX_INSTRUMENTS - 1)); FileReader envChunk = chunks.GetChunk(DTMChunk::idIENV); while(chunk.CanRead(sizeof(DTMInstrument))) { DTMInstrument instr; chunk.ReadStruct(instr); if(instr.insNum < GetNumInstruments()) { ModSample &sample = Samples[instr.insNum + 1]; sample.nVibDepth = instr.vibDepth; sample.nVibRate = instr.vibRate; sample.nVibSweep = 255; ModInstrument *mptIns = AllocateInstrument(instr.insNum + 1, instr.insNum + 1); if(mptIns != nullptr) { InstrumentEnvelope &mptEnv = mptIns->VolEnv; mptIns->nFadeOut = std::min(static_cast(instr.fadeout), uint16(0xFFF)); if(instr.envelope != 0xFF && envChunk.Seek(2 + sizeof(DTMEnvelope) * instr.envelope)) { DTMEnvelope env; envChunk.ReadStruct(env); mptEnv.dwFlags.set(ENV_ENABLED); mptEnv.resize(std::min({ static_cast(env.numPoints), std::size(env.points), static_cast(MAX_ENVPOINTS) })); for(size_t i = 0; i < mptEnv.size(); i++) { mptEnv[i].value = std::min(uint8(64), static_cast(env.points[i].value)); mptEnv[i].tick = env.points[i].tick; } if(instr.sustain != 0xFF) { mptEnv.dwFlags.set(ENV_SUSTAIN); mptEnv.nSustainStart = mptEnv.nSustainEnd = instr.sustain; } if(!mptEnv.empty()) { mptEnv.dwFlags.set(ENV_LOOP); mptEnv.nLoopStart = mptEnv.nLoopEnd = static_cast(mptEnv.size() - 1); } } } } } } } // Read pattern data for(auto &chunk : chunks.GetAllChunks(DTMChunk::idDAPT)) { chunk.Skip(4); // FF FF FF FF PATTERNINDEX patNum = chunk.ReadUint16BE(); ROWINDEX numRows = chunk.ReadUint16BE(); if(patternFormat == DTM_206_PATTERN_FORMAT) { // The stored data is actually not row-based, but tick-based. numRows /= m_nDefaultSpeed; } if(!(loadFlags & loadPatternData) || patNum > 255 || !Patterns.Insert(patNum, numRows)) { continue; } if(patternFormat == DTM_206_PATTERN_FORMAT) { chunk.Skip(4); for(CHANNELINDEX chn = 0; chn < GetNumChannels(); chn++) { uint16 length = chunk.ReadUint16BE(); if(length % 2u) length++; FileReader rowChunk = chunk.ReadChunk(length); int tick = 0; std::div_t position = { 0, 0 }; while(rowChunk.CanRead(6) && static_cast(position.quot) < numRows) { ModCommand *m = Patterns[patNum].GetpModCommand(position.quot, chn); const auto [note, volume, instr, command, param, delay] = rowChunk.ReadArray(); if(note > 0 && note <= 96) { m->note = note + NOTE_MIN + 12; if(position.rem) { m->command = CMD_MODCMDEX; m->param = 0xD0 | static_cast(std::min(position.rem, 15)); } } else if(note & 0x80) { // Lower 7 bits contain note, probably intended for MIDI-like note-on/note-off events if(position.rem) { m->command = CMD_MODCMDEX; m->param = 0xC0 | static_cast(std::min(position.rem, 15)); } else { m->note = NOTE_NOTECUT; } } if(volume) { m->volcmd = VOLCMD_VOLUME; m->vol = std::min(volume, uint8(64)); // Volume can go up to 255, but we do not support over-amplification at the moment. } if(instr) { m->instr = instr; } if(command || param) { m->command = command; m->param = param; ConvertModCommand(*m); #ifdef MODPLUG_TRACKER m->Convert(MOD_TYPE_MOD, MOD_TYPE_IT, *this); #endif // G is 8-bit volume // P is tremor (need to disable oldfx) } if(delay & 0x80) tick += (delay & 0x7F) * 0x100 + rowChunk.ReadUint8(); else tick += delay; position = std::div(tick, m_nDefaultSpeed); } } } else { ModCommand *m = Patterns[patNum].GetpModCommand(0, 0); for(ROWINDEX row = 0; row < numRows; row++) { for(CHANNELINDEX chn = 0; chn < GetNumChannels(); chn++, m++) { const auto data = chunk.ReadArray(); if(patternFormat == DTM_204_PATTERN_FORMAT) { const auto [note, instrVol, instrCmd, param] = data; if(note > 0 && note < 0x80) { m->note = (note >> 4) * 12 + (note & 0x0F) + NOTE_MIN + 11; } uint8 vol = instrVol >> 2; if(vol) { m->volcmd = VOLCMD_VOLUME; m->vol = vol - 1u; } m->instr = ((instrVol & 0x03) << 4) | (instrCmd >> 4); m->command = instrCmd & 0x0F; m->param = param; } else { ReadMODPatternEntry(data, *m); m->instr |= data[0] & 0x30; // Allow more than 31 instruments } ConvertModCommand(*m); // Fix commands without memory and slide nibble precedence switch(m->command) { case CMD_PORTAMENTOUP: case CMD_PORTAMENTODOWN: if(!m->param) { m->command = CMD_NONE; } break; case CMD_VOLUMESLIDE: case CMD_TONEPORTAVOL: case CMD_VIBRATOVOL: if(m->param & 0xF0) { m->param &= 0xF0; } else if(!m->param) { m->command = CMD_NONE; } break; default: break; } #ifdef MODPLUG_TRACKER m->Convert(MOD_TYPE_MOD, MOD_TYPE_IT, *this); #endif } } } } // Read pattern names if(FileReader chunk = chunks.GetChunk(DTMChunk::idPATN)) { PATTERNINDEX pat = 0; std::string name; while(chunk.CanRead(1) && pat < Patterns.Size()) { chunk.ReadNullString(name, 32); Patterns[pat].SetName(name); pat++; } } // Read channel names if(FileReader chunk = chunks.GetChunk(DTMChunk::idTRKN)) { CHANNELINDEX chn = 0; std::string name; while(chunk.CanRead(1) && chn < GetNumChannels()) { chunk.ReadNullString(name, 32); ChnSettings[chn].szName = name; chn++; } } // Read sample data for(auto &chunk : chunks.GetAllChunks(DTMChunk::idDAIT)) { SAMPLEINDEX smp = chunk.ReadUint16BE(); if(smp >= GetNumSamples() || !(loadFlags & loadSampleData)) { continue; } ModSample &mptSmp = Samples[smp + 1]; SampleIO( mptSmp.uFlags[CHN_16BIT] ? SampleIO::_16bit : SampleIO::_8bit, mptSmp.uFlags[CHN_STEREO] ? SampleIO::stereoInterleaved: SampleIO::mono, SampleIO::bigEndian, SampleIO::signedPCM).ReadSample(mptSmp, chunk); } // Is this accurate? mpt::ustring tracker; if(patternFormat == DTM_206_PATTERN_FORMAT) { tracker = U_("Digital Home Studio"); } else if(FileReader chunk = chunks.GetChunk(DTMChunk::idVERS)) { uint32 version = chunk.ReadUint32BE(); tracker = MPT_UFORMAT("Digital Tracker {}.{}")(version >> 4, version & 0x0F); } else { tracker = U_("Digital Tracker"); } m_modFormat.formatName = U_("Digital Tracker"); m_modFormat.type = U_("dtm"); m_modFormat.madeWithTracker = std::move(tracker); m_modFormat.charset = mpt::Charset::ISO8859_1; return true; } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Loaders.h0000644000175000017500000000641614052666041017650 00000000000000/* * Loaders.h * --------- * Purpose: Common functions for module loaders * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "../common/misc_util.h" #include "../common/FileReader.h" #include "Sndfile.h" #include "SampleIO.h" OPENMPT_NAMESPACE_BEGIN // Functions to create 4-byte and 2-byte magic byte identifiers in little-endian format // Use this together with uint32le/uint16le file members. constexpr uint32 MagicLE(const char(&id)[5]) { return static_cast((static_cast(id[3]) << 24) | (static_cast(id[2]) << 16) | (static_cast(id[1]) << 8) | static_cast(id[0])); } constexpr uint16 MagicLE(const char(&id)[3]) { return static_cast((static_cast(id[1]) << 8) | static_cast(id[0])); } // Functions to create 4-byte and 2-byte magic byte identifiers in big-endian format // Use this together with uint32be/uint16be file members. // Note: Historically, some magic bytes in MPT-specific fields are reversed (due to the use of multi-char literals). // Such fields turned up reversed in files, so MagicBE is used to keep them readable in the code. constexpr uint32 MagicBE(const char(&id)[5]) { return static_cast((static_cast(id[0]) << 24) | (static_cast(id[1]) << 16) | (static_cast(id[2]) << 8) | static_cast(id[3])); } constexpr uint16 MagicBE(const char(&id)[3]) { return static_cast((static_cast(id[0]) << 8) | static_cast(id[1])); } // Read 'howMany' order items from an array. // 'stopIndex' is treated as '---', 'ignoreIndex' is treated as '+++'. If the format doesn't support such indices, just pass uint16_max. template bool ReadOrderFromArray(ModSequence &order, const T(&orders)[arraySize], size_t howMany = arraySize, uint16 stopIndex = uint16_max, uint16 ignoreIndex = uint16_max) { static_assert(mpt::is_binary_safe::value); LimitMax(howMany, arraySize); LimitMax(howMany, MAX_ORDERS); ORDERINDEX readEntries = static_cast(howMany); order.resize(readEntries); for(int i = 0; i < readEntries; i++) { PATTERNINDEX pat = static_cast(orders[i]); if(pat == stopIndex) pat = order.GetInvalidPatIndex(); else if(pat == ignoreIndex) pat = order.GetIgnoreIndex(); order.at(i) = pat; } return true; } // Read 'howMany' order items as integers with defined endianness from a file. // 'stopIndex' is treated as '---', 'ignoreIndex' is treated as '+++'. If the format doesn't support such indices, just pass uint16_max. template bool ReadOrderFromFile(ModSequence &order, FileReader &file, size_t howMany, uint16 stopIndex = uint16_max, uint16 ignoreIndex = uint16_max) { static_assert(mpt::is_binary_safe::value); if(!file.CanRead(howMany * sizeof(T))) return false; LimitMax(howMany, MAX_ORDERS); ORDERINDEX readEntries = static_cast(howMany); order.resize(readEntries); T patF; for(auto &pat : order) { file.ReadStruct(patF); pat = static_cast(patF); if(pat == stopIndex) pat = order.GetInvalidPatIndex(); else if(pat == ignoreIndex) pat = order.GetIgnoreIndex(); } return true; } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Load_far.cpp0000644000175000017500000001707014043545744020325 00000000000000/* * Load_far.cpp * ------------ * Purpose: Farandole (FAR) module loader * Notes : (currently none) * Authors: OpenMPT Devs (partly inspired by Storlek's FAR loader from Schism Tracker) * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Loaders.h" OPENMPT_NAMESPACE_BEGIN // FAR File Header struct FARFileHeader { uint8le magic[4]; char songName[40]; uint8le eof[3]; uint16le headerLength; uint8le version; uint8le onOff[16]; uint8le editingState[9]; // Stuff we don't care about uint8le defaultSpeed; uint8le chnPanning[16]; uint8le patternState[4]; // More stuff we don't care about uint16le messageLength; }; MPT_BINARY_STRUCT(FARFileHeader, 98) struct FAROrderHeader { uint8le orders[256]; uint8le numPatterns; // supposed to be "number of patterns stored in the file"; apparently that's wrong uint8le numOrders; uint8le restartPos; uint16le patternSize[256]; }; MPT_BINARY_STRUCT(FAROrderHeader, 771) // FAR Sample header struct FARSampleHeader { // Sample flags enum SampleFlags { smp16Bit = 0x01, smpLoop = 0x08, }; char name[32]; uint32le length; uint8le finetune; uint8le volume; uint32le loopStart; uint32le loopEnd; uint8le type; uint8le loop; // Convert sample header to OpenMPT's internal format. void ConvertToMPT(ModSample &mptSmp) const { mptSmp.Initialize(); mptSmp.nLength = length; mptSmp.nLoopStart = loopStart; mptSmp.nLoopEnd = loopEnd; mptSmp.nC5Speed = 8363 * 2; mptSmp.nVolume = volume * 16; if(type & smp16Bit) { mptSmp.nLength /= 2; mptSmp.nLoopStart /= 2; mptSmp.nLoopEnd /= 2; } if((loop & 8) && mptSmp.nLoopEnd > mptSmp.nLoopStart) { mptSmp.uFlags.set(CHN_LOOP); } } // Retrieve the internal sample format flags for this sample. SampleIO GetSampleFormat() const { return SampleIO( (type & smp16Bit) ? SampleIO::_16bit : SampleIO::_8bit, SampleIO::mono, SampleIO::littleEndian, SampleIO::signedPCM); } }; MPT_BINARY_STRUCT(FARSampleHeader, 48) static bool ValidateHeader(const FARFileHeader &fileHeader) { if(std::memcmp(fileHeader.magic, "FAR\xFE", 4) != 0 || std::memcmp(fileHeader.eof, "\x0D\x0A\x1A", 3) ) { return false; } if(fileHeader.headerLength < sizeof(FARFileHeader)) { return false; } return true; } static uint64 GetHeaderMinimumAdditionalSize(const FARFileHeader &fileHeader) { return fileHeader.headerLength - sizeof(FARFileHeader); } CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderFAR(MemoryFileReader file, const uint64 *pfilesize) { FARFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return ProbeWantMoreData; } if(!ValidateHeader(fileHeader)) { return ProbeFailure; } return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); } bool CSoundFile::ReadFAR(FileReader &file, ModLoadingFlags loadFlags) { file.Rewind(); FARFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return false; } if(!ValidateHeader(fileHeader)) { return false; } if(!file.CanRead(mpt::saturate_cast(GetHeaderMinimumAdditionalSize(fileHeader)))) { return false; } if(loadFlags == onlyVerifyHeader) { return true; } // Globals InitializeGlobals(MOD_TYPE_FAR); m_nChannels = 16; m_nSamplePreAmp = 32; m_nDefaultSpeed = fileHeader.defaultSpeed; m_nDefaultTempo.Set(80); m_nDefaultGlobalVolume = MAX_GLOBAL_VOLUME; m_SongFlags = SONG_LINEARSLIDES; m_playBehaviour.set(kPeriodsAreHertz); m_modFormat.formatName = U_("Farandole Composer"); m_modFormat.type = U_("far"); m_modFormat.charset = mpt::Charset::CP437; m_songName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileHeader.songName); // Read channel settings for(CHANNELINDEX chn = 0; chn < 16; chn++) { ChnSettings[chn].Reset(); ChnSettings[chn].dwFlags = fileHeader.onOff[chn] ? ChannelFlags(0) : CHN_MUTE; ChnSettings[chn].nPan = ((fileHeader.chnPanning[chn] & 0x0F) << 4) + 8; } // Read song message if(fileHeader.messageLength != 0) { m_songMessage.ReadFixedLineLength(file, fileHeader.messageLength, 132, 0); // 132 characters per line... wow. :) } // Read orders FAROrderHeader orderHeader; if(!file.ReadStruct(orderHeader)) { return false; } ReadOrderFromArray(Order(), orderHeader.orders, orderHeader.numOrders, 0xFF, 0xFE); Order().SetRestartPos(orderHeader.restartPos); file.Seek(fileHeader.headerLength); // Pattern effect LUT static constexpr EffectCommand farEffects[] = { CMD_NONE, CMD_PORTAMENTOUP, CMD_PORTAMENTODOWN, CMD_TONEPORTAMENTO, CMD_RETRIG, CMD_VIBRATO, // depth CMD_VIBRATO, // speed CMD_VOLUMESLIDE, // up CMD_VOLUMESLIDE, // down CMD_VIBRATO, // sustained (?) CMD_NONE, // actually slide-to-volume CMD_S3MCMDEX, // panning CMD_S3MCMDEX, // note offset => note delay? CMD_NONE, // fine tempo down CMD_NONE, // fine tempo up CMD_SPEED, }; // Read patterns for(PATTERNINDEX pat = 0; pat < 256; pat++) { if(!orderHeader.patternSize[pat]) { continue; } FileReader patternChunk = file.ReadChunk(orderHeader.patternSize[pat]); // Calculate pattern length in rows (every event is 4 bytes, and we have 16 channels) ROWINDEX numRows = (orderHeader.patternSize[pat] - 2) / (16 * 4); if(!(loadFlags & loadPatternData) || !Patterns.Insert(pat, numRows)) { continue; } // Read break row and unused value (used to be pattern tempo) ROWINDEX breakRow = patternChunk.ReadUint8(); patternChunk.Skip(1); if(breakRow > 0 && breakRow < numRows - 2) { breakRow++; } else { breakRow = ROWINDEX_INVALID; } // Read pattern data for(ROWINDEX row = 0; row < numRows; row++) { PatternRow rowBase = Patterns[pat].GetRow(row); for(CHANNELINDEX chn = 0; chn < 16; chn++) { ModCommand &m = rowBase[chn]; const auto [note, instr, volume, effect] = patternChunk.ReadArray(); if(note > 0 && note <= 72) { m.note = note + 35 + NOTE_MIN; m.instr = instr + 1; } if(volume > 0 && volume <= 16) { m.volcmd = VOLCMD_VOLUME; m.vol = (volume - 1u) * 64u / 15u; } m.param = effect & 0x0F; switch(effect >> 4) { case 0x01: case 0x02: m.param |= 0xF0; break; case 0x03: // Porta to note (TODO: Parameter is number of rows the portamento should take) m.param <<= 2; break; case 0x04: // Retrig m.param = 6 / (1 + (m.param & 0xf)) + 1; // ugh? break; case 0x06: // Vibrato speed case 0x07: // Volume slide up m.param *= 8; break; case 0x0A: // Volume-portamento (what!) m.volcmd = VOLCMD_VOLUME; m.vol = (m.param << 2) + 4; break; case 0x0B: // Panning m.param |= 0x80; break; case 0x0C: // Note offset m.param = 6 / (1 + m.param) + 1; m.param |= 0x0D; } m.command = farEffects[effect >> 4]; } } Patterns[pat].WriteEffect(EffectWriter(CMD_PATTERNBREAK, 0).Row(breakRow).RetryNextRow()); } if(!(loadFlags & loadSampleData)) { return true; } // Read samples uint8 sampleMap[8]; // Sample usage bitset file.ReadArray(sampleMap); for(SAMPLEINDEX smp = 0; smp < 64; smp++) { if(!(sampleMap[smp >> 3] & (1 << (smp & 7)))) { continue; } FARSampleHeader sampleHeader; if(!file.ReadStruct(sampleHeader)) { return true; } m_nSamples = smp + 1; ModSample &sample = Samples[m_nSamples]; m_szNames[m_nSamples] = mpt::String::ReadBuf(mpt::String::nullTerminated, sampleHeader.name); sampleHeader.ConvertToMPT(sample); sampleHeader.GetSampleFormat().ReadSample(sample, file); } return true; } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Load_fmt.cpp0000644000175000017500000001217614025370210020325 00000000000000/* * Load_fmt.cpp * ------------ * Purpose: Davey W Taylor's FM Tracker module loader * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Loaders.h" OPENMPT_NAMESPACE_BEGIN struct FMTChannelSetting { char name[8]; char settings[11]; }; MPT_BINARY_STRUCT(FMTChannelSetting, 19) struct FMTFileHeader { char magic[11]; // Includes format version number for simplicity char trackerName[20]; char songName[32]; FMTChannelSetting channels[8]; uint8 lastRow; uint8 lastOrder; uint8 lastPattern; }; MPT_BINARY_STRUCT(FMTFileHeader, 218) static uint64 GetHeaderMinimumAdditionalSize(const FMTFileHeader &fileHeader) { // Order list + pattern delays, pattern mapping + at least one byte per channel return (fileHeader.lastOrder + 1u) * 2u + (fileHeader.lastPattern + 1u) * 9u; } static bool ValidateHeader(const FMTFileHeader &fileHeader) { if(memcmp(fileHeader.magic, "FMTracker\x01\x01", 11)) return false; for(const auto &channel : fileHeader.channels) { // Reject anything that resembles OPL3 if((channel.settings[8] & 0xFC) || (channel.settings[9] & 0xFC) || (channel.settings[10] & 0xF0)) return false; } return true; } CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderFMT(MemoryFileReader file, const uint64 *pfilesize) { FMTFileHeader fileHeader; if(!file.Read(fileHeader)) return ProbeWantMoreData; if(!ValidateHeader(fileHeader)) return ProbeFailure; return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); } bool CSoundFile::ReadFMT(FileReader &file, ModLoadingFlags loadFlags) { file.Rewind(); FMTFileHeader fileHeader; if(!file.Read(fileHeader)) return false; if(!ValidateHeader(fileHeader)) return false; if(!file.CanRead(mpt::saturate_cast(GetHeaderMinimumAdditionalSize(fileHeader)))) return false; if(loadFlags == onlyVerifyHeader) return true; InitializeGlobals(MOD_TYPE_S3M); InitializeChannels(); m_nChannels = 8; m_nSamples = 8; m_nDefaultTempo = TEMPO(45.5); // 18.2 Hz timer m_playBehaviour.set(kOPLNoteStopWith0Hz); m_SongFlags.set(SONG_IMPORTED); m_songName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileHeader.songName); for(CHANNELINDEX chn = 0; chn < 8; chn++) { const auto name = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileHeader.channels[chn].name); ChnSettings[chn].szName = name; ModSample &mptSmp = Samples[chn + 1]; mptSmp.Initialize(MOD_TYPE_S3M); OPLPatch patch{{}}; memcpy(patch.data(), fileHeader.channels[chn].settings, 11); mptSmp.SetAdlib(true, patch); mptSmp.nC5Speed = 8215; m_szNames[chn + 1] = name; } const ORDERINDEX numOrders = fileHeader.lastOrder + 1u; ReadOrderFromFile(Order(), file, numOrders); std::vector delays; file.ReadVector(delays, numOrders); for(uint8 delay : delays) { if(delay < 1 || delay > 8) return false; } m_nDefaultSpeed = delays[0]; const PATTERNINDEX numPatterns = fileHeader.lastPattern + 1u; const ROWINDEX numRows = fileHeader.lastRow + 1u; std::vector patternMap; file.ReadVector(patternMap, numPatterns); Patterns.ResizeArray(numPatterns); for(PATTERNINDEX pat = 0; pat < numPatterns; pat++) { if(!(loadFlags & loadPatternData) || !Patterns.Insert(patternMap[pat], numRows)) break; auto &pattern = Patterns[patternMap[pat]]; for(CHANNELINDEX chn = 0; chn < 8; chn++) { for(ROWINDEX row = 0; row < pattern.GetNumRows(); row++) { uint8 data = file.ReadUint8(); if(data & 0x80) { row += data & 0x7F; } else { ModCommand &m = *pattern.GetpModCommand(row, chn); if(data == 1) { m.note = NOTE_NOTECUT; } else if(data >= 2 && data <= 97) { m.note = data + NOTE_MIN + 11u; m.instr = static_cast(chn + 1u); } } } } } // Write song speed to patterns... due to a quirk in the original playback routine // (delays is applied before notes are triggered, not afterwards), a pattern's delay // already applies to the last row of the previous pattern. // In case you wonder if anyone would ever notice: My own songs written with this tracker // actively work around this issue and will sound wrong if tempo is changed on the first row. for(ORDERINDEX ord = 0; ord < numOrders; ord++) { if(!Order().IsValidPat(ord)) { if(PATTERNINDEX pat = Patterns.InsertAny(numRows); pat != PATTERNINDEX_INVALID) Order()[ord] = pat; else continue; } auto m = Patterns[Order()[ord]].end() - 1; auto delay = delays[(ord + 1u) % numOrders]; if(m->param == delay) continue; if(m->command == CMD_SPEED) { PATTERNINDEX newPat = Order().EnsureUnique(ord); if(newPat != PATTERNINDEX_INVALID) m = Patterns[newPat].end() - 1; } m->command = CMD_SPEED; m->param = delay; } m_modFormat.formatName = U_("FM Tracker"); m_modFormat.type = U_("fmt"); m_modFormat.madeWithTracker = mpt::ToUnicode(mpt::Charset::CP437, mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileHeader.trackerName)); m_modFormat.charset = mpt::Charset::CP437; return true; } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Load_gdm.cpp0000644000175000017500000003530614147260247020323 00000000000000/* * Load_gdm.cpp * ------------ * Purpose: GDM (BWSB Soundsystem) module loader * Notes : This code is partly based on zilym's original code / specs (which are utterly wrong :P). * Thanks to the MenTaLguY for gdm.txt and ajs for gdm2s3m and some hints. * * Hint 1: Most (all?) of the unsupported features were not supported in 2GDM / BWSB either. * Hint 2: Files will be played like their original formats would be played in MPT, so no * BWSB quirks including crashes and freezes are supported. :-P * Authors: Johannes Schultz * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Loaders.h" #include "mod_specifications.h" OPENMPT_NAMESPACE_BEGIN // GDM File Header struct GDMFileHeader { char magic[4]; // ID: 'GDM\xFE' char songTitle[32]; // Music's title char songMusician[32]; // Name of music's composer char dosEOF[3]; // 13, 10, 26 char magic2[4]; // ID: 'GMFS' uint8le formatMajorVer; // Format major version uint8le formatMinorVer; // Format minor version uint16le trackerID; // Composing Tracker ID code (00 = 2GDM) uint8le trackerMajorVer; // Tracker's major version uint8le trackerMinorVer; // Tracker's minor version uint8le panMap[32]; // 0-Left to 15-Right, 255-N/U uint8le masterVol; // Range: 0...64 uint8le tempo; // Initial music tempo (6) uint8le bpm; // Initial music BPM (125) uint16le originalFormat; // Original format ID: // 1-MOD, 2-MTM, 3-S3M, 4-669, 5-FAR, 6-ULT, 7-STM, 8-MED, 9-PSM // (versions of 2GDM prior to v1.15 won't set this correctly) // 2GDM v1.17 will only spit out 0-byte files when trying to convert a PSM16 file, // and fail outright when trying to convert a new PSM file. uint32le orderOffset; uint8le lastOrder; // Number of orders in module - 1 uint32le patternOffset; uint8le lastPattern; // Number of patterns in module - 1 uint32le sampleHeaderOffset; uint32le sampleDataOffset; uint8le lastSample; // Number of samples in module - 1 uint32le messageTextOffset; // Offset of song message uint32le messageTextLength; uint32le scrollyScriptOffset; // Offset of scrolly script (huh?) uint16le scrollyScriptLength; uint32le textGraphicOffset; // Offset of text graphic (huh?) uint16le textGraphicLength; }; MPT_BINARY_STRUCT(GDMFileHeader, 157) // GDM Sample Header struct GDMSampleHeader { enum SampleFlags { smpLoop = 0x01, smp16Bit = 0x02, // 16-Bit samples are not handled correctly by 2GDM (not implemented) smpVolume = 0x04, // Use default volume smpPanning = 0x08, smpLZW = 0x10, // LZW-compressed samples are not implemented in 2GDM smpStereo = 0x20, // Stereo samples are not handled correctly by 2GDM (not implemented) }; char name[32]; // sample's name char fileName[12]; // sample's filename uint8le emsHandle; // useless uint32le length; // length in bytes uint32le loopBegin; // loop start in samples uint32le loopEnd; // loop end in samples uint8le flags; // see SampleFlags uint16le c4Hertz; // frequency uint8le volume; // default volume uint8le panning; // default pan }; MPT_BINARY_STRUCT(GDMSampleHeader, 62) static constexpr MODTYPE gdmFormatOrigin[] = { MOD_TYPE_NONE, MOD_TYPE_MOD, MOD_TYPE_MTM, MOD_TYPE_S3M, MOD_TYPE_669, MOD_TYPE_FAR, MOD_TYPE_ULT, MOD_TYPE_STM, MOD_TYPE_MED, MOD_TYPE_PSM }; static constexpr mpt::uchar gdmFormatOriginType[][4] = { UL_(""), UL_("mod"), UL_("mtm"), UL_("s3m"), UL_("669"), UL_("far"), UL_("ult"), UL_("stm"), UL_("med"), UL_("psm") }; static constexpr const mpt::uchar * gdmFormatOriginFormat[] = { UL_(""), UL_("Generic MOD"), UL_("MultiTracker"), UL_("Scream Tracker 3"), UL_("Composer 669 / UNIS 669"), UL_("Farandole Composer"), UL_("UltraTracker"), UL_("Scream Tracker 2"), UL_("OctaMED"), UL_("Epic Megagames MASI") }; static bool ValidateHeader(const GDMFileHeader &fileHeader) { if(std::memcmp(fileHeader.magic, "GDM\xFE", 4) || fileHeader.dosEOF[0] != 13 || fileHeader.dosEOF[1] != 10 || fileHeader.dosEOF[2] != 26 || std::memcmp(fileHeader.magic2, "GMFS", 4) || fileHeader.formatMajorVer != 1 || fileHeader.formatMinorVer != 0 || fileHeader.originalFormat >= std::size(gdmFormatOrigin) || fileHeader.originalFormat == 0) { return false; } return true; } CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderGDM(MemoryFileReader file, const uint64 *pfilesize) { GDMFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return ProbeWantMoreData; } if(!ValidateHeader(fileHeader)) { return ProbeFailure; } MPT_UNREFERENCED_PARAMETER(pfilesize); return ProbeSuccess; } bool CSoundFile::ReadGDM(FileReader &file, ModLoadingFlags loadFlags) { file.Rewind(); GDMFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return false; } if(!ValidateHeader(fileHeader)) { return false; } if(loadFlags == onlyVerifyHeader) { return true; } InitializeGlobals(gdmFormatOrigin[fileHeader.originalFormat]); m_SongFlags.set(SONG_IMPORTED); m_modFormat.formatName = U_("General Digital Music"); m_modFormat.type = U_("gdm"); m_modFormat.madeWithTracker = MPT_UFORMAT("BWSB 2GDM {}.{}")(fileHeader.trackerMajorVer, fileHeader.formatMinorVer); m_modFormat.originalType = gdmFormatOriginType[fileHeader.originalFormat]; m_modFormat.originalFormatName = gdmFormatOriginFormat[fileHeader.originalFormat]; m_modFormat.charset = mpt::Charset::CP437; // Song name m_songName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileHeader.songTitle); // Artist name { std::string artist = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileHeader.songMusician); if(artist != "Unknown") { m_songArtist = mpt::ToUnicode(mpt::Charset::CP437, artist); } } // Read channel pan map... 0...15 = channel panning, 16 = surround channel, 255 = channel does not exist m_nChannels = 32; for(CHANNELINDEX i = 0; i < 32; i++) { ChnSettings[i].Reset(); if(fileHeader.panMap[i] < 16) { ChnSettings[i].nPan = static_cast(std::min((fileHeader.panMap[i] * 16) + 8, 256)); } else if(fileHeader.panMap[i] == 16) { ChnSettings[i].nPan = 128; ChnSettings[i].dwFlags = CHN_SURROUND; } else if(fileHeader.panMap[i] == 0xFF) { m_nChannels = i; break; } } if(m_nChannels < 1) { return false; } m_nDefaultGlobalVolume = std::min(fileHeader.masterVol * 4u, 256u); m_nDefaultSpeed = fileHeader.tempo; m_nDefaultTempo.Set(fileHeader.bpm); // Read orders if(file.Seek(fileHeader.orderOffset)) { ReadOrderFromFile(Order(), file, fileHeader.lastOrder + 1, 0xFF, 0xFE); } // Read samples if(!file.Seek(fileHeader.sampleHeaderOffset)) { return false; } m_nSamples = fileHeader.lastSample + 1; // Sample headers for(SAMPLEINDEX smp = 1; smp <= m_nSamples; smp++) { GDMSampleHeader gdmSample; if(!file.ReadStruct(gdmSample)) { break; } ModSample &sample = Samples[smp]; sample.Initialize(); m_szNames[smp] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, gdmSample.name); sample.filename = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, gdmSample.fileName); sample.nC5Speed = gdmSample.c4Hertz; if(UseFinetuneAndTranspose()) { // Use the same inaccurate table as 2GDM for translating back to finetune, as our own routines // give slightly different results for the provided sample rates that may result in transpose != 0. static constexpr uint16 rate2finetune[] = { 8363, 8424, 8485, 8547, 8608, 8671, 8734, 8797, 7894, 7951, 8009, 8067, 8125, 8184, 8244, 8303 }; for(uint8 i = 0; i < 16; i++) { if(sample.nC5Speed == rate2finetune[i]) { sample.nFineTune = MOD2XMFineTune(i); break; } } } sample.nGlobalVol = 64; // Not supported in this format sample.nLength = gdmSample.length; // in bytes // Sample format if(gdmSample.flags & GDMSampleHeader::smp16Bit) { sample.uFlags.set(CHN_16BIT); sample.nLength /= 2; } sample.nLoopStart = gdmSample.loopBegin; sample.nLoopEnd = gdmSample.loopEnd - 1; if(gdmSample.flags & GDMSampleHeader::smpLoop) sample.uFlags.set(CHN_LOOP); if((gdmSample.flags & GDMSampleHeader::smpVolume) && gdmSample.volume != 0xFF) sample.nVolume = std::min(static_cast(gdmSample.volume), uint8(64)) * 4; else sample.uFlags.set(SMP_NODEFAULTVOLUME); if(gdmSample.flags & GDMSampleHeader::smpPanning) { // Default panning is used sample.uFlags.set(CHN_PANNING); // 0...15, 16 = surround (not supported), 255 = no default panning sample.nPan = static_cast((gdmSample.panning > 15) ? 128 : std::min((gdmSample.panning * 16) + 8, 256)); sample.uFlags.set(CHN_SURROUND, gdmSample.panning == 16); } else { sample.nPan = 128; } } // Read sample data if((loadFlags & loadSampleData) && file.Seek(fileHeader.sampleDataOffset)) { for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++) { SampleIO( Samples[smp].uFlags[CHN_16BIT] ? SampleIO::_16bit : SampleIO::_8bit, SampleIO::mono, SampleIO::littleEndian, SampleIO::unsignedPCM) .ReadSample(Samples[smp], file); } } // Read patterns Patterns.ResizeArray(fileHeader.lastPattern + 1); const CModSpecifications &modSpecs = GetModSpecifications(GetBestSaveFormat()); bool onlyAmigaNotes = true; // We'll start at position patternsOffset and decode all patterns file.Seek(fileHeader.patternOffset); for(PATTERNINDEX pat = 0; pat <= fileHeader.lastPattern; pat++) { // Read pattern length *including* the two "length" bytes uint16 patternLength = file.ReadUint16LE(); if(patternLength <= 2) { // Huh, no pattern data present? continue; } FileReader chunk = file.ReadChunk(patternLength - 2); if(!(loadFlags & loadPatternData) || !chunk.IsValid() || !Patterns.Insert(pat, 64)) { continue; } enum { rowDone = 0x00, // Advance to next row channelMask = 0x1F, // Mask for retrieving channel information noteFlag = 0x20, // Note / instrument information present effectFlag = 0x40, // Effect information present effectMask = 0x1F, // Mask for retrieving effect command effectMore = 0x20, // Another effect follows }; for(ROWINDEX row = 0; row < 64; row++) { PatternRow rowBase = Patterns[pat].GetRow(row); uint8 channelByte; // If channel byte is zero, advance to next row. while((channelByte = chunk.ReadUint8()) != rowDone) { CHANNELINDEX channel = channelByte & channelMask; if(channel >= m_nChannels) break; // Better safe than sorry! ModCommand &m = rowBase[channel]; if(channelByte & noteFlag) { // Note and sample follows auto [note, instr] = chunk.ReadArray(); if(note) { note = (note & 0x7F) - 1; // High bit = no-retrig flag (notes with portamento have this set) m.note = (note & 0x0F) + 12 * (note >> 4) + 12 + NOTE_MIN; if(!m.IsAmigaNote()) { onlyAmigaNotes = false; } } m.instr = instr; } if(channelByte & effectFlag) { // Effect(s) follow(s) m.command = CMD_NONE; m.volcmd = VOLCMD_NONE; while(chunk.CanRead(2)) { // We may want to restore the old command in some cases. const ModCommand oldCmd = m; const auto [effByte, param] = chunk.ReadArray(); m.param = param; // Effect translation LUT static constexpr EffectCommand gdmEffTrans[] = { CMD_NONE, CMD_PORTAMENTOUP, CMD_PORTAMENTODOWN, CMD_TONEPORTAMENTO, CMD_VIBRATO, CMD_TONEPORTAVOL, CMD_VIBRATOVOL, CMD_TREMOLO, CMD_TREMOR, CMD_OFFSET, CMD_VOLUMESLIDE, CMD_POSITIONJUMP, CMD_VOLUME, CMD_PATTERNBREAK, CMD_MODCMDEX, CMD_SPEED, CMD_ARPEGGIO, CMD_NONE /* set internal flag */, CMD_RETRIG, CMD_GLOBALVOLUME, CMD_FINEVIBRATO, CMD_NONE, CMD_NONE, CMD_NONE, CMD_NONE, CMD_NONE, CMD_NONE, CMD_NONE, CMD_NONE, CMD_NONE, CMD_S3MCMDEX, CMD_TEMPO, }; // Translate effect uint8 command = effByte & effectMask; if(command < std::size(gdmEffTrans)) m.command = gdmEffTrans[command]; else m.command = CMD_NONE; // Fix some effects switch(m.command) { case CMD_PORTAMENTOUP: case CMD_PORTAMENTODOWN: if(m.param >= 0xE0 && m_nType != MOD_TYPE_MOD) m.param = 0xDF; // Don't spill into fine slide territory break; case CMD_TONEPORTAVOL: case CMD_VIBRATOVOL: if(m.param & 0xF0) m.param &= 0xF0; break; case CMD_VOLUME: m.param = std::min(m.param, uint8(64)); if(modSpecs.HasVolCommand(VOLCMD_VOLUME)) { m.volcmd = VOLCMD_VOLUME; m.vol = m.param; // Don't destroy old command, if there was one. m.command = oldCmd.command; m.param = oldCmd.param; } break; case CMD_MODCMDEX: switch(m.param >> 4) { case 0x8: m.command = CMD_PORTAMENTOUP; m.param = 0xE0 | (m.param & 0x0F); break; case 0x9: m.command = CMD_PORTAMENTODOWN; m.param = 0xE0 | (m.param & 0x0F); break; default: if(!modSpecs.HasCommand(CMD_MODCMDEX)) m.ExtendedMODtoS3MEffect(); break; } break; case CMD_RETRIG: if(!modSpecs.HasCommand(CMD_RETRIG) && modSpecs.HasCommand(CMD_MODCMDEX)) { // Retrig in "MOD style" m.command = CMD_MODCMDEX; m.param = 0x90 | (m.param & 0x0F); } break; case CMD_S3MCMDEX: // Some really special commands if(m.param == 0x01) { // Surround (implemented in 2GDM but not in BWSB itself) m.param = 0x91; } else if((m.param & 0xF0) == 0x80) { // 4-Bit Panning if (!modSpecs.HasCommand(CMD_S3MCMDEX)) m.command = CMD_MODCMDEX; } else { // All other effects are implemented neither in 2GDM nor in BWSB. m.command = CMD_NONE; } break; } // Move pannings to volume column - should never happen if(m.command == CMD_S3MCMDEX && ((m.param >> 4) == 0x8) && m.volcmd == VOLCMD_NONE) { m.volcmd = VOLCMD_PANNING; m.vol = ((m.param & 0x0F) * 64 + 8) / 15; m.command = oldCmd.command; m.param = oldCmd.param; } if(!(effByte & effectMore)) break; } } } } } m_SongFlags.set(SONG_AMIGALIMITS | SONG_ISAMIGA, GetType() == MOD_TYPE_MOD && GetNumChannels() == 4 && onlyAmigaNotes); // Read song comments if(fileHeader.messageTextLength > 0 && file.Seek(fileHeader.messageTextOffset)) { m_songMessage.Read(file, fileHeader.messageTextLength, SongMessage::leAutodetect); } return true; } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Load_imf.cpp0000644000175000017500000004270314151774633020332 00000000000000/* * Load_imf.cpp * ------------ * Purpose: IMF (Imago Orpheus) module loader * Notes : Reverb and Chorus are not supported. * Authors: Storlek (Original author - http://schismtracker.org/ - code ported with permission) * Johannes Schultz (OpenMPT Port, tweaks) * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Loaders.h" OPENMPT_NAMESPACE_BEGIN struct IMFChannel { char name[12]; // Channel name (ASCIIZ-String, max 11 chars) uint8 chorus; // Default chorus uint8 reverb; // Default reverb uint8 panning; // Pan positions 00-FF uint8 status; // Channel status: 0 = enabled, 1 = mute, 2 = disabled (ignore effects!) }; MPT_BINARY_STRUCT(IMFChannel, 16) struct IMFFileHeader { enum SongFlags { linearSlides = 0x01, }; char title[32]; // Songname (ASCIIZ-String, max. 31 chars) uint16le ordNum; // Number of orders saved uint16le patNum; // Number of patterns saved uint16le insNum; // Number of instruments saved uint16le flags; // See SongFlags uint8le unused1[8]; uint8le tempo; // Default tempo (Axx, 1...255) uint8le bpm; // Default beats per minute (BPM) (Txx, 32...255) uint8le master; // Default master volume (Vxx, 0...64) uint8le amp; // Amplification factor (mixing volume, 4...127) uint8le unused2[8]; char im10[4]; // 'IM10' IMFChannel channels[32]; // Channel settings }; MPT_BINARY_STRUCT(IMFFileHeader, 576) struct IMFEnvelope { enum EnvFlags { envEnabled = 0x01, envSustain = 0x02, envLoop = 0x04, }; uint8 points; // Number of envelope points uint8 sustain; // Envelope sustain point uint8 loopStart; // Envelope loop start point uint8 loopEnd; // Envelope loop end point uint8 flags; // See EnvFlags uint8 unused[3]; }; MPT_BINARY_STRUCT(IMFEnvelope, 8) struct IMFEnvNode { uint16le tick; uint16le value; }; MPT_BINARY_STRUCT(IMFEnvNode, 4) struct IMFInstrument { enum EnvTypes { volEnv = 0, panEnv = 1, filterEnv = 2, }; char name[32]; // Inst. name (ASCIIZ-String, max. 31 chars) uint8le map[120]; // Multisample settings uint8le unused[8]; IMFEnvNode nodes[3][16]; IMFEnvelope env[3]; uint16le fadeout; // Fadeout rate (0...0FFFH) uint16le smpNum; // Number of samples in instrument char ii10[4]; // 'II10' (not verified by Orpheus) void ConvertEnvelope(InstrumentEnvelope &mptEnv, EnvTypes e) const { const uint8 shift = (e == volEnv) ? 0 : 2; const uint8 mirror = (e == filterEnv) ? 0xFF : 0x00; mptEnv.dwFlags.set(ENV_ENABLED, (env[e].flags & 1) != 0); mptEnv.dwFlags.set(ENV_SUSTAIN, (env[e].flags & 2) != 0); mptEnv.dwFlags.set(ENV_LOOP, (env[e].flags & 4) != 0); mptEnv.resize(Clamp(env[e].points, uint8(2), uint8(16))); mptEnv.nLoopStart = env[e].loopStart; mptEnv.nLoopEnd = env[e].loopEnd; mptEnv.nSustainStart = mptEnv.nSustainEnd = env[e].sustain; uint16 minTick = 0; // minimum tick value for next node for(uint32 n = 0; n < mptEnv.size(); n++) { mptEnv[n].tick = minTick = std::max(minTick, nodes[e][n].tick.get()); minTick++; uint8 value = static_cast(nodes[e][n].value ^ mirror) >> shift; mptEnv[n].value = std::min(value, uint8(ENVELOPE_MAX)); } mptEnv.Convert(MOD_TYPE_XM, MOD_TYPE_IT); } // Convert an IMFInstrument to OpenMPT's internal instrument representation. void ConvertToMPT(ModInstrument &mptIns, SAMPLEINDEX firstSample) const { mptIns.name = mpt::String::ReadBuf(mpt::String::nullTerminated, name); if(smpNum) { for(size_t note = 0; note < std::min(std::size(map), std::size(mptIns.Keyboard) - 12u); note++) { mptIns.Keyboard[note + 12] = firstSample + map[note]; } } mptIns.nFadeOut = fadeout; mptIns.midiPWD = 1; // For CMD_FINETUNE ConvertEnvelope(mptIns.VolEnv, volEnv); ConvertEnvelope(mptIns.PanEnv, panEnv); ConvertEnvelope(mptIns.PitchEnv, filterEnv); if(mptIns.PitchEnv.dwFlags[ENV_ENABLED]) mptIns.PitchEnv.dwFlags.set(ENV_FILTER); // hack to get === to stop notes if(!mptIns.VolEnv.dwFlags[ENV_ENABLED] && !mptIns.nFadeOut) mptIns.nFadeOut = 32767; } }; MPT_BINARY_STRUCT(IMFInstrument, 384) struct IMFSample { enum SampleFlags { smpLoop = 0x01, smpPingPongLoop = 0x02, smp16Bit = 0x04, smpPanning = 0x08, }; char filename[13]; // Sample filename (12345678.ABC) */ uint8le unused1[3]; uint32le length; // Length (in bytes) uint32le loopStart; // Loop start (in bytes) uint32le loopEnd; // Loop end (in bytes) uint32le c5Speed; // Samplerate uint8le volume; // Default volume (0...64) uint8le panning; // Default pan (0...255) uint8le unused2[14]; uint8le flags; // Sample flags uint8le unused3[5]; uint16le ems; // Reserved for internal usage uint32le dram; // Reserved for internal usage char is10[4]; // 'IS10' // Convert an IMFSample to OpenMPT's internal sample representation. void ConvertToMPT(ModSample &mptSmp) const { mptSmp.Initialize(); mptSmp.filename = mpt::String::ReadBuf(mpt::String::nullTerminated, filename); mptSmp.nLength = length; mptSmp.nLoopStart = loopStart; mptSmp.nLoopEnd = loopEnd; mptSmp.nC5Speed = c5Speed; mptSmp.nVolume = volume * 4; mptSmp.nPan = panning; if(flags & smpLoop) mptSmp.uFlags.set(CHN_LOOP); if(flags & smpPingPongLoop) mptSmp.uFlags.set(CHN_PINGPONGLOOP); if(flags & smp16Bit) { mptSmp.uFlags.set(CHN_16BIT); mptSmp.nLength /= 2; mptSmp.nLoopStart /= 2; mptSmp.nLoopEnd /= 2; } if(flags & smpPanning) mptSmp.uFlags.set(CHN_PANNING); } }; MPT_BINARY_STRUCT(IMFSample, 64) static constexpr EffectCommand imfEffects[] = { CMD_NONE, CMD_SPEED, // 0x01 1xx Set Tempo CMD_TEMPO, // 0x02 2xx Set BPM CMD_TONEPORTAMENTO, // 0x03 3xx Tone Portamento CMD_TONEPORTAVOL, // 0x04 4xy Tone Portamento + Volume Slide CMD_VIBRATO, // 0x05 5xy Vibrato CMD_VIBRATOVOL, // 0x06 6xy Vibrato + Volume Slide CMD_FINEVIBRATO, // 0x07 7xy Fine Vibrato CMD_TREMOLO, // 0x08 8xy Tremolo CMD_ARPEGGIO, // 0x09 9xy Arpeggio CMD_PANNING8, // 0x0A Axx Set Pan Position CMD_PANNINGSLIDE, // 0x0B Bxy Pan Slide CMD_VOLUME, // 0x0C Cxx Set Volume CMD_VOLUMESLIDE, // 0x0D Dxy Volume Slide CMD_VOLUMESLIDE, // 0x0E Exy Fine Volume Slide CMD_FINETUNE, // 0x0F Fxx Set Finetune CMD_NOTESLIDEUP, // 0x10 Gxy Note Slide Up CMD_NOTESLIDEDOWN, // 0x11 Hxy Note Slide Down CMD_PORTAMENTOUP, // 0x12 Ixx Slide Up CMD_PORTAMENTODOWN, // 0x13 Jxx Slide Down CMD_PORTAMENTOUP, // 0x14 Kxx Fine Slide Up CMD_PORTAMENTODOWN, // 0x15 Lxx Fine Slide Down CMD_MIDI, // 0x16 Mxx Set Filter Cutoff CMD_MIDI, // 0x17 Nxy Filter Slide + Resonance CMD_OFFSET, // 0x18 Oxx Set Sample Offset CMD_NONE, // 0x19 Pxx Set Fine Sample Offset - XXX CMD_KEYOFF, // 0x1A Qxx Key Off CMD_RETRIG, // 0x1B Rxy Retrig CMD_TREMOR, // 0x1C Sxy Tremor CMD_POSITIONJUMP, // 0x1D Txx Position Jump CMD_PATTERNBREAK, // 0x1E Uxx Pattern Break CMD_GLOBALVOLUME, // 0x1F Vxx Set Mastervolume CMD_GLOBALVOLSLIDE, // 0x20 Wxy Mastervolume Slide CMD_S3MCMDEX, // 0x21 Xxx Extended Effect // X1x Set Filter // X3x Glissando // X5x Vibrato Waveform // X8x Tremolo Waveform // XAx Pattern Loop // XBx Pattern Delay // XCx Note Cut // XDx Note Delay // XEx Ignore Envelope // XFx Invert Loop CMD_NONE, // 0x22 Yxx Chorus - XXX CMD_NONE, // 0x23 Zxx Reverb - XXX }; static void ImportIMFEffect(ModCommand &m) { uint8 n; // fix some of them switch(m.command) { case 0xE: // fine volslide // hackaround to get almost-right behavior for fine slides (i think!) if(m.param == 0) /* nothing */; else if(m.param == 0xF0) m.param = 0xEF; else if(m.param == 0x0F) m.param = 0xFE; else if(m.param & 0xF0) m.param |= 0x0F; else m.param |= 0xF0; break; case 0xF: // set finetune m.param ^= 0x80; break; case 0x14: // fine slide up case 0x15: // fine slide down // this is about as close as we can do... if(m.param >> 4) m.param = 0xF0 | (m.param >> 4); else m.param |= 0xE0; break; case 0x16: // cutoff m.param = (0xFF - m.param) / 2u; break; case 0x17: // cutoff slide + resonance (TODO: cutoff slide is currently not handled) m.param = 0x80 | (m.param & 0x0F); break; case 0x1F: // set global volume m.param = mpt::saturate_cast(m.param * 2); break; case 0x21: n = 0; switch (m.param >> 4) { case 0: /* undefined, but since S0x does nothing in IT anyway, we won't care. this is here to allow S00 to pick up the previous value (assuming IMF even does that -- I haven't actually tried it) */ break; default: // undefined case 0x1: // set filter case 0xF: // invert loop m.command = CMD_NONE; break; case 0x3: // glissando n = 0x20; break; case 0x5: // vibrato waveform n = 0x30; break; case 0x8: // tremolo waveform n = 0x40; break; case 0xA: // pattern loop n = 0xB0; break; case 0xB: // pattern delay n = 0xE0; break; case 0xC: // note cut case 0xD: // note delay // Apparently, Imago Orpheus doesn't cut samples on tick 0. if(!m.param) m.command = CMD_NONE; break; case 0xE: // ignore envelope switch(m.param & 0x0F) { // All envelopes // Predicament: we can only disable one envelope at a time. Volume is probably most noticeable, so let's go with that. case 0: m.param = 0x77; break; // Volume case 1: m.param = 0x77; break; // Panning case 2: m.param = 0x79; break; // Filter case 3: m.param = 0x7B; break; } break; case 0x18: // sample offset // O00 doesn't pick up the previous value if(!m.param) m.command = CMD_NONE; break; } if(n) m.param = n | (m.param & 0x0F); break; } m.command = (m.command < std::size(imfEffects)) ? imfEffects[m.command] : CMD_NONE; if(m.command == CMD_VOLUME && m.volcmd == VOLCMD_NONE) { m.volcmd = VOLCMD_VOLUME; m.vol = m.param; m.command = CMD_NONE; m.param = 0; } } static bool ValidateHeader(const IMFFileHeader &fileHeader) { if(std::memcmp(fileHeader.im10, "IM10", 4) || fileHeader.ordNum > 256 || fileHeader.insNum >= MAX_INSTRUMENTS || fileHeader.bpm < 32 || fileHeader.master > 64 || fileHeader.amp < 4 || fileHeader.amp > 127) { return false; } bool channelFound = false; for(const auto &chn : fileHeader.channels) { switch(chn.status) { case 0: // enabled; don't worry about it channelFound = true; break; case 1: // mute channelFound = true; break; case 2: // disabled // nothing break; default: // uhhhh.... freak out return false; } } if(!channelFound) { return false; } return true; } static uint64 GetHeaderMinimumAdditionalSize(const IMFFileHeader &fileHeader) { return 256 + fileHeader.patNum * 4 + fileHeader.insNum * sizeof(IMFInstrument); } CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderIMF(MemoryFileReader file, const uint64 *pfilesize) { IMFFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return ProbeWantMoreData; } if(!ValidateHeader(fileHeader)) { return ProbeFailure; } return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); } bool CSoundFile::ReadIMF(FileReader &file, ModLoadingFlags loadFlags) { IMFFileHeader fileHeader; file.Rewind(); if(!file.ReadStruct(fileHeader)) { return false; } if(!ValidateHeader(fileHeader)) { return false; } if(!file.CanRead(mpt::saturate_cast(GetHeaderMinimumAdditionalSize(fileHeader)))) { return false; } if(loadFlags == onlyVerifyHeader) { return true; } // Read channel configuration std::bitset<32> ignoreChannels; // bit set for each channel that's completely disabled uint8 detectedChannels = 0; for(uint8 chn = 0; chn < 32; chn++) { ChnSettings[chn].Reset(); ChnSettings[chn].nPan = fileHeader.channels[chn].panning * 256 / 255; ChnSettings[chn].szName = mpt::String::ReadBuf(mpt::String::nullTerminated, fileHeader.channels[chn].name); // TODO: reverb/chorus? switch(fileHeader.channels[chn].status) { case 0: // enabled; don't worry about it detectedChannels = chn + 1; break; case 1: // mute ChnSettings[chn].dwFlags = CHN_MUTE; detectedChannels = chn + 1; break; case 2: // disabled ChnSettings[chn].dwFlags = CHN_MUTE; ignoreChannels[chn] = true; break; default: // uhhhh.... freak out return false; } } InitializeGlobals(MOD_TYPE_IMF); m_nChannels = detectedChannels; m_modFormat.formatName = U_("Imago Orpheus"); m_modFormat.type = U_("imf"); m_modFormat.charset = mpt::Charset::CP437; //From mikmod: work around an Orpheus bug if(fileHeader.channels[0].status == 0) { CHANNELINDEX chn; for(chn = 1; chn < 16; chn++) if(fileHeader.channels[chn].status != 1) break; if(chn == 16) for(chn = 1; chn < 16; chn++) ChnSettings[chn].dwFlags.reset(CHN_MUTE); } // Song Name m_songName = mpt::String::ReadBuf(mpt::String::nullTerminated, fileHeader.title); m_SongFlags.set(SONG_LINEARSLIDES, fileHeader.flags & IMFFileHeader::linearSlides); m_nDefaultSpeed = fileHeader.tempo; m_nDefaultTempo.Set(fileHeader.bpm); m_nDefaultGlobalVolume = fileHeader.master * 4u; m_nSamplePreAmp = fileHeader.amp; m_nInstruments = fileHeader.insNum; m_nSamples = 0; // Will be incremented later uint8 orders[256]; file.ReadArray(orders); ReadOrderFromArray(Order(), orders, fileHeader.ordNum, uint16_max, 0xFF); // Read patterns if(loadFlags & loadPatternData) Patterns.ResizeArray(fileHeader.patNum); for(PATTERNINDEX pat = 0; pat < fileHeader.patNum; pat++) { const uint16 length = file.ReadUint16LE(), numRows = file.ReadUint16LE(); FileReader patternChunk = file.ReadChunk(length - 4); if(!(loadFlags & loadPatternData) || !Patterns.Insert(pat, numRows)) { continue; } ModCommand dummy; ROWINDEX row = 0; while(row < numRows) { uint8 mask = patternChunk.ReadUint8(); if(mask == 0) { row++; continue; } uint8 channel = mask & 0x1F; ModCommand &m = (channel < GetNumChannels()) ? *Patterns[pat].GetpModCommand(row, channel) : dummy; if(mask & 0x20) { // Read note/instrument const auto [note, instr] = patternChunk.ReadArray(); m.note = note; m.instr = instr; if(m.note == 160) { m.note = NOTE_KEYOFF; } else if(m.note == 255) { m.note = NOTE_NONE; } else { m.note = (m.note >> 4) * 12 + (m.note & 0x0F) + 12 + 1; if(!m.IsNoteOrEmpty()) { m.note = NOTE_NONE; } } } if((mask & 0xC0) == 0xC0) { // Read both effects and figure out what to do with them const auto [e1c, e1d, e2c, e2d] = patternChunk.ReadArray(); // Command 1, Data 1, Command 2, Data 2 if(e1c == 0x0C) { m.vol = std::min(e1d, uint8(0x40)); m.volcmd = VOLCMD_VOLUME; m.command = e2c; m.param = e2d; } else if(e2c == 0x0C) { m.vol = std::min(e2d, uint8(0x40)); m.volcmd = VOLCMD_VOLUME; m.command = e1c; m.param = e1d; } else if(e1c == 0x0A) { m.vol = e1d * 64 / 255; m.volcmd = VOLCMD_PANNING; m.command = e2c; m.param = e2d; } else if(e2c == 0x0A) { m.vol = e2d * 64 / 255; m.volcmd = VOLCMD_PANNING; m.command = e1c; m.param = e1d; } else { /* check if one of the effects is a 'global' effect -- if so, put it in some unused channel instead. otherwise pick the most important effect. */ m.command = e2c; m.param = e2d; } } else if(mask & 0xC0) { // There's one effect, just stick it in the effect column const auto [command, param] = patternChunk.ReadArray(); m.command = command; m.param = param; } if(m.command) ImportIMFEffect(m); if(ignoreChannels[channel] && m.IsGlobalCommand()) m.command = CMD_NONE; } } SAMPLEINDEX firstSample = 1; // first sample index of the current instrument // read instruments for(INSTRUMENTINDEX ins = 0; ins < GetNumInstruments(); ins++) { ModInstrument *instr = AllocateInstrument(ins + 1); IMFInstrument instrumentHeader; if(!file.ReadStruct(instrumentHeader) || instr == nullptr) { continue; } // Orpheus does not check this! //if(memcmp(instrumentHeader.ii10, "II10", 4) != 0) // return false; instrumentHeader.ConvertToMPT(*instr, firstSample); // Read this instrument's samples for(SAMPLEINDEX smp = 0; smp < instrumentHeader.smpNum; smp++) { IMFSample sampleHeader; file.ReadStruct(sampleHeader); const SAMPLEINDEX smpID = firstSample + smp; if(memcmp(sampleHeader.is10, "IS10", 4) || smpID >= MAX_SAMPLES) { continue; } m_nSamples = smpID; ModSample &sample = Samples[smpID]; sampleHeader.ConvertToMPT(sample); m_szNames[smpID] = sample.filename; if(sampleHeader.length) { FileReader sampleChunk = file.ReadChunk(sampleHeader.length); if(loadFlags & loadSampleData) { SampleIO( sample.uFlags[CHN_16BIT] ? SampleIO::_16bit : SampleIO::_8bit, SampleIO::mono, SampleIO::littleEndian, SampleIO::signedPCM) .ReadSample(sample, sampleChunk); } } } firstSample += instrumentHeader.smpNum; } return true; } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Load_it.cpp0000644000175000017500000024215014175523206020163 00000000000000/* * Load_it.cpp * ----------- * Purpose: IT (Impulse Tracker) module loader / saver * Notes : Also handles MPTM loading / saving, as the formats are almost identical. * Authors: Olivier Lapicque * OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Loaders.h" #include "tuningcollection.h" #include "mod_specifications.h" #ifdef MODPLUG_TRACKER #include "../mptrack/Moddoc.h" #include "../mptrack/TrackerSettings.h" #endif // MODPLUG_TRACKER #ifdef MPT_EXTERNAL_SAMPLES #include "../common/mptPathString.h" #endif // MPT_EXTERNAL_SAMPLES #include "../common/serialization_utils.h" #ifndef MODPLUG_NO_FILESAVE #include "../common/mptFileIO.h" #endif // MODPLUG_NO_FILESAVE #include "plugins/PlugInterface.h" #include #include "../common/version.h" #include "ITTools.h" #include "mpt/io/base.hpp" #include "mpt/io/io.hpp" #include "mpt/io/io_stdstream.hpp" OPENMPT_NAMESPACE_BEGIN const uint16 verMptFileVer = 0x891; const uint16 verMptFileVerLoadLimit = 0x1000; // If cwtv-field is greater or equal to this value, // the MPTM file will not be loaded. /* MPTM version history for cwtv-field in "IT" header (only for MPTM files!): 0x890(1.18.02.00) -> 0x891(1.19.00.00): Pattern-specific time signatures Fixed behaviour of Pattern Loop command for rows > 255 (r617) 0x88F(1.18.01.00) -> 0x890(1.18.02.00): Removed volume command velocity :xy, added delay-cut command :xy. 0x88E(1.17.02.50) -> 0x88F(1.18.01.00): Numerous changes 0x88D(1.17.02.49) -> 0x88E(1.17.02.50): Changed ID to that of IT and undone the orderlist change done in 0x88A->0x88B. Now extended orderlist is saved as extension. 0x88C(1.17.02.48) -> 0x88D(1.17.02.49): Some tuning related changes - that part fails to read on older versions. 0x88B -> 0x88C: Changed type in which tuning number is printed to file: size_t -> uint16. 0x88A -> 0x88B: Changed order-to-pattern-index table type from uint8-array to vector. */ #ifndef MODPLUG_NO_FILESAVE static bool AreNonDefaultTuningsUsed(const CSoundFile& sf) { const INSTRUMENTINDEX numIns = sf.GetNumInstruments(); for(INSTRUMENTINDEX i = 1; i <= numIns; i++) { if(sf.Instruments[i] != nullptr && sf.Instruments[i]->pTuning != nullptr) return true; } return false; } static void WriteTuningCollection(std::ostream& oStrm, const CTuningCollection& tc) { tc.Serialize(oStrm, U_("Tune specific tunings")); } static void WriteTuningMap(std::ostream& oStrm, const CSoundFile& sf) { if(sf.GetNumInstruments() > 0) { //Writing instrument tuning data: first creating //tuning name <-> tuning id number map, //and then writing the tuning id for every instrument. //For example if there are 6 instruments and //first half use tuning 'T1', and the other half //tuning 'T2', the output would be something like //T1 1 T2 2 1 1 1 2 2 2 //Creating the tuning address <-> tuning id number map. std::map tNameToShort_Map; unsigned short figMap = 0; for(INSTRUMENTINDEX i = 1; i <= sf.GetNumInstruments(); i++) { CTuning *pTuning = nullptr; if(sf.Instruments[i] != nullptr) { pTuning = sf.Instruments[i]->pTuning; } auto iter = tNameToShort_Map.find(pTuning); if(iter != tNameToShort_Map.end()) continue; //Tuning already mapped. tNameToShort_Map[pTuning] = figMap; figMap++; } //...and write the map with tuning names replacing //the addresses. const uint16 tuningMapSize = static_cast(tNameToShort_Map.size()); mpt::IO::WriteIntLE(oStrm, tuningMapSize); for(auto &iter : tNameToShort_Map) { if(iter.first) mpt::IO::WriteSizedStringLE(oStrm, mpt::ToCharset(mpt::Charset::UTF8, iter.first->GetName())); else //Case: Using original IT tuning. mpt::IO::WriteSizedStringLE(oStrm, "->MPT_ORIGINAL_IT<-"); mpt::IO::WriteIntLE(oStrm, iter.second); } //Writing tuning data for instruments. for(INSTRUMENTINDEX i = 1; i <= sf.GetNumInstruments(); i++) { CTuning *pTuning = nullptr; if(sf.Instruments[i] != nullptr) { pTuning = sf.Instruments[i]->pTuning; } auto iter = tNameToShort_Map.find(pTuning); if(iter == tNameToShort_Map.end()) //Should never happen { sf.AddToLog(LogError, U_("Error: 210807_1")); return; } mpt::IO::WriteIntLE(oStrm, iter->second); } } } #endif // MODPLUG_NO_FILESAVE static void ReadTuningCollection(std::istream &iStrm, CTuningCollection &tc, const std::size_t dummy, mpt::Charset defaultCharset) { MPT_UNREFERENCED_PARAMETER(dummy); mpt::ustring name; tc.Deserialize(iStrm, name, defaultCharset); } template static bool ReadTuningMapTemplate(std::istream& iStrm, std::map &shortToTNameMap, mpt::Charset charset, const size_t maxNum = 500) { TUNNUMTYPE numTuning = 0; mpt::IO::ReadIntLE(iStrm, numTuning); if(numTuning > maxNum) return true; for(size_t i = 0; i < numTuning; i++) { std::string temp; uint16 ui = 0; if(!mpt::IO::ReadSizedStringLE(iStrm, temp, 255)) return true; mpt::IO::ReadIntLE(iStrm, ui); shortToTNameMap[ui] = mpt::ToUnicode(charset, temp); } if(iStrm.good()) return false; else return true; } static void ReadTuningMapImpl(std::istream& iStrm, CSoundFile& csf, mpt::Charset charset, const size_t = 0, bool old = false) { std::map shortToTNameMap; if(old) { ReadTuningMapTemplate(iStrm, shortToTNameMap, charset); } else { ReadTuningMapTemplate(iStrm, shortToTNameMap, charset); } // Read & set tunings for instruments std::vector notFoundTunings; for(INSTRUMENTINDEX i = 1; i<=csf.GetNumInstruments(); i++) { uint16 ui = 0; mpt::IO::ReadIntLE(iStrm, ui); auto iter = shortToTNameMap.find(ui); if(csf.Instruments[i] && iter != shortToTNameMap.end()) { const mpt::ustring str = iter->second; if(str == U_("->MPT_ORIGINAL_IT<-")) { csf.Instruments[i]->pTuning = nullptr; continue; } csf.Instruments[i]->pTuning = csf.GetTuneSpecificTunings().GetTuning(str); if(csf.Instruments[i]->pTuning) continue; #ifdef MODPLUG_TRACKER CTuning *localTuning = TrackerSettings::Instance().oldLocalTunings->GetTuning(str); if(localTuning) { std::unique_ptr pNewTuning = std::unique_ptr(new CTuning(*localTuning)); CTuning *pT = csf.GetTuneSpecificTunings().AddTuning(std::move(pNewTuning)); if(pT) { csf.AddToLog(LogInformation, U_("Local tunings are deprecated and no longer supported. Tuning '") + str + U_("' found in Local tunings has been copied to Tune-specific tunings and will be saved in the module file.")); csf.Instruments[i]->pTuning = pT; if(csf.GetpModDoc() != nullptr) { csf.GetpModDoc()->SetModified(); } continue; } else { csf.AddToLog(LogError, U_("Copying Local tuning '") + str + U_("' to Tune-specific tunings failed.")); } } #endif if(str == U_("12TET [[fs15 1.17.02.49]]") || str == U_("12TET")) { std::unique_ptr pNewTuning = csf.CreateTuning12TET(str); CTuning *pT = csf.GetTuneSpecificTunings().AddTuning(std::move(pNewTuning)); if(pT) { #ifdef MODPLUG_TRACKER csf.AddToLog(LogInformation, U_("Built-in tunings will no longer be used. Tuning '") + str + U_("' has been copied to Tune-specific tunings and will be saved in the module file.")); csf.Instruments[i]->pTuning = pT; if(csf.GetpModDoc() != nullptr) { csf.GetpModDoc()->SetModified(); } #endif continue; } else { #ifdef MODPLUG_TRACKER csf.AddToLog(LogError, U_("Copying Built-in tuning '") + str + U_("' to Tune-specific tunings failed.")); #endif } } // Checking if not found tuning already noticed. if(!mpt::contains(notFoundTunings, str)) { notFoundTunings.push_back(str); csf.AddToLog(LogWarning, U_("Tuning '") + str + U_("' used by the module was not found.")); #ifdef MODPLUG_TRACKER if(csf.GetpModDoc() != nullptr) { csf.GetpModDoc()->SetModified(); // The tuning is changed so the modified flag is set. } #endif // MODPLUG_TRACKER } csf.Instruments[i]->pTuning = csf.GetDefaultTuning(); } else { //This 'else' happens probably only in case of corrupted file. if(csf.Instruments[i]) csf.Instruments[i]->pTuning = csf.GetDefaultTuning(); } } //End read&set instrument tunings } static void ReadTuningMap(std::istream& iStrm, CSoundFile& csf, const size_t dummy, mpt::Charset charset) { ReadTuningMapImpl(iStrm, csf, charset, dummy, false); } ////////////////////////////////////////////////////////// // Impulse Tracker IT file support size_t CSoundFile::ITInstrToMPT(FileReader &file, ModInstrument &ins, uint16 trkvers) { if(trkvers < 0x0200) { // Load old format (IT 1.xx) instrument (early IT 2.xx modules may have cmwt set to 1.00 for backwards compatibility) ITOldInstrument instrumentHeader; if(!file.ReadStruct(instrumentHeader)) { return 0; } else { instrumentHeader.ConvertToMPT(ins); return sizeof(ITOldInstrument); } } else { const FileReader::off_t offset = file.GetPosition(); // Try loading extended instrument... instSize will differ between normal and extended instruments. ITInstrumentEx instrumentHeader; file.ReadStructPartial(instrumentHeader); size_t instSize = instrumentHeader.ConvertToMPT(ins, GetType()); file.Seek(offset + instSize); // Try reading modular instrument data. // Yes, it is completely idiotic that we have both this and LoadExtendedInstrumentProperties. // This is only required for files saved with *really* old OpenMPT versions (pre-1.17-RC1). // This chunk was also written in later versions (probably to maintain compatibility with // those ancient versions), but this also means that redundant information is stored in the file. // Starting from OpenMPT 1.25.02.07, this chunk is no longer written. if(file.ReadMagic("MSNI")) { //...the next piece of data must be the total size of the modular data FileReader modularData = file.ReadChunk(file.ReadUint32LE()); instSize += 8 + modularData.GetLength(); if(modularData.ReadMagic("GULP")) { ins.nMixPlug = modularData.ReadUint8(); if(ins.nMixPlug > MAX_MIXPLUGINS) ins.nMixPlug = 0; } } return instSize; } } static void CopyPatternName(CPattern &pattern, FileReader &file) { char name[MAX_PATTERNNAME] = ""; file.ReadString(name, MAX_PATTERNNAME); pattern.SetName(name); } // Get version of Schism Tracker that was used to create an IT/S3M file. mpt::ustring CSoundFile::GetSchismTrackerVersion(uint16 cwtv, uint32 reserved) { // Schism Tracker version information in a nutshell: // < 0x020: a proper version (files saved by such versions are likely very rare) // = 0x020: any version between the 0.2a release (2005-04-29?) and 2007-04-17 // = 0x050: anywhere from 2007-04-17 to 2009-10-31 // > 0x050: the number of days since 2009-10-31 // = 0xFFF: any version starting from 2020-10-28 (exact version stored in reserved value) cwtv &= 0xFFF; if(cwtv > 0x050) { int32 date = SchismTrackerEpoch + (cwtv < 0xFFF ? cwtv - 0x050 : reserved); int32 y = static_cast((Util::mul32to64(10000, date) + 14780) / 3652425); int32 ddd = date - (365 * y + y / 4 - y / 100 + y / 400); if(ddd < 0) { y--; ddd = date - (365 * y + y / 4 - y / 100 + y / 400); } int32 mi = (100 * ddd + 52) / 3060; return MPT_UFORMAT("Schism Tracker {}-{}-{}")( mpt::ufmt::dec0<4>(y + (mi + 2) / 12), mpt::ufmt::dec0<2>((mi + 2) % 12 + 1), mpt::ufmt::dec0<2>(ddd - (mi * 306 + 5) / 10 + 1)); } else { return MPT_UFORMAT("Schism Tracker 0.{}")(mpt::ufmt::hex0<2>(cwtv)); } } static bool ValidateHeader(const ITFileHeader &fileHeader) { if((std::memcmp(fileHeader.id, "IMPM", 4) && std::memcmp(fileHeader.id, "tpm.", 4)) || fileHeader.insnum > 0xFF || fileHeader.smpnum >= MAX_SAMPLES ) { return false; } return true; } static uint64 GetHeaderMinimumAdditionalSize(const ITFileHeader &fileHeader) { return fileHeader.ordnum + (fileHeader.insnum + fileHeader.smpnum + fileHeader.patnum) * 4; } CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderIT(MemoryFileReader file, const uint64 *pfilesize) { ITFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return ProbeWantMoreData; } if(!ValidateHeader(fileHeader)) { return ProbeFailure; } return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); } bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) { file.Rewind(); ITFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return false; } if(!ValidateHeader(fileHeader)) { return false; } if(!file.CanRead(mpt::saturate_cast(GetHeaderMinimumAdditionalSize(fileHeader)))) { return false; } if(loadFlags == onlyVerifyHeader) { return true; } InitializeGlobals(MOD_TYPE_IT); bool interpretModPlugMade = false; mpt::ustring madeWithTracker; // OpenMPT crap at the end of file size_t mptStartPos = 0; if(!memcmp(fileHeader.id, "tpm.", 4)) { // Legacy MPTM files (old 1.17.02.4x releases) SetType(MOD_TYPE_MPT); file.Seek(file.GetLength() - 4); mptStartPos = file.ReadUint32LE(); } else { if(fileHeader.cwtv > 0x888 && fileHeader.cwtv <= 0xFFF) { file.Seek(file.GetLength() - 4); mptStartPos = file.ReadUint32LE(); if(mptStartPos >= 0x100 && mptStartPos < file.GetLength()) { if(file.Seek(mptStartPos) && file.ReadMagic("228")) { SetType(MOD_TYPE_MPT); if(fileHeader.cwtv >= verMptFileVerLoadLimit) { AddToLog(LogError, U_("The file informed that it is incompatible with this version of OpenMPT. Loading was terminated.")); return false; } else if(fileHeader.cwtv > verMptFileVer) { AddToLog(LogInformation, U_("The loaded file was made with a more recent OpenMPT version and this version may not be able to load all the features or play the file correctly.")); } } } } if(GetType() == MOD_TYPE_IT) { // Which tracker was used to make this? if((fileHeader.cwtv & 0xF000) == 0x5000) { // OpenMPT Version number (Major.Minor) // This will only be interpreted as "made with ModPlug" (i.e. disable compatible playback etc) if the "reserved" field is set to "OMPT" - else, compatibility was used. uint32 mptVersion = (fileHeader.cwtv & 0x0FFF) << 16; if(!memcmp(&fileHeader.reserved, "OMPT", 4)) interpretModPlugMade = true; else if(mptVersion >= 0x01'29'00'00) mptVersion |= fileHeader.reserved & 0xFFFF; m_dwLastSavedWithVersion = Version(mptVersion); } else if(fileHeader.cmwt == 0x888 || fileHeader.cwtv == 0x888) { // OpenMPT 1.17.02.26 (r122) to 1.18 (raped IT format) // Exact version number will be determined later. interpretModPlugMade = true; m_dwLastSavedWithVersion = MPT_V("1.17.00.00"); } else if(fileHeader.cwtv == 0x0217 && fileHeader.cmwt == 0x0200 && fileHeader.reserved == 0) { if(memchr(fileHeader.chnpan, 0xFF, sizeof(fileHeader.chnpan)) != nullptr) { // ModPlug Tracker 1.16 (semi-raped IT format) or BeRoTracker (will be determined later) m_dwLastSavedWithVersion = MPT_V("1.16.00.00"); madeWithTracker = U_("ModPlug Tracker 1.09 - 1.16"); } else { // OpenMPT 1.17 disguised as this in compatible mode, // but never writes 0xFF in the pan map for unused channels (which is an invalid value). m_dwLastSavedWithVersion = MPT_V("1.17.00.00"); madeWithTracker = U_("OpenMPT 1.17 (compatibility export)"); } interpretModPlugMade = true; } else if(fileHeader.cwtv == 0x0214 && fileHeader.cmwt == 0x0202 && fileHeader.reserved == 0) { // ModPlug Tracker b3.3 - 1.09, instruments 557 bytes apart m_dwLastSavedWithVersion = MPT_V("1.09.00.00"); madeWithTracker = U_("ModPlug Tracker b3.3 - 1.09"); interpretModPlugMade = true; } else if(fileHeader.cwtv == 0x0300 && fileHeader.cmwt == 0x0300 && fileHeader.reserved == 0 && fileHeader.ordnum == 256 && fileHeader.sep == 128 && fileHeader.pwd == 0) { // A rare variant used from OpenMPT 1.17.02.20 (r113) to 1.17.02.25 (r121), found e.g. in xTr1m-SD.it m_dwLastSavedWithVersion = MPT_V("1.17.02.20"); interpretModPlugMade = true; } } } m_SongFlags.set(SONG_LINEARSLIDES, (fileHeader.flags & ITFileHeader::linearSlides) != 0); m_SongFlags.set(SONG_ITOLDEFFECTS, (fileHeader.flags & ITFileHeader::itOldEffects) != 0); m_SongFlags.set(SONG_ITCOMPATGXX, (fileHeader.flags & ITFileHeader::itCompatGxx) != 0); m_SongFlags.set(SONG_EXFILTERRANGE, (fileHeader.flags & ITFileHeader::extendedFilterRange) != 0); m_songName = mpt::String::ReadBuf(mpt::String::spacePadded, fileHeader.songname); // Read row highlights if((fileHeader.special & ITFileHeader::embedPatternHighlights)) { // MPT 1.09 and older (and maybe also newer) versions leave this blank (0/0), but have the "special" flag set. // Newer versions of MPT and OpenMPT 1.17 *always* write 4/16 here. // Thus, we will just ignore those old versions. // Note: OpenMPT 1.17.03.02 was the first version to properly make use of the time signature in the IT header. // This poses a small unsolvable problem: // - In compatible mode, we cannot distinguish this version from earlier 1.17 releases. // Thus we cannot know when to read this field or not (m_dwLastSavedWithVersion will always be 1.17.00.00). // Luckily OpenMPT 1.17.03.02 should not be very wide-spread. // - In normal mode the time signature is always present in the song extensions anyway. So it's okay if we read // the signature here and maybe overwrite it later when parsing the song extensions. if(!m_dwLastSavedWithVersion || m_dwLastSavedWithVersion >= MPT_V("1.17.03.02")) { m_nDefaultRowsPerBeat = fileHeader.highlight_minor; m_nDefaultRowsPerMeasure = fileHeader.highlight_major; } } // Global Volume m_nDefaultGlobalVolume = fileHeader.globalvol << 1; if(m_nDefaultGlobalVolume > MAX_GLOBAL_VOLUME) m_nDefaultGlobalVolume = MAX_GLOBAL_VOLUME; if(fileHeader.speed) m_nDefaultSpeed = fileHeader.speed; m_nDefaultTempo.Set(std::max(uint8(31), static_cast(fileHeader.tempo))); m_nSamplePreAmp = std::min(static_cast(fileHeader.mv), uint8(128)); // Reading Channels Pan Positions for(CHANNELINDEX i = 0; i < 64; i++) if(fileHeader.chnpan[i] != 0xFF) { ChnSettings[i].Reset(); ChnSettings[i].nVolume = Clamp(fileHeader.chnvol[i], 0, 64); if(fileHeader.chnpan[i] & 0x80) ChnSettings[i].dwFlags.set(CHN_MUTE); uint8 n = fileHeader.chnpan[i] & 0x7F; if(n <= 64) ChnSettings[i].nPan = n * 4; if(n == 100) ChnSettings[i].dwFlags.set(CHN_SURROUND); } // Reading orders file.Seek(sizeof(ITFileHeader)); if(GetType() == MOD_TYPE_MPT && fileHeader.cwtv > 0x88A && fileHeader.cwtv <= 0x88D) { // Deprecated format used for MPTm files created with OpenMPT 1.17.02.46 - 1.17.02.48. uint16 version = file.ReadUint16LE(); if(version != 0) return false; uint32 numOrd = file.ReadUint32LE(); if(numOrd > ModSpecs::mptm.ordersMax || !ReadOrderFromFile(Order(), file, numOrd)) return false; } else { ReadOrderFromFile(Order(), file, fileHeader.ordnum, 0xFF, 0xFE); } // Reading instrument, sample and pattern offsets std::vector insPos, smpPos, patPos; if(!file.ReadVector(insPos, fileHeader.insnum) || !file.ReadVector(smpPos, fileHeader.smpnum) || !file.ReadVector(patPos, fileHeader.patnum)) { return false; } // Find the first parapointer. // This is used for finding out whether the edit history is actually stored in the file or not, // as some early versions of Schism Tracker set the history flag, but didn't save anything. // We will consider the history invalid if it ends after the first parapointer. uint32 minPtr = std::numeric_limits::max(); for(uint32 pos : insPos) { if(pos > 0 && pos < minPtr) minPtr = pos; } for(uint32 pos : smpPos) { if(pos > 0 && pos < minPtr) minPtr = pos; } for(uint32 pos : patPos) { if(pos > 0 && pos < minPtr) minPtr = pos; } if(fileHeader.special & ITFileHeader::embedSongMessage) { minPtr = std::min(minPtr, fileHeader.msgoffset.get()); } const bool possiblyUNMO3 = fileHeader.cmwt == 0x0214 && (fileHeader.cwtv == 0x0214 || fileHeader.cwtv == 0) && fileHeader.highlight_major == 0 && fileHeader.highlight_minor == 0 && fileHeader.pwd == 0 && fileHeader.reserved == 0 && (fileHeader.flags & (ITFileHeader::useMIDIPitchController | ITFileHeader::reqEmbeddedMIDIConfig)) == 0; if(possiblyUNMO3 && fileHeader.insnum == 0 && fileHeader.smpnum > 0 && file.GetPosition() + 4 * smpPos.size() + 2 <= minPtr) { // UNMO3 < v2.4.0.1 reserves some space for instrument parapointers even in sample mode. // This makes reading MIDI macros and plugin information impossible. // Note: While UNMO3 and CheeseTracker header fingerprints are almost identical, we cannot mis-detect CheeseTracker here, // as it always sets the instrument mode flag and writes non-zero row highlights. bool oldUNMO3 = true; for(uint16 i = 0; i < fileHeader.smpnum; i++) { if(file.ReadUint32LE() != 0) { oldUNMO3 = false; file.SkipBack(4 + i * 4); break; } } if(oldUNMO3) { madeWithTracker = U_("UNMO3 <= 2.4"); } } if(possiblyUNMO3 && fileHeader.cwtv == 0) { madeWithTracker = U_("UNMO3 v0/1"); } // Reading IT Edit History Info // This is only supposed to be present if bit 1 of the special flags is set. // However, old versions of Schism and probably other trackers always set this bit // even if they don't write the edit history count. So we have to filter this out... // This is done by looking at the parapointers. If the history data ends after // the first parapointer, we assume that it's actually no history data. if(fileHeader.special & ITFileHeader::embedEditHistory) { const uint16 nflt = file.ReadUint16LE(); if(file.CanRead(nflt * sizeof(ITHistoryStruct)) && file.GetPosition() + nflt * sizeof(ITHistoryStruct) <= minPtr) { m_FileHistory.resize(nflt); for(auto &mptHistory : m_FileHistory) { ITHistoryStruct itHistory; file.ReadStruct(itHistory); itHistory.ConvertToMPT(mptHistory); } if(possiblyUNMO3 && nflt == 0) { if(fileHeader.special & ITFileHeader::embedPatternHighlights) madeWithTracker = U_("UNMO3 <= 2.4.0.1"); // Set together with MIDI macro embed flag else madeWithTracker = U_("UNMO3"); // Either 2.4.0.2+ or no MIDI macros embedded } } else { // Oops, we were not supposed to read this. file.SkipBack(2); } } else if(possiblyUNMO3 && fileHeader.special <= 1) { // UNMO3 < v2.4.0.1 will set the edit history special bit iff the MIDI macro embed bit is also set, // but it always writes the two extra bytes for the edit history length (zeroes). // If MIDI macros are embedded, we are fine and end up in the first case of the if statement (read edit history). // Otherwise we end up here and might have to read the edit history length. if(file.ReadUint16LE() == 0) { madeWithTracker = U_("UNMO3 <= 2.4"); } else { // These were not zero bytes, but potentially belong to the upcoming MIDI config - need to skip back. // I think the only application that could end up here is CheeseTracker, if it allows to write 0 for both row highlight values. // IT 2.14 itself will always write the edit history. file.SkipBack(2); } } // Reading MIDI Output & Macros bool hasMidiConfig = (fileHeader.flags & ITFileHeader::reqEmbeddedMIDIConfig) || (fileHeader.special & ITFileHeader::embedMIDIConfiguration); if(hasMidiConfig && file.ReadStruct(m_MidiCfg)) { m_MidiCfg.Sanitize(); } // Ignore MIDI data. Fixes some files like denonde.it that were made with old versions of Impulse Tracker (which didn't support Zxx filters) and have Zxx effects in the patterns. if(fileHeader.cwtv < 0x0214) { m_MidiCfg.ClearZxxMacros(); } // Read pattern names: "PNAM" FileReader patNames; if(file.ReadMagic("PNAM")) { patNames = file.ReadChunk(file.ReadUint32LE()); } m_nChannels = 1; // Read channel names: "CNAM" if(file.ReadMagic("CNAM")) { FileReader chnNames = file.ReadChunk(file.ReadUint32LE()); const CHANNELINDEX readChns = std::min(MAX_BASECHANNELS, static_cast(chnNames.GetLength() / MAX_CHANNELNAME)); m_nChannels = readChns; for(CHANNELINDEX i = 0; i < readChns; i++) { chnNames.ReadString(ChnSettings[i].szName, MAX_CHANNELNAME); } } // Read mix plugins information FileReader pluginChunk = file.ReadChunk((minPtr >= file.GetPosition()) ? minPtr - file.GetPosition() : file.BytesLeft()); const bool isBeRoTracker = LoadMixPlugins(pluginChunk); // Read Song Message if((fileHeader.special & ITFileHeader::embedSongMessage) && fileHeader.msglength > 0 && file.Seek(fileHeader.msgoffset)) { // Generally, IT files should use CR for line endings. However, ChibiTracker uses LF. One could do... // if(itHeader.cwtv == 0x0214 && itHeader.cmwt == 0x0214 && itHeader.reserved == ITFileHeader::chibiMagic) --> Chibi detected. // But we'll just use autodetection here: m_songMessage.Read(file, fileHeader.msglength, SongMessage::leAutodetect); } // Reading Instruments m_nInstruments = 0; if(fileHeader.flags & ITFileHeader::instrumentMode) { m_nInstruments = std::min(static_cast(fileHeader.insnum), static_cast(MAX_INSTRUMENTS - 1)); } for(INSTRUMENTINDEX i = 0; i < GetNumInstruments(); i++) { if(insPos[i] > 0 && file.Seek(insPos[i]) && file.CanRead(fileHeader.cmwt < 0x200 ? sizeof(ITOldInstrument) : sizeof(ITInstrument))) { ModInstrument *instrument = AllocateInstrument(i + 1); if(instrument != nullptr) { ITInstrToMPT(file, *instrument, fileHeader.cmwt); // MIDI Pitch Wheel Depth is a global setting in IT. Apply it to all instruments. instrument->midiPWD = fileHeader.pwd; } } } // In order to properly compute the position, in file, of eventual extended settings // such as "attack" we need to keep the "real" size of the last sample as those extra // setting will follow this sample in the file FileReader::off_t lastSampleOffset = 0; if(fileHeader.smpnum > 0) { lastSampleOffset = smpPos[fileHeader.smpnum - 1] + sizeof(ITSample); } bool possibleXMconversion = false; // Reading Samples m_nSamples = std::min(static_cast(fileHeader.smpnum), static_cast(MAX_SAMPLES - 1)); bool lastSampleCompressed = false; for(SAMPLEINDEX i = 0; i < GetNumSamples(); i++) { ITSample sampleHeader; if(smpPos[i] > 0 && file.Seek(smpPos[i]) && file.ReadStruct(sampleHeader)) { // IT does not check for the IMPS magic, and some bad XM->IT converter out there doesn't write the magic bytes for empty sample slots. ModSample &sample = Samples[i + 1]; size_t sampleOffset = sampleHeader.ConvertToMPT(sample); m_szNames[i + 1] = mpt::String::ReadBuf(mpt::String::spacePadded, sampleHeader.name); if(!file.Seek(sampleOffset)) continue; lastSampleCompressed = false; if(sample.uFlags[CHN_ADLIB]) { // FM instrument in MPTM OPLPatch patch; if(file.ReadArray(patch)) { sample.SetAdlib(true, patch); } } else if(!sample.uFlags[SMP_KEEPONDISK]) { SampleIO sampleIO = sampleHeader.GetSampleFormat(fileHeader.cwtv); if(loadFlags & loadSampleData) { sampleIO.ReadSample(sample, file); } else { if(sampleIO.IsVariableLengthEncoded()) lastSampleCompressed = true; else file.Skip(sampleIO.CalculateEncodedSize(sample.nLength)); } if(sampleIO.GetEncoding() == SampleIO::unsignedPCM && sample.nLength != 0) { // There is some XM to IT converter (don't know which one) and it identifies as IT 2.04. // The only safe way to distinguish it from an IT-saved file are the unsigned samples. possibleXMconversion = true; } } else { // External sample in MPTM file size_t strLen; file.ReadVarInt(strLen); if((loadFlags & loadSampleData) && strLen) { std::string filenameU8; file.ReadString(filenameU8, strLen); #if defined(MPT_EXTERNAL_SAMPLES) SetSamplePath(i + 1, mpt::PathString::FromUTF8(filenameU8)); #elif !defined(LIBOPENMPT_BUILD_TEST) AddToLog(LogWarning, MPT_UFORMAT("Loading external sample {} ('{}') failed: External samples are not supported.")(i + 1, mpt::ToUnicode(mpt::Charset::UTF8, filenameU8))); #endif // MPT_EXTERNAL_SAMPLES } else { file.Skip(strLen); } } lastSampleOffset = std::max(lastSampleOffset, file.GetPosition()); } } m_nSamples = std::max(SAMPLEINDEX(1), GetNumSamples()); if(possibleXMconversion && fileHeader.cwtv == 0x0204 && fileHeader.cmwt == 0x0200 && fileHeader.special == 0 && fileHeader.reserved == 0 && (fileHeader.flags & ~ITFileHeader::linearSlides) == (ITFileHeader::useStereoPlayback | ITFileHeader::instrumentMode | ITFileHeader::itOldEffects) && fileHeader.globalvol == 128 && fileHeader.mv == 48 && fileHeader.sep == 128 && fileHeader.pwd == 0 && fileHeader.msglength == 0) { for(uint8 pan : fileHeader.chnpan) { if(pan != 0x20 && pan != 0xA0) possibleXMconversion = false; } for(uint8 vol : fileHeader.chnvol) { if(vol != 0x40) possibleXMconversion = false; } for(size_t i = 20; i < std::size(fileHeader.songname); i++) { if(fileHeader.songname[i] != 0) possibleXMconversion = false; } if(possibleXMconversion) madeWithTracker = U_("XM Conversion"); } m_nMinPeriod = 0; m_nMaxPeriod = int32_max; PATTERNINDEX numPats = std::min(static_cast(patPos.size()), GetModSpecifications().patternsMax); if(numPats != patPos.size()) { // Hack: Notify user here if file contains more patterns than what can be read. AddToLog(LogWarning, MPT_UFORMAT("The module contains {} patterns but only {} patterns can be loaded in this OpenMPT version.")(patPos.size(), numPats)); } if(!(loadFlags & loadPatternData)) { numPats = 0; } // Checking for number of used channels, which is not explicitely specified in the file. for(PATTERNINDEX pat = 0; pat < numPats; pat++) { if(patPos[pat] == 0 || !file.Seek(patPos[pat])) continue; uint16 len = file.ReadUint16LE(); ROWINDEX numRows = file.ReadUint16LE(); if(numRows < 1 || numRows > MAX_PATTERN_ROWS || !file.Skip(4)) continue; FileReader patternData = file.ReadChunk(len); ROWINDEX row = 0; std::vector chnMask(GetNumChannels()); while(row < numRows && patternData.CanRead(1)) { uint8 b = patternData.ReadUint8(); if(!b) { row++; continue; } CHANNELINDEX ch = (b & IT_bitmask_patternChanField_c); // 0x7f We have some data grab a byte keeping only 7 bits if(ch) { ch = (ch - 1);// & IT_bitmask_patternChanMask_c; // 0x3f mask of the byte again, keeping only 6 bits } if(ch >= chnMask.size()) { chnMask.resize(ch + 1, 0); } if(b & IT_bitmask_patternChanEnabled_c) // 0x80 check if the upper bit is enabled. { chnMask[ch] = patternData.ReadUint8(); // set the channel mask for this channel. } // Channel used if(chnMask[ch] & 0x0F) // if this channel is used set m_nChannels { if(ch >= GetNumChannels() && ch < MAX_BASECHANNELS) { m_nChannels = ch + 1; } } // Now we actually update the pattern-row entry the note,instrument etc. // Note if(chnMask[ch] & 1) patternData.Skip(1); // Instrument if(chnMask[ch] & 2) patternData.Skip(1); // Volume if(chnMask[ch] & 4) patternData.Skip(1); // Effect if(chnMask[ch] & 8) patternData.Skip(2); } lastSampleOffset = std::max(lastSampleOffset, file.GetPosition()); } // Compute extra instruments settings position if(lastSampleOffset > 0) { file.Seek(lastSampleOffset); if(lastSampleCompressed) { // If the last sample was compressed, we do not know where it ends. // Hence, in case we decided not to decode the sample data, we now // have to emulate this until we reach EOF or some instrument / song properties. while(file.CanRead(4)) { if(file.ReadMagic("XTPM") || file.ReadMagic("STPM")) { uint32 id = file.ReadUint32LE(); file.SkipBack(8); // Our chunk IDs should only contain ASCII characters if(!(id & 0x80808080) && (id & 0x60606060)) { break; } } file.Skip(file.ReadUint16LE()); } } } // Load instrument and song extensions. interpretModPlugMade |= LoadExtendedInstrumentProperties(file); if(interpretModPlugMade && !isBeRoTracker) { m_playBehaviour.reset(); m_nMixLevels = MixLevels::Original; } // Need to do this before reading the patterns because m_nChannels might be modified by LoadExtendedSongProperties. *sigh* LoadExtendedSongProperties(file, false, &interpretModPlugMade); // Reading Patterns Patterns.ResizeArray(numPats); for(PATTERNINDEX pat = 0; pat < numPats; pat++) { if(patPos[pat] == 0 || !file.Seek(patPos[pat])) { // Empty 64-row pattern if(!Patterns.Insert(pat, 64)) { AddToLog(LogWarning, MPT_UFORMAT("Allocating patterns failed starting from pattern {}")(pat)); break; } // Now (after the Insert() call), we can read the pattern name. CopyPatternName(Patterns[pat], patNames); continue; } uint16 len = file.ReadUint16LE(); ROWINDEX numRows = file.ReadUint16LE(); if(!file.Skip(4) || !Patterns.Insert(pat, numRows)) continue; FileReader patternData = file.ReadChunk(len); // Now (after the Insert() call), we can read the pattern name. CopyPatternName(Patterns[pat], patNames); std::vector chnMask(GetNumChannels()); std::vector lastValue(GetNumChannels(), ModCommand::Empty()); auto patData = Patterns[pat].begin(); ROWINDEX row = 0; while(row < numRows && patternData.CanRead(1)) { uint8 b = patternData.ReadUint8(); if(!b) { row++; patData += GetNumChannels(); continue; } CHANNELINDEX ch = b & IT_bitmask_patternChanField_c; // 0x7f if(ch) { ch = (ch - 1); //& IT_bitmask_patternChanMask_c; // 0x3f } if(ch >= chnMask.size()) { chnMask.resize(ch + 1, 0); lastValue.resize(ch + 1, ModCommand::Empty()); MPT_ASSERT(chnMask.size() <= GetNumChannels()); } if(b & IT_bitmask_patternChanEnabled_c) // 0x80 { chnMask[ch] = patternData.ReadUint8(); } // Now we grab the data for this particular row/channel. ModCommand dummy = ModCommand::Empty(); ModCommand &m = ch < m_nChannels ? patData[ch] : dummy; if(chnMask[ch] & 0x10) { m.note = lastValue[ch].note; } if(chnMask[ch] & 0x20) { m.instr = lastValue[ch].instr; } if(chnMask[ch] & 0x40) { m.volcmd = lastValue[ch].volcmd; m.vol = lastValue[ch].vol; } if(chnMask[ch] & 0x80) { m.command = lastValue[ch].command; m.param = lastValue[ch].param; } if(chnMask[ch] & 1) // Note { uint8 note = patternData.ReadUint8(); if(note < 0x80) note += NOTE_MIN; if(!(GetType() & MOD_TYPE_MPT)) { if(note > NOTE_MAX && note < 0xFD) note = NOTE_FADE; else if(note == 0xFD) note = NOTE_NONE; } m.note = note; lastValue[ch].note = note; } if(chnMask[ch] & 2) { uint8 instr = patternData.ReadUint8(); m.instr = instr; lastValue[ch].instr = instr; } if(chnMask[ch] & 4) { uint8 vol = patternData.ReadUint8(); // 0-64: Set Volume if(vol <= 64) { m.volcmd = VOLCMD_VOLUME; m.vol = vol; } else // 128-192: Set Panning if(vol >= 128 && vol <= 192) { m.volcmd = VOLCMD_PANNING; m.vol = vol - 128; } else // 65-74: Fine Volume Up if(vol < 75) { m.volcmd = VOLCMD_FINEVOLUP; m.vol = vol - 65; } else // 75-84: Fine Volume Down if(vol < 85) { m.volcmd = VOLCMD_FINEVOLDOWN; m.vol = vol - 75; } else // 85-94: Volume Slide Up if(vol < 95) { m.volcmd = VOLCMD_VOLSLIDEUP; m.vol = vol - 85; } else // 95-104: Volume Slide Down if(vol < 105) { m.volcmd = VOLCMD_VOLSLIDEDOWN; m.vol = vol - 95; } else // 105-114: Pitch Slide Up if(vol < 115) { m.volcmd = VOLCMD_PORTADOWN; m.vol = vol - 105; } else // 115-124: Pitch Slide Down if(vol < 125) { m.volcmd = VOLCMD_PORTAUP; m.vol = vol - 115; } else // 193-202: Portamento To if(vol >= 193 && vol <= 202) { m.volcmd = VOLCMD_TONEPORTAMENTO; m.vol = vol - 193; } else // 203-212: Vibrato depth if(vol >= 203 && vol <= 212) { m.volcmd = VOLCMD_VIBRATODEPTH; m.vol = vol - 203; // Old versions of ModPlug saved this as vibrato speed instead, so let's fix that. if(m.vol && m_dwLastSavedWithVersion && m_dwLastSavedWithVersion <= MPT_V("1.17.02.54")) m.volcmd = VOLCMD_VIBRATOSPEED; } else // 213-222: Unused (was velocity) // 223-232: Offset if(vol >= 223 && vol <= 232) { m.volcmd = VOLCMD_OFFSET; m.vol = vol - 223; } lastValue[ch].volcmd = m.volcmd; lastValue[ch].vol = m.vol; } // Reading command/param if(chnMask[ch] & 8) { const auto [command, param] = patternData.ReadArray(); m.command = command; m.param = param; S3MConvert(m, true); // In some IT-compatible trackers, it is possible to input a parameter without a command. // In this case, we still need to update the last value memory. OpenMPT didn't do this until v1.25.01.07. // Example: ckbounce.it lastValue[ch].command = m.command; lastValue[ch].param = m.param; } } } if(!m_dwLastSavedWithVersion && fileHeader.cwtv == 0x0888) { // Up to OpenMPT 1.17.02.45 (r165), it was possible that the "last saved with" field was 0 // when saving a file in OpenMPT for the first time. m_dwLastSavedWithVersion = MPT_V("1.17.00.00"); } if(m_dwLastSavedWithVersion && madeWithTracker.empty()) { madeWithTracker = U_("OpenMPT ") + mpt::ufmt::val(m_dwLastSavedWithVersion); if(memcmp(&fileHeader.reserved, "OMPT", 4) && (fileHeader.cwtv & 0xF000) == 0x5000) { madeWithTracker += U_(" (compatibility export)"); } else if(m_dwLastSavedWithVersion.IsTestVersion()) { madeWithTracker += U_(" (test build)"); } } else { const int32 schismDateVersion = SchismTrackerEpoch + ((fileHeader.cwtv == 0x1FFF) ? fileHeader.reserved : (fileHeader.cwtv - 0x1050)); switch(fileHeader.cwtv >> 12) { case 0: if(isBeRoTracker) { // Old versions madeWithTracker = U_("BeRoTracker"); } else if(fileHeader.cwtv == 0x0214 && fileHeader.cmwt == 0x0200 && fileHeader.flags == 9 && fileHeader.special == 0 && fileHeader.highlight_major == 0 && fileHeader.highlight_minor == 0 && fileHeader.insnum == 0 && fileHeader.patnum + 1 == fileHeader.ordnum && fileHeader.globalvol == 128 && fileHeader.mv == 100 && fileHeader.speed == 1 && fileHeader.sep == 128 && fileHeader.pwd == 0 && fileHeader.msglength == 0 && fileHeader.msgoffset == 0 && fileHeader.reserved == 0) { madeWithTracker = U_("OpenSPC conversion"); } else if(fileHeader.cwtv == 0x0214 && fileHeader.cmwt == 0x0200 && fileHeader.highlight_major == 0 && fileHeader.highlight_minor == 0 && fileHeader.reserved == 0) { // ModPlug Tracker 1.00a5, instruments 560 bytes apart m_dwLastSavedWithVersion = MPT_V("1.00.00.A5"); madeWithTracker = U_("ModPlug Tracker 1.00a5"); interpretModPlugMade = true; } else if(fileHeader.cwtv == 0x0214 && fileHeader.cmwt == 0x0214 && !memcmp(&fileHeader.reserved, "CHBI", 4)) { madeWithTracker = U_("ChibiTracker"); m_playBehaviour.reset(kITShortSampleRetrig); } else if(fileHeader.cwtv == 0x0214 && fileHeader.cmwt == 0x0214 && fileHeader.special <= 1 && fileHeader.pwd == 0 && fileHeader.reserved == 0 && (fileHeader.flags & (ITFileHeader::vol0Optimisations | ITFileHeader::instrumentMode | ITFileHeader::useMIDIPitchController | ITFileHeader::reqEmbeddedMIDIConfig | ITFileHeader::extendedFilterRange)) == ITFileHeader::instrumentMode && m_nSamples > 0 && (Samples[1].filename == "XXXXXXXX.YYY")) { madeWithTracker = U_("CheeseTracker"); } else if(fileHeader.cwtv == 0 && madeWithTracker.empty()) { madeWithTracker = U_("Unknown"); } else if(fileHeader.cmwt < 0x0300 && madeWithTracker.empty()) { if(fileHeader.cmwt > 0x0214) { madeWithTracker = U_("Impulse Tracker 2.15"); } else if(fileHeader.cwtv > 0x0214) { // Patched update of IT 2.14 (0x0215 - 0x0217 == p1 - p3) // p4 (as found on modland) adds the ITVSOUND driver, but doesn't seem to change // anything as far as file saving is concerned. madeWithTracker = MPT_UFORMAT("Impulse Tracker 2.14p{}")(fileHeader.cwtv - 0x0214); } else { madeWithTracker = MPT_UFORMAT("Impulse Tracker {}.{}")((fileHeader.cwtv & 0x0F00) >> 8, mpt::ufmt::hex0<2>((fileHeader.cwtv & 0xFF))); } if(m_FileHistory.empty() && fileHeader.reserved != 0) { // Starting from version 2.07, IT stores the total edit time of a module in the "reserved" field uint32 editTime = DecodeITEditTimer(fileHeader.cwtv, fileHeader.reserved); FileHistory hist; hist.openTime = static_cast(editTime * (HISTORY_TIMER_PRECISION / 18.2)); m_FileHistory.push_back(hist); } } break; case 1: madeWithTracker = GetSchismTrackerVersion(fileHeader.cwtv, fileHeader.reserved); // Hertz in linear mode: Added 2015-01-29, https://github.com/schismtracker/schismtracker/commit/671b30311082a0e7df041fca25f989b5d2478f69 if(schismDateVersion < SchismVersionFromDate<2015, 01, 29>::date && m_SongFlags[SONG_LINEARSLIDES]) m_playBehaviour.reset(kPeriodsAreHertz); // Hertz in Amiga mode: Added 2021-05-02, https://github.com/schismtracker/schismtracker/commit/c656a6cbd5aaf81198a7580faf81cb7960cb6afa else if(schismDateVersion < SchismVersionFromDate<2021, 05, 02>::date && !m_SongFlags[SONG_LINEARSLIDES]) m_playBehaviour.reset(kPeriodsAreHertz); // Qxx with short samples: Added 2016-05-13, https://github.com/schismtracker/schismtracker/commit/e7b1461fe751554309fd403713c2a1ef322105ca if(schismDateVersion < SchismVersionFromDate<2016, 05, 13>::date) m_playBehaviour.reset(kITShortSampleRetrig); // Instrument pan doesn't override channel pan: Added 2021-05-02, https://github.com/schismtracker/schismtracker/commit/a34ec86dc819915debc9e06f4727b77bf2dd29ee if(schismDateVersion < SchismVersionFromDate<2021, 05, 02>::date) m_playBehaviour.reset(kITDoNotOverrideChannelPan); // Notes set instrument panning, not instrument numbers: Added 2021-05-02, https://github.com/schismtracker/schismtracker/commit/648f5116f984815c69e11d018b32dfec53c6b97a if(schismDateVersion < SchismVersionFromDate<2021, 05, 02>::date) m_playBehaviour.reset(kITPanningReset); // Imprecise calculation of ping-pong loop wraparound: Added 2021-11-01, https://github.com/schismtracker/schismtracker/commit/22cbb9b676e9c2c9feb7a6a17deca7a17ac138cc if(schismDateVersion < SchismVersionFromDate<2021, 11, 01>::date) m_playBehaviour.set(kImprecisePingPongLoops); // Pitch/Pan Separation can be overridden by panning commands: Added 2021-11-01, https://github.com/schismtracker/schismtracker/commit/6e9f1207015cae0fe1b829fff7bb867e02ec6dea if(schismDateVersion < SchismVersionFromDate<2021, 11, 01>::date) m_playBehaviour.reset(kITPitchPanSeparation); break; case 4: madeWithTracker = MPT_UFORMAT("pyIT {}.{}")((fileHeader.cwtv & 0x0F00) >> 8, mpt::ufmt::hex0<2>(fileHeader.cwtv & 0xFF)); break; case 6: madeWithTracker = U_("BeRoTracker"); break; case 7: if(fileHeader.cwtv == 0x7FFF && fileHeader.cmwt == 0x0215) madeWithTracker = U_("munch.py"); else madeWithTracker = MPT_UFORMAT("ITMCK {}.{}.{}")((fileHeader.cwtv >> 8) & 0x0F, (fileHeader.cwtv >> 4) & 0x0F, fileHeader.cwtv & 0x0F); break; case 0xD: madeWithTracker = U_("spc2it"); break; } } if(GetType() == MOD_TYPE_MPT) { // START - mpt specific: if(fileHeader.cwtv > 0x0889 && file.Seek(mptStartPos)) { LoadMPTMProperties(file, fileHeader.cwtv); } } m_modFormat.formatName = (GetType() == MOD_TYPE_MPT) ? U_("OpenMPT MPTM") : MPT_UFORMAT("Impulse Tracker {}.{}")(fileHeader.cmwt >> 8, mpt::ufmt::hex0<2>(fileHeader.cmwt & 0xFF)); m_modFormat.type = (GetType() == MOD_TYPE_MPT) ? U_("mptm") : U_("it"); m_modFormat.madeWithTracker = std::move(madeWithTracker); m_modFormat.charset = m_dwLastSavedWithVersion ? mpt::Charset::Windows1252 : mpt::Charset::CP437; return true; } void CSoundFile::LoadMPTMProperties(FileReader &file, uint16 cwtv) { std::istringstream iStrm(mpt::buffer_cast(file.GetRawDataAsByteVector())); if(cwtv >= 0x88D) { srlztn::SsbRead ssb(iStrm); ssb.BeginRead("mptm", Version::Current().GetRawVersion()); int8 useUTF8Tuning = 0; ssb.ReadItem(useUTF8Tuning, "UTF8Tuning"); mpt::Charset TuningCharset = useUTF8Tuning ? mpt::Charset::UTF8 : GetCharsetInternal(); ssb.ReadItem(GetTuneSpecificTunings(), "0", [TuningCharset](std::istream &iStrm, CTuningCollection &tc, const std::size_t dummy){ return ReadTuningCollection(iStrm, tc, dummy, TuningCharset); }); ssb.ReadItem(*this, "1", [TuningCharset](std::istream& iStrm, CSoundFile& csf, const std::size_t dummy){ return ReadTuningMap(iStrm, csf, dummy, TuningCharset); }); ssb.ReadItem(Order, "2", &ReadModSequenceOld); ssb.ReadItem(Patterns, FileIdPatterns, &ReadModPatterns); mpt::Charset sequenceDefaultCharset = GetCharsetInternal(); ssb.ReadItem(Order, FileIdSequences, [sequenceDefaultCharset](std::istream &iStrm, ModSequenceSet &seq, std::size_t nSize){ return ReadModSequences(iStrm, seq, nSize, sequenceDefaultCharset); }); if(ssb.GetStatus() & srlztn::SNT_FAILURE) { AddToLog(LogError, U_("Unknown error occurred while deserializing file.")); } } else { // Loading for older files. mpt::ustring name; if(GetTuneSpecificTunings().Deserialize(iStrm, name, GetCharsetInternal()) != Tuning::SerializationResult::Success) { AddToLog(LogError, U_("Loading tune specific tunings failed.")); } else { ReadTuningMapImpl(iStrm, *this, GetCharsetInternal(), 0, cwtv < 0x88C); } } } #ifndef MODPLUG_NO_FILESAVE // Save edit history. Pass a null pointer for *f to retrieve the number of bytes that would be written. static uint32 SaveITEditHistory(const CSoundFile &sndFile, std::ostream *file) { size_t num = sndFile.GetFileHistory().size(); #ifdef MODPLUG_TRACKER const CModDoc *pModDoc = sndFile.GetpModDoc(); num += (pModDoc != nullptr) ? 1 : 0; // + 1 for this session #endif // MODPLUG_TRACKER uint16 fnum = mpt::saturate_cast(num); // Number of entries that are actually going to be written const uint32 bytesWritten = 2 + fnum * 8; // Number of bytes that are actually going to be written if(!file) { return bytesWritten; } std::ostream & f = *file; // Write number of history entries mpt::IO::WriteIntLE(f, fnum); // Write history data const size_t start = (num > uint16_max) ? num - uint16_max : 0; for(size_t n = start; n < num; n++) { FileHistory mptHistory; #ifdef MODPLUG_TRACKER if(n < sndFile.GetFileHistory().size()) #endif // MODPLUG_TRACKER { // Previous timestamps mptHistory = sndFile.GetFileHistory()[n]; #ifdef MODPLUG_TRACKER } else if(pModDoc != nullptr) { // Current ("new") timestamp const time_t creationTime = pModDoc->GetCreationTime(); MemsetZero(mptHistory.loadDate); //localtime_s(&loadDate, &creationTime); const tm* const p = localtime(&creationTime); if (p != nullptr) mptHistory.loadDate = *p; else sndFile.AddToLog(LogError, U_("Unable to retrieve current time.")); mptHistory.openTime = (uint32)(difftime(time(nullptr), creationTime) * HISTORY_TIMER_PRECISION); #endif // MODPLUG_TRACKER } ITHistoryStruct itHistory; itHistory.ConvertToIT(mptHistory); mpt::IO::Write(f, itHistory); } return bytesWritten; } bool CSoundFile::SaveIT(std::ostream &f, const mpt::PathString &filename, bool compatibilityExport) { const CModSpecifications &specs = (GetType() == MOD_TYPE_MPT ? ModSpecs::mptm : (compatibilityExport ? ModSpecs::it : ModSpecs::itEx)); uint32 dwChnNamLen; ITFileHeader itHeader; uint64 dwPos = 0; uint32 dwHdrPos = 0, dwExtra = 0; // Writing Header MemsetZero(itHeader); dwChnNamLen = 0; memcpy(itHeader.id, "IMPM", 4); mpt::String::WriteBuf(mpt::String::nullTerminated, itHeader.songname) = m_songName; itHeader.highlight_minor = mpt::saturate_cast(m_nDefaultRowsPerBeat); itHeader.highlight_major = mpt::saturate_cast(m_nDefaultRowsPerMeasure); if(GetType() == MOD_TYPE_MPT) { itHeader.ordnum = Order().GetLengthTailTrimmed(); if(Order().NeedsExtraDatafield() && itHeader.ordnum > 256) { // If there are more order items, write them elsewhere. itHeader.ordnum = 256; } } else { // An additional "---" pattern is appended so Impulse Tracker won't ignore the last order item. // Interestingly, this can exceed IT's 256 order limit. Also, IT will always save at least two orders. itHeader.ordnum = std::min(Order().GetLengthTailTrimmed(), specs.ordersMax) + 1; if(itHeader.ordnum < 2) itHeader.ordnum = 2; } itHeader.insnum = std::min(m_nInstruments, specs.instrumentsMax); itHeader.smpnum = std::min(m_nSamples, specs.samplesMax); itHeader.patnum = std::min(Patterns.GetNumPatterns(), specs.patternsMax); // Parapointers std::vector patpos(itHeader.patnum); std::vector smppos(itHeader.smpnum); std::vector inspos(itHeader.insnum); //VERSION if(GetType() == MOD_TYPE_MPT) { // MPTM itHeader.cwtv = verMptFileVer; // Used in OMPT-hack versioning. itHeader.cmwt = 0x888; } else { // IT const uint32 mptVersion = Version::Current().GetRawVersion(); itHeader.cwtv = 0x5000 | static_cast((mptVersion >> 16) & 0x0FFF); // format: txyy (t = tracker ID, x = version major, yy = version minor), e.g. 0x5117 (OpenMPT = 5, 117 = v1.17) itHeader.cmwt = 0x0214; // Common compatible tracker :) // Hack from schism tracker: for(INSTRUMENTINDEX nIns = 1; nIns <= GetNumInstruments(); nIns++) { if(Instruments[nIns] && Instruments[nIns]->PitchEnv.dwFlags[ENV_FILTER]) { itHeader.cmwt = 0x0216; break; } } if(compatibilityExport) itHeader.reserved = mptVersion & 0xFFFF; else memcpy(&itHeader.reserved, "OMPT", 4); } itHeader.flags = ITFileHeader::useStereoPlayback | ITFileHeader::useMIDIPitchController; itHeader.special = ITFileHeader::embedEditHistory | ITFileHeader::embedPatternHighlights; if(m_nInstruments) itHeader.flags |= ITFileHeader::instrumentMode; if(m_SongFlags[SONG_LINEARSLIDES]) itHeader.flags |= ITFileHeader::linearSlides; if(m_SongFlags[SONG_ITOLDEFFECTS]) itHeader.flags |= ITFileHeader::itOldEffects; if(m_SongFlags[SONG_ITCOMPATGXX]) itHeader.flags |= ITFileHeader::itCompatGxx; if(m_SongFlags[SONG_EXFILTERRANGE] && !compatibilityExport) itHeader.flags |= ITFileHeader::extendedFilterRange; itHeader.globalvol = static_cast(m_nDefaultGlobalVolume / 2u); itHeader.mv = static_cast(std::min(m_nSamplePreAmp, uint32(128))); itHeader.speed = mpt::saturate_cast(m_nDefaultSpeed); itHeader.tempo = mpt::saturate_cast(m_nDefaultTempo.GetInt()); // We save the real tempo in an extension below if it exceeds 255. itHeader.sep = 128; // pan separation // IT doesn't have a per-instrument Pitch Wheel Depth setting, so we just store the first non-zero PWD setting in the header. for(INSTRUMENTINDEX ins = 1; ins <= GetNumInstruments(); ins++) { if(Instruments[ins] != nullptr && Instruments[ins]->midiPWD != 0) { itHeader.pwd = static_cast(std::abs(Instruments[ins]->midiPWD)); break; } } dwHdrPos = sizeof(itHeader) + itHeader.ordnum; // Channel Pan and Volume memset(itHeader.chnpan, 0xA0, 64); memset(itHeader.chnvol, 64, 64); for(CHANNELINDEX ich = 0; ich < std::min(m_nChannels, CHANNELINDEX(64)); ich++) // Header only has room for settings for 64 chans... { itHeader.chnpan[ich] = (uint8)(ChnSettings[ich].nPan >> 2); if (ChnSettings[ich].dwFlags[CHN_SURROUND]) itHeader.chnpan[ich] = 100; itHeader.chnvol[ich] = (uint8)(ChnSettings[ich].nVolume); #ifdef MODPLUG_TRACKER if(TrackerSettings::Instance().MiscSaveChannelMuteStatus) #endif if (ChnSettings[ich].dwFlags[CHN_MUTE]) itHeader.chnpan[ich] |= 0x80; } // Channel names if(!compatibilityExport) { for(CHANNELINDEX i = 0; i < m_nChannels; i++) { if(ChnSettings[i].szName[0]) { dwChnNamLen = (i + 1) * MAX_CHANNELNAME; } } if(dwChnNamLen) dwExtra += dwChnNamLen + 8; } if(!m_MidiCfg.IsMacroDefaultSetupUsed()) { itHeader.flags |= ITFileHeader::reqEmbeddedMIDIConfig; itHeader.special |= ITFileHeader::embedMIDIConfiguration; dwExtra += sizeof(MIDIMacroConfigData); } // Pattern Names const PATTERNINDEX numNamedPats = compatibilityExport ? 0 : Patterns.GetNumNamedPatterns(); if(numNamedPats > 0) { dwExtra += (numNamedPats * MAX_PATTERNNAME) + 8; } // Mix Plugins. Just calculate the size of this extra block for now. if(!compatibilityExport) { dwExtra += SaveMixPlugins(nullptr, true); } // Edit History. Just calculate the size of this extra block for now. dwExtra += SaveITEditHistory(*this, nullptr); // Comments uint16 msglength = 0; if(!m_songMessage.empty()) { itHeader.special |= ITFileHeader::embedSongMessage; itHeader.msglength = msglength = mpt::saturate_cast(m_songMessage.length() + 1u); itHeader.msgoffset = dwHdrPos + dwExtra + (itHeader.insnum + itHeader.smpnum + itHeader.patnum) * 4; } // Write file header mpt::IO::Write(f, itHeader); Order().WriteAsByte(f, itHeader.ordnum); mpt::IO::Write(f, inspos); mpt::IO::Write(f, smppos); mpt::IO::Write(f, patpos); // Writing edit history information SaveITEditHistory(*this, &f); // Writing midi cfg if(itHeader.flags & ITFileHeader::reqEmbeddedMIDIConfig) { mpt::IO::Write(f, static_cast(m_MidiCfg)); } // Writing pattern names if(numNamedPats) { mpt::IO::WriteRaw(f, "PNAM", 4); mpt::IO::WriteIntLE(f, numNamedPats * MAX_PATTERNNAME); for(PATTERNINDEX pat = 0; pat < numNamedPats; pat++) { char name[MAX_PATTERNNAME]; mpt::String::WriteBuf(mpt::String::maybeNullTerminated, name) = Patterns[pat].GetName(); mpt::IO::Write(f, name); } } // Writing channel names if(dwChnNamLen && !compatibilityExport) { mpt::IO::WriteRaw(f, "CNAM", 4); mpt::IO::WriteIntLE(f, dwChnNamLen); uint32 nChnNames = dwChnNamLen / MAX_CHANNELNAME; for(uint32 inam = 0; inam < nChnNames; inam++) { char name[MAX_CHANNELNAME]; mpt::String::WriteBuf(mpt::String::maybeNullTerminated, name) = ChnSettings[inam].szName; mpt::IO::Write(f, name); } } // Writing mix plugins info if(!compatibilityExport) { SaveMixPlugins(&f, false); } // Writing song message dwPos = dwHdrPos + dwExtra + (itHeader.insnum + itHeader.smpnum + itHeader.patnum) * 4; if(itHeader.special & ITFileHeader::embedSongMessage) { dwPos += msglength; mpt::IO::WriteRaw(f, m_songMessage.c_str(), msglength); } // Writing instruments const ModInstrument dummyInstr; for(INSTRUMENTINDEX nins = 1; nins <= itHeader.insnum; nins++) { ITInstrumentEx iti; uint32 instSize; const ModInstrument &instr = (Instruments[nins] != nullptr) ? *Instruments[nins] : dummyInstr; instSize = iti.ConvertToIT(instr, compatibilityExport, *this); // Writing instrument inspos[nins - 1] = static_cast(dwPos); dwPos += instSize; mpt::IO::WritePartial(f, iti, instSize); } // Writing dummy sample headers (until we know the correct sample data offset) ITSample itss; MemsetZero(itss); for(SAMPLEINDEX smp = 0; smp < itHeader.smpnum; smp++) { smppos[smp] = static_cast(dwPos); dwPos += sizeof(ITSample); mpt::IO::Write(f, itss); } // Writing Patterns bool needsMptPatSave = false; for(PATTERNINDEX pat = 0; pat < itHeader.patnum; pat++) { uint32 dwPatPos = static_cast(dwPos); if (!Patterns.IsValidPat(pat)) continue; if(Patterns[pat].GetOverrideSignature()) needsMptPatSave = true; // Check for empty pattern if(Patterns[pat].GetNumRows() == 64 && Patterns.IsPatternEmpty(pat)) { patpos[pat] = 0; continue; } patpos[pat] = static_cast(dwPos); // Write pattern header ROWINDEX writeRows = mpt::saturate_cast(Patterns[pat].GetNumRows()); uint16 writeSize = 0; uint16le patinfo[4]; patinfo[0] = 0; patinfo[1] = static_cast(writeRows); patinfo[2] = 0; patinfo[3] = 0; mpt::IO::Write(f, patinfo); dwPos += 8; struct ChnState { ModCommand lastCmd; uint8 mask = 0xFF; }; const CHANNELINDEX maxChannels = std::min(specs.channelsMax, GetNumChannels()); std::vector chnStates(maxChannels); // Maximum 7 bytes per cell, plus end of row marker, so this buffer is always large enough to cover one row. std::vector buf(7 * maxChannels + 1); for(ROWINDEX row = 0; row < writeRows; row++) { uint32 len = 0; const ModCommand *m = Patterns[pat].GetpModCommand(row, 0); for(CHANNELINDEX ch = 0; ch < maxChannels; ch++, m++) { // Skip mptm-specific notes. if(m->IsPcNote()) { needsMptPatSave = true; continue; } auto &chnState = chnStates[ch]; uint8 b = 0; uint8 command = m->command; uint8 param = m->param; uint8 vol = 0xFF; uint8 note = m->note; if (note != NOTE_NONE) b |= 1; if (m->IsNote()) note -= NOTE_MIN; if (note == NOTE_FADE && GetType() != MOD_TYPE_MPT) note = 0xF6; if (m->instr) b |= 2; if (m->volcmd != VOLCMD_NONE) { vol = std::min(m->vol, uint8(9)); switch(m->volcmd) { case VOLCMD_VOLUME: vol = std::min(m->vol, uint8(64)); break; case VOLCMD_PANNING: vol = std::min(m->vol, uint8(64)) + 128; break; case VOLCMD_VOLSLIDEUP: vol += 85; break; case VOLCMD_VOLSLIDEDOWN: vol += 95; break; case VOLCMD_FINEVOLUP: vol += 65; break; case VOLCMD_FINEVOLDOWN: vol += 75; break; case VOLCMD_VIBRATODEPTH: vol += 203; break; case VOLCMD_TONEPORTAMENTO: vol += 193; break; case VOLCMD_PORTADOWN: vol += 105; break; case VOLCMD_PORTAUP: vol += 115; break; case VOLCMD_VIBRATOSPEED: if(command == CMD_NONE) { // Move unsupported command if possible command = CMD_VIBRATO; param = std::min(m->vol, uint8(15)) << 4; vol = 0xFF; } else { vol = 203; } break; case VOLCMD_OFFSET: if(!compatibilityExport) vol += 223; else vol = 0xFF; break; default: vol = 0xFF; } } if (vol != 0xFF) b |= 4; if (command != CMD_NONE) { S3MSaveConvert(command, param, true, compatibilityExport); if (command) b |= 8; } // Packing information if (b) { // Same note ? if (b & 1) { if ((note == chnState.lastCmd.note) && (chnState.lastCmd.volcmd & 1)) { b &= ~1; b |= 0x10; } else { chnState.lastCmd.note = note; chnState.lastCmd.volcmd |= 1; } } // Same instrument ? if (b & 2) { if ((m->instr == chnState.lastCmd.instr) && (chnState.lastCmd.volcmd & 2)) { b &= ~2; b |= 0x20; } else { chnState.lastCmd.instr = m->instr; chnState.lastCmd.volcmd |= 2; } } // Same volume column byte ? if (b & 4) { if ((vol == chnState.lastCmd.vol) && (chnState.lastCmd.volcmd & 4)) { b &= ~4; b |= 0x40; } else { chnState.lastCmd.vol = vol; chnState.lastCmd.volcmd |= 4; } } // Same command / param ? if (b & 8) { if ((command == chnState.lastCmd.command) && (param == chnState.lastCmd.param) && (chnState.lastCmd.volcmd & 8)) { b &= ~8; b |= 0x80; } else { chnState.lastCmd.command = command; chnState.lastCmd.param = param; chnState.lastCmd.volcmd |= 8; } } if (b != chnState.mask) { chnState.mask = b; buf[len++] = static_cast((ch + 1) | IT_bitmask_patternChanEnabled_c); buf[len++] = b; } else { buf[len++] = static_cast(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; if(writeSize > uint16_max - len) { AddToLog(LogWarning, MPT_UFORMAT("Warning: File format limit was reached. Some pattern data may not get written to file. (pattern {})")(pat)); break; } else { dwPos += len; writeSize += (uint16)len; mpt::IO::WriteRaw(f, buf.data(), len); } } mpt::IO::SeekAbsolute(f, dwPatPos); patinfo[0] = writeSize; mpt::IO::Write(f, patinfo); mpt::IO::SeekAbsolute(f, dwPos); } // Writing Sample Data for(SAMPLEINDEX smp = 1; smp <= itHeader.smpnum; smp++) { const ModSample &sample = Samples[smp]; #ifdef MODPLUG_TRACKER uint32 type = GetType() == MOD_TYPE_IT ? 1 : 4; if(compatibilityExport) type = 2; bool compress = ((((sample.GetNumChannels() > 1) ? TrackerSettings::Instance().MiscITCompressionStereo : TrackerSettings::Instance().MiscITCompressionMono) & type) != 0); #else bool compress = false; #endif // MODPLUG_TRACKER // Old MPT, DUMB and probably other libraries will only consider the IT2.15 compression flag if the header version also indicates IT2.15. // MilkyTracker <= 0.90.85 assumes IT2.15 compression with cmwt == 0x215, ignoring the delta flag completely. itss.ConvertToIT(sample, GetType(), compress, itHeader.cmwt >= 0x215, GetType() == MOD_TYPE_MPT); const bool isExternal = itss.cvt == ITSample::cvtExternalSample; mpt::String::WriteBuf(mpt::String::nullTerminated, itss.name) = m_szNames[smp]; itss.samplepointer = static_cast(dwPos); if(dwPos > uint32_max) { // Sample position does not fit into sample pointer! AddToLog(LogWarning, MPT_UFORMAT("Cannot save sample {}: File size exceeds 4 GB.")(smp)); itss.samplepointer = 0; itss.length = 0; } SmpLength smpLength = itss.length; // Possibly truncated to 2^32 samples mpt::IO::SeekAbsolute(f, smppos[smp - 1]); mpt::IO::Write(f, itss); if(dwPos > uint32_max) { continue; } // TODO this actually wraps around at 2 GB, so we either need to use the 64-bit seek API or warn earlier! mpt::IO::SeekAbsolute(f, dwPos); if(!isExternal) { if(sample.nLength > smpLength && smpLength != 0) { // Sample length does not fit into IT header! AddToLog(LogWarning, MPT_UFORMAT("Truncating sample {}: Length exceeds exceeds 4 gigasamples.")(smp)); } dwPos += itss.GetSampleFormat().WriteSample(f, sample, smpLength); } else { #ifdef MPT_EXTERNAL_SAMPLES const std::string filenameU8 = GetSamplePath(smp).AbsolutePathToRelative(filename.GetPath()).ToUTF8(); const size_t strSize = filenameU8.size(); size_t intBytes = 0; if(mpt::IO::WriteVarInt(f, strSize, &intBytes)) { dwPos += intBytes + strSize; mpt::IO::WriteRaw(f, filenameU8.data(), strSize); } #else MPT_UNREFERENCED_PARAMETER(filename); #endif // MPT_EXTERNAL_SAMPLES } } //Save hacked-on extra info if(!compatibilityExport) { if(GetNumInstruments()) { SaveExtendedInstrumentProperties(itHeader.insnum, f); } SaveExtendedSongProperties(f); } // Updating offsets mpt::IO::SeekAbsolute(f, dwHdrPos); mpt::IO::Write(f, inspos); mpt::IO::Write(f, smppos); mpt::IO::Write(f, patpos); if(GetType() == MOD_TYPE_IT) { return true; } //hack //BEGIN: MPT SPECIFIC: bool success = true; mpt::IO::SeekEnd(f); const mpt::IO::Offset MPTStartPos = mpt::IO::TellWrite(f); srlztn::SsbWrite ssb(f); ssb.BeginWrite("mptm", Version::Current().GetRawVersion()); if(GetTuneSpecificTunings().GetNumTunings() > 0 || AreNonDefaultTuningsUsed(*this)) ssb.WriteItem(int8(1), "UTF8Tuning"); if(GetTuneSpecificTunings().GetNumTunings() > 0) ssb.WriteItem(GetTuneSpecificTunings(), "0", &WriteTuningCollection); if(AreNonDefaultTuningsUsed(*this)) ssb.WriteItem(*this, "1", &WriteTuningMap); if(Order().NeedsExtraDatafield()) ssb.WriteItem(Order, "2", &WriteModSequenceOld); if(needsMptPatSave) ssb.WriteItem(Patterns, FileIdPatterns, &WriteModPatterns); ssb.WriteItem(Order, FileIdSequences, &WriteModSequences); ssb.FinishWrite(); if(ssb.GetStatus() & srlztn::SNT_FAILURE) { AddToLog(LogError, U_("Error occurred in writing MPTM extensions.")); } //Last 4 bytes should tell where the hack mpt things begin. if(!f.good()) { f.clear(); success = false; } mpt::IO::WriteIntLE(f, static_cast(MPTStartPos)); mpt::IO::SeekEnd(f); //END : MPT SPECIFIC //NO WRITING HERE ANYMORE. return success; } #endif // MODPLUG_NO_FILESAVE #ifndef MODPLUG_NO_FILESAVE uint32 CSoundFile::SaveMixPlugins(std::ostream *file, bool updatePlugData) { #ifndef NO_PLUGINS uint32 totalSize = 0; for(PLUGINDEX i = 0; i < MAX_MIXPLUGINS; i++) { const SNDMIXPLUGIN &plugin = m_MixPlugins[i]; if(plugin.IsValidPlugin()) { uint32 chunkSize = sizeof(SNDMIXPLUGININFO) + 4; // plugininfo+4 (datalen) if(plugin.pMixPlugin && updatePlugData) { plugin.pMixPlugin->SaveAllParameters(); } const uint32 extraDataSize = 4 + sizeof(float32) + // 4 for ID and size of dryRatio 4 + sizeof(int32); // Default Program // For each extra entity, add 4 for ID, plus 4 for size of entity, plus size of entity chunkSize += extraDataSize + 4; // +4 is for size field itself const uint32 plugDataSize = std::min(mpt::saturate_cast(plugin.pluginData.size()), uint32_max - chunkSize); chunkSize += plugDataSize; if(file) { std::ostream &f = *file; // Chunk ID (= plugin ID) char id[4] = { 'F', 'X', '0', '0' }; if(i >= 100) id[1] = '0' + (i / 100u); id[2] += (i / 10u) % 10u; id[3] += (i % 10u); mpt::IO::WriteRaw(f, id, 4); // Write chunk size, plugin info and plugin data chunk mpt::IO::WriteIntLE(f, chunkSize); mpt::IO::Write(f, m_MixPlugins[i].Info); mpt::IO::WriteIntLE(f, plugDataSize); if(plugDataSize) { mpt::IO::WriteRaw(f, m_MixPlugins[i].pluginData.data(), plugDataSize); } mpt::IO::WriteIntLE(f, extraDataSize); // Dry/Wet ratio mpt::IO::WriteRaw(f, "DWRT", 4); // DWRT chunk does not include a size, so better make sure we always write 4 bytes here. static_assert(sizeof(IEEE754binary32LE) == 4); mpt::IO::Write(f, IEEE754binary32LE(m_MixPlugins[i].fDryRatio)); // Default program mpt::IO::WriteRaw(f, "PROG", 4); // PROG chunk does not include a size, so better make sure we always write 4 bytes here. static_assert(sizeof(m_MixPlugins[i].defaultProgram) == sizeof(int32)); mpt::IO::WriteIntLE(f, m_MixPlugins[i].defaultProgram); // Please, if you add any more chunks here, don't repeat history (see above) and *do* add a size field for your chunk, mmmkay? } totalSize += chunkSize + 8; } } std::vector chinfo(GetNumChannels()); uint32 numChInfo = 0; for(CHANNELINDEX j = 0; j < GetNumChannels(); j++) { if((chinfo[j] = ChnSettings[j].nMixPlugin) != 0) { numChInfo = j + 1; } } if(numChInfo) { if(file) { std::ostream &f = *file; mpt::IO::WriteRaw(f, "CHFX", 4); mpt::IO::WriteIntLE(f, numChInfo * 4); chinfo.resize(numChInfo); mpt::IO::Write(f, chinfo); } totalSize += numChInfo * 4 + 8; } return totalSize; #else MPT_UNREFERENCED_PARAMETER(file); MPT_UNREFERENCED_PARAMETER(updatePlugData); return 0; #endif // NO_PLUGINS } #endif // MODPLUG_NO_FILESAVE bool CSoundFile::LoadMixPlugins(FileReader &file) { bool isBeRoTracker = false; while(file.CanRead(9)) { char code[4]; file.ReadArray(code); const uint32 chunkSize = file.ReadUint32LE(); if(!memcmp(code, "IMPI", 4) // IT instrument, we definitely read too far || !memcmp(code, "IMPS", 4) // IT sample, ditto || !memcmp(code, "XTPM", 4) // Instrument extensions, ditto || !memcmp(code, "STPM", 4) // Song extensions, ditto || !file.CanRead(chunkSize)) { file.SkipBack(8); return isBeRoTracker; } FileReader chunk = file.ReadChunk(chunkSize); // Channel FX if(!memcmp(code, "CHFX", 4)) { for(auto &chn : ChnSettings) { chn.nMixPlugin = static_cast(chunk.ReadUint32LE()); } #ifndef NO_PLUGINS } // Plugin Data FX00, ... FX99, F100, ... F255 #define MPT_ISDIGIT(x) (code[(x)] >= '0' && code[(x)] <= '9') else if(code[0] == 'F' && (code[1] == 'X' || MPT_ISDIGIT(1)) && MPT_ISDIGIT(2) && MPT_ISDIGIT(3)) #undef MPT_ISDIGIT { PLUGINDEX plug = (code[2] - '0') * 10 + (code[3] - '0'); //calculate plug-in number. if(code[1] != 'X') plug += (code[1] - '0') * 100; if(plug < MAX_MIXPLUGINS) { ReadMixPluginChunk(chunk, m_MixPlugins[plug]); } #endif // NO_PLUGINS } else if(!memcmp(code, "MODU", 4)) { isBeRoTracker = true; m_dwLastSavedWithVersion = Version(); // Reset MPT detection for old files that have a similar fingerprint } } return isBeRoTracker; } #ifndef NO_PLUGINS void CSoundFile::ReadMixPluginChunk(FileReader &file, SNDMIXPLUGIN &plugin) { // MPT's standard plugin data. Size not specified in file.. grrr.. file.ReadStruct(plugin.Info); mpt::String::SetNullTerminator(plugin.Info.szName.buf); mpt::String::SetNullTerminator(plugin.Info.szLibraryName.buf); plugin.editorX = plugin.editorY = int32_min; // Plugin user data FileReader pluginDataChunk = file.ReadChunk(file.ReadUint32LE()); plugin.pluginData.resize(mpt::saturate_cast(pluginDataChunk.BytesLeft())); pluginDataChunk.ReadRaw(mpt::as_span(plugin.pluginData)); if(FileReader modularData = file.ReadChunk(file.ReadUint32LE()); modularData.IsValid()) { while(modularData.CanRead(5)) { // do we recognize this chunk? char code[4]; modularData.ReadArray(code); uint32 dataSize = 0; if(!memcmp(code, "DWRT", 4) || !memcmp(code, "PROG", 4)) { // Legacy system with fixed size chunks dataSize = 4; } else { dataSize = modularData.ReadUint32LE(); } FileReader dataChunk = modularData.ReadChunk(dataSize); if(!memcmp(code, "DWRT", 4)) { plugin.fDryRatio = std::clamp(dataChunk.ReadFloatLE(), 0.0f, 1.0f); if(!std::isnormal(plugin.fDryRatio)) plugin.fDryRatio = 0.0f; } else if(!memcmp(code, "PROG", 4)) { plugin.defaultProgram = dataChunk.ReadUint32LE(); } else if(!memcmp(code, "MCRO", 4)) { // Read plugin-specific macros //dataChunk.ReadStructPartial(plugin.macros, dataChunk.GetLength()); } } } } #endif // NO_PLUGINS #ifndef MODPLUG_NO_FILESAVE void CSoundFile::SaveExtendedSongProperties(std::ostream &f) const { const CModSpecifications &specs = GetModSpecifications(); // Extra song data - Yet Another Hack. mpt::IO::WriteIntLE(f, MagicBE("MPTS")); #define WRITEMODULARHEADER(code, fsize) \ { \ mpt::IO::WriteIntLE(f, code); \ MPT_ASSERT(mpt::in_range(fsize)); \ const uint16 _size = fsize; \ mpt::IO::WriteIntLE(f, _size); \ } #define WRITEMODULAR(code, field) \ { \ WRITEMODULARHEADER(code, sizeof(field)) \ mpt::IO::WriteIntLE(f, field); \ } if(m_nDefaultTempo.GetInt() > 255) { uint32 tempo = m_nDefaultTempo.GetInt(); WRITEMODULAR(MagicBE("DT.."), tempo); } if(m_nDefaultTempo.GetFract() != 0 && specs.hasFractionalTempo) { uint32 tempo = m_nDefaultTempo.GetFract(); WRITEMODULAR(MagicLE("DTFR"), tempo); } if(m_nDefaultRowsPerBeat > 255 || m_nDefaultRowsPerMeasure > 255 || GetType() == MOD_TYPE_XM) { WRITEMODULAR(MagicBE("RPB."), m_nDefaultRowsPerBeat); WRITEMODULAR(MagicBE("RPM."), m_nDefaultRowsPerMeasure); } if(GetType() != MOD_TYPE_XM) { WRITEMODULAR(MagicBE("C..."), m_nChannels); } if((GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) && GetNumChannels() > 64) { // IT header has only room for 64 channels. Save the settings that do not fit to the header here as an extension. WRITEMODULARHEADER(MagicBE("ChnS"), (GetNumChannels() - 64) * 2); for(CHANNELINDEX chn = 64; chn < GetNumChannels(); chn++) { uint8 panvol[2]; panvol[0] = (uint8)(ChnSettings[chn].nPan >> 2); if (ChnSettings[chn].dwFlags[CHN_SURROUND]) panvol[0] = 100; if (ChnSettings[chn].dwFlags[CHN_MUTE]) panvol[0] |= 0x80; panvol[1] = (uint8)ChnSettings[chn].nVolume; mpt::IO::Write(f, panvol); } } { WRITEMODULARHEADER(MagicBE("TM.."), 1); uint8 mode = static_cast(m_nTempoMode); mpt::IO::WriteIntLE(f, mode); } const int32 tmpMixLevels = static_cast(m_nMixLevels); WRITEMODULAR(MagicBE("PMM."), tmpMixLevels); if(m_dwCreatedWithVersion) { WRITEMODULAR(MagicBE("CWV."), m_dwCreatedWithVersion.GetRawVersion()); } WRITEMODULAR(MagicBE("LSWV"), Version::Current().GetRawVersion()); WRITEMODULAR(MagicBE("SPA."), m_nSamplePreAmp); WRITEMODULAR(MagicBE("VSTV"), m_nVSTiVolume); if(GetType() == MOD_TYPE_XM && m_nDefaultGlobalVolume != MAX_GLOBAL_VOLUME) { WRITEMODULAR(MagicBE("DGV."), m_nDefaultGlobalVolume); } if(GetType() != MOD_TYPE_XM && Order().GetRestartPos() != 0) { WRITEMODULAR(MagicBE("RP.."), Order().GetRestartPos()); } if(m_nResampling != SRCMODE_DEFAULT && specs.hasDefaultResampling) { WRITEMODULAR(MagicLE("RSMP"), static_cast(m_nResampling)); } // Sample cues if(GetType() == MOD_TYPE_MPT) { for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++) { const ModSample &sample = Samples[smp]; if(sample.nLength && sample.HasCustomCuePoints()) { // Write one chunk for every sample. // Rationale: chunks are limited to 65536 bytes, which can easily be reached // with the amount of samples that OpenMPT supports. WRITEMODULARHEADER(MagicLE("CUES"), static_cast(2 + std::size(sample.cues) * 4)); mpt::IO::WriteIntLE(f, smp); for(auto cue : sample.cues) { mpt::IO::WriteIntLE(f, cue); } } } } // Tempo Swing Factors if(!m_tempoSwing.empty()) { std::ostringstream oStrm; TempoSwing::Serialize(oStrm, m_tempoSwing); std::string data = oStrm.str(); uint16 length = mpt::saturate_cast(data.size()); WRITEMODULARHEADER(MagicLE("SWNG"), length); mpt::IO::WriteRaw(f, data.data(), length); } // Playback compatibility flags { uint8 bits[(kMaxPlayBehaviours + 7) / 8u]; MemsetZero(bits); size_t maxBit = 0; for(size_t i = 0; i < kMaxPlayBehaviours; i++) { if(m_playBehaviour[i]) { bits[i >> 3] |= 1 << (i & 0x07); maxBit = i + 8; } } uint16 numBytes = static_cast(maxBit / 8u); WRITEMODULARHEADER(MagicBE("MSF."), numBytes); mpt::IO::WriteRaw(f, bits, numBytes); } if(!m_songArtist.empty() && specs.hasArtistName) { std::string songArtistU8 = mpt::ToCharset(mpt::Charset::UTF8, m_songArtist); uint16 length = mpt::saturate_cast(songArtistU8.length()); WRITEMODULARHEADER(MagicLE("AUTH"), length); mpt::IO::WriteRaw(f, songArtistU8.c_str(), length); } #ifdef MODPLUG_TRACKER // MIDI mapping directives if(GetMIDIMapper().GetCount() > 0) { const size_t objectsize = GetMIDIMapper().Serialize(); if(!mpt::in_range(objectsize)) { AddToLog(LogWarning, U_("Too many MIDI Mapping directives to save; data won't be written.")); } else { WRITEMODULARHEADER(MagicBE("MIMA"), static_cast(objectsize)); GetMIDIMapper().Serialize(&f); } } // Channel colors { CHANNELINDEX numChannels = 0; for(CHANNELINDEX i = 0; i < m_nChannels; i++) { if(ChnSettings[i].color != ModChannelSettings::INVALID_COLOR) { numChannels = i + 1; } } if(numChannels > 0) { WRITEMODULARHEADER(MagicLE("CCOL"), numChannels * 4); for(CHANNELINDEX i = 0; i < numChannels; i++) { uint32 color = ChnSettings[i].color; if(color != ModChannelSettings::INVALID_COLOR) color &= 0x00FFFFFF; std::array rgb{static_cast(color), static_cast(color >> 8), static_cast(color >> 16), static_cast(color >> 24)}; mpt::IO::Write(f, rgb); } } } #endif #undef WRITEMODULAR #undef WRITEMODULARHEADER return; } #endif // MODPLUG_NO_FILESAVE template void ReadField(FileReader &chunk, std::size_t size, T &field) { field = chunk.ReadSizedIntLE(size); } template void ReadFieldCast(FileReader &chunk, std::size_t size, T &field) { static_assert(sizeof(T) <= sizeof(int32)); field = static_cast(chunk.ReadSizedIntLE(size)); } void CSoundFile::LoadExtendedSongProperties(FileReader &file, bool ignoreChannelCount, bool *pInterpretMptMade) { if(!file.ReadMagic("STPM")) // 'MPTS' { return; } // Found MPTS, interpret the file MPT made. if(pInterpretMptMade != nullptr) *pInterpretMptMade = true; // HACK: Reset mod flags to default values here, as they are not always written. m_playBehaviour.reset(); while(file.CanRead(7)) { const uint32 code = file.ReadUint32LE(); const uint16 size = file.ReadUint16LE(); // Start of MPTM extensions, non-ASCII ID or truncated field if(code == MagicLE("228\x04")) { file.SkipBack(6); break; } else if((code & 0x80808080) || !(code & 0x60606060) || !file.CanRead(size)) { break; } FileReader chunk = file.ReadChunk(size); switch (code) // interpret field code { case MagicBE("DT.."): { uint32 tempo; ReadField(chunk, size, tempo); m_nDefaultTempo.Set(tempo, m_nDefaultTempo.GetFract()); break; } case MagicLE("DTFR"): { uint32 tempoFract; ReadField(chunk, size, tempoFract); m_nDefaultTempo.Set(m_nDefaultTempo.GetInt(), tempoFract); break; } case MagicBE("RPB."): ReadField(chunk, size, m_nDefaultRowsPerBeat); break; case MagicBE("RPM."): ReadField(chunk, size, m_nDefaultRowsPerMeasure); break; // FIXME: If there are only PC events on the last few channels in an MPTM MO3, they won't be imported! case MagicBE("C..."): if(!ignoreChannelCount) { CHANNELINDEX chn = 0; ReadField(chunk, size, chn); m_nChannels = Clamp(chn, m_nChannels, MAX_BASECHANNELS); } break; case MagicBE("TM.."): ReadFieldCast(chunk, size, m_nTempoMode); break; case MagicBE("PMM."): ReadFieldCast(chunk, size, m_nMixLevels); break; case MagicBE("CWV."): { uint32 ver = 0; ReadField(chunk, size, ver); m_dwCreatedWithVersion = Version(ver); break; } case MagicBE("LSWV"): { uint32 ver = 0; ReadField(chunk, size, ver); if(ver != 0) { m_dwLastSavedWithVersion = Version(ver); } break; } case MagicBE("SPA."): ReadField(chunk, size, m_nSamplePreAmp); break; case MagicBE("VSTV"): ReadField(chunk, size, m_nVSTiVolume); break; case MagicBE("DGV."): ReadField(chunk, size, m_nDefaultGlobalVolume); break; case MagicBE("RP.."): if(GetType() != MOD_TYPE_XM) { ORDERINDEX restartPos; ReadField(chunk, size, restartPos); Order().SetRestartPos(restartPos); } break; case MagicLE("RSMP"): ReadFieldCast(chunk, size, m_nResampling); if(!Resampling::IsKnownMode(m_nResampling)) m_nResampling = SRCMODE_DEFAULT; break; #ifdef MODPLUG_TRACKER case MagicBE("MIMA"): GetMIDIMapper().Deserialize(chunk); break; case MagicLE("CCOL"): // Channel colors { const CHANNELINDEX numChannels = std::min(MAX_BASECHANNELS, static_cast(size / 4u)); for(CHANNELINDEX i = 0; i < numChannels; i++) { auto rgb = chunk.ReadArray(); if(rgb[3]) ChnSettings[i].color = ModChannelSettings::INVALID_COLOR; else ChnSettings[i].color = rgb[0] | (rgb[1] << 8) | (rgb[2] << 16); } } break; #endif case MagicLE("AUTH"): { std::string artist; chunk.ReadString(artist, chunk.GetLength()); m_songArtist = mpt::ToUnicode(mpt::Charset::UTF8, artist); } break; case MagicBE("ChnS"): // Channel settings for channels 65+ if(size <= (MAX_BASECHANNELS - 64) * 2 && (size % 2u) == 0) { static_assert(mpt::array_size::size >= 64); const CHANNELINDEX loopLimit = std::min(uint16(64 + size / 2), uint16(std::size(ChnSettings))); for(CHANNELINDEX chn = 64; chn < loopLimit; chn++) { auto [pan, vol] = chunk.ReadArray(); if(pan != 0xFF) { ChnSettings[chn].nVolume = vol; ChnSettings[chn].nPan = 128; ChnSettings[chn].dwFlags.reset(); if(pan & 0x80) ChnSettings[chn].dwFlags.set(CHN_MUTE); pan &= 0x7F; if(pan <= 64) ChnSettings[chn].nPan = pan << 2; if(pan == 100) ChnSettings[chn].dwFlags.set(CHN_SURROUND); } } } break; case MagicLE("CUES"): // Sample cues if(size > 2) { SAMPLEINDEX smp = chunk.ReadUint16LE(); if(smp > 0 && smp <= GetNumSamples()) { ModSample &sample = Samples[smp]; for(auto &cue : sample.cues) { if(chunk.CanRead(4)) cue = chunk.ReadUint32LE(); else cue = MAX_SAMPLE_LENGTH; } } } break; case MagicLE("SWNG"): // Tempo Swing Factors if(size > 2) { std::istringstream iStrm(mpt::buffer_cast(chunk.ReadRawDataAsByteVector())); TempoSwing::Deserialize(iStrm, m_tempoSwing, chunk.GetLength()); } break; case MagicBE("MSF."): // Playback compatibility flags { size_t bit = 0; m_playBehaviour.reset(); while(chunk.CanRead(1) && bit < m_playBehaviour.size()) { uint8 b = chunk.ReadUint8(); for(uint8 i = 0; i < 8; i++, bit++) { if((b & (1 << i)) && bit < m_playBehaviour.size()) { m_playBehaviour.set(bit); } } } } break; } } // Validate read values. Limit(m_nDefaultTempo, GetModSpecifications().GetTempoMin(), GetModSpecifications().GetTempoMax()); if(m_nTempoMode >= TempoMode::NumModes) m_nTempoMode = TempoMode::Classic; if(m_nMixLevels >= MixLevels::NumMixLevels) m_nMixLevels = MixLevels::Original; //m_dwCreatedWithVersion //m_dwLastSavedWithVersion //m_nSamplePreAmp //m_nVSTiVolume //m_nDefaultGlobalVolume LimitMax(m_nDefaultGlobalVolume, MAX_GLOBAL_VOLUME); //m_nRestartPos //m_ModFlags LimitMax(m_nDefaultRowsPerBeat, MAX_ROWS_PER_BEAT); LimitMax(m_nDefaultRowsPerMeasure, MAX_ROWS_PER_BEAT); } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Load_itp.cpp0000644000175000017500000003102714175523206020342 00000000000000/* * Load_itp.cpp * ------------ * Purpose: Impulse Tracker Project (ITP) module loader * Notes : Despite its name, ITP is not a format supported by Impulse Tracker. * In fact, it's a format invented by the OpenMPT team to allow people to work * with the IT format, but keeping the instrument files with big samples separate * from the pattern data, to keep the work files small and handy. * The design of the format is quite flawed, though, so it was superseded by * extra functionality in the MPTM format in OpenMPT 1.24. * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "../common/version.h" #include "Loaders.h" #include "ITTools.h" #ifdef MODPLUG_TRACKER // For loading external instruments #include "../mptrack/Moddoc.h" #endif // MODPLUG_TRACKER #ifdef MPT_EXTERNAL_SAMPLES #include "../common/mptFileIO.h" #endif // MPT_EXTERNAL_SAMPLES OPENMPT_NAMESPACE_BEGIN // Version changelog: // v1.03: - Relative unicode instrument paths instead of absolute ANSI paths // - Per-path variable string length // - Embedded samples are IT-compressed // (rev. 3249) // v1.02: Explicitly updated format to use new instrument flags representation (rev. 483) // v1.01: Added option to embed instrument headers struct ITPModCommand { uint8 note; uint8 instr; uint8 volcmd; uint8 command; uint8 vol; uint8 param; operator ModCommand() const { static constexpr VolumeCommand ITPVolCmds[] = { VOLCMD_NONE, VOLCMD_VOLUME, VOLCMD_PANNING, VOLCMD_VOLSLIDEUP, VOLCMD_VOLSLIDEDOWN, VOLCMD_FINEVOLUP, VOLCMD_FINEVOLDOWN, VOLCMD_VIBRATOSPEED, VOLCMD_VIBRATODEPTH, VOLCMD_PANSLIDELEFT, VOLCMD_PANSLIDERIGHT, VOLCMD_TONEPORTAMENTO, VOLCMD_PORTAUP, VOLCMD_PORTADOWN, VOLCMD_PLAYCONTROL, VOLCMD_OFFSET, }; static constexpr EffectCommand ITPCommands[] = { CMD_NONE, CMD_ARPEGGIO, CMD_PORTAMENTOUP, CMD_PORTAMENTODOWN, CMD_TONEPORTAMENTO, CMD_VIBRATO, CMD_TONEPORTAVOL, CMD_VIBRATOVOL, CMD_TREMOLO, CMD_PANNING8, CMD_OFFSET, CMD_VOLUMESLIDE, CMD_POSITIONJUMP, CMD_VOLUME, CMD_PATTERNBREAK, CMD_RETRIG, CMD_SPEED, CMD_TEMPO, CMD_TREMOR, CMD_MODCMDEX, CMD_S3MCMDEX, CMD_CHANNELVOLUME, CMD_CHANNELVOLSLIDE, CMD_GLOBALVOLUME, CMD_GLOBALVOLSLIDE, CMD_KEYOFF, CMD_FINEVIBRATO, CMD_PANBRELLO, CMD_XFINEPORTAUPDOWN, CMD_PANNINGSLIDE, CMD_SETENVPOSITION, CMD_MIDI, CMD_SMOOTHMIDI, CMD_DELAYCUT, CMD_XPARAM, }; ModCommand result; result.note = (ModCommand::IsNote(note) || ModCommand::IsSpecialNote(note)) ? static_cast(note) : static_cast(NOTE_NONE); result.instr = instr; result.volcmd = (volcmd < std::size(ITPVolCmds)) ? ITPVolCmds[volcmd] : VOLCMD_NONE; result.command = (command < std::size(ITPCommands)) ? ITPCommands[command] : CMD_NONE; result.vol = vol; result.param = param; return result; } }; MPT_BINARY_STRUCT(ITPModCommand, 6) struct ITPHeader { uint32le magic; uint32le version; }; MPT_BINARY_STRUCT(ITPHeader, 8) static bool ValidateHeader(const ITPHeader &hdr) { if(hdr.magic != MagicBE(".itp")) { return false; } if(hdr.version < 0x00000100 || hdr.version > 0x00000103) { return false; } return true; } static uint64 GetHeaderMinimumAdditionalSize(const ITPHeader &hdr) { return 76 + (hdr.version <= 0x102 ? 4 : 0); } CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderITP(MemoryFileReader file, const uint64 *pfilesize) { ITPHeader hdr; if(!file.ReadStruct(hdr)) { return ProbeWantMoreData; } if(!ValidateHeader(hdr)) { return ProbeFailure; } return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(hdr)); } bool CSoundFile::ReadITP(FileReader &file, ModLoadingFlags loadFlags) { #if !defined(MPT_EXTERNAL_SAMPLES) && !defined(MPT_FUZZ_TRACKER) // Doesn't really make sense to support this format when there's no support for external files... MPT_UNREFERENCED_PARAMETER(file); MPT_UNREFERENCED_PARAMETER(loadFlags); return false; #else // !MPT_EXTERNAL_SAMPLES && !MPT_FUZZ_TRACKER enum ITPSongFlags { ITP_EMBEDMIDICFG = 0x00001, // Embed macros in file ITP_ITOLDEFFECTS = 0x00004, // Old Impulse Tracker effect implementations ITP_ITCOMPATGXX = 0x00008, // IT "Compatible Gxx" (IT's flag to behave more like other trackers w/r/t portamento effects) ITP_LINEARSLIDES = 0x00010, // Linear slides vs. Amiga slides ITP_EXFILTERRANGE = 0x08000, // Cutoff Filter has double frequency range (up to ~10Khz) ITP_ITPROJECT = 0x20000, // Is a project file ITP_ITPEMBEDIH = 0x40000, // Embed instrument headers in project file }; file.Rewind(); ITPHeader hdr; if(!file.ReadStruct(hdr)) { return false; } if(!ValidateHeader(hdr)) { return false; } if(!file.CanRead(mpt::saturate_cast(GetHeaderMinimumAdditionalSize(hdr)))) { return false; } if(loadFlags == onlyVerifyHeader) { return true; } const uint32 version = hdr.version; InitializeGlobals(MOD_TYPE_IT); m_playBehaviour.reset(); file.ReadSizedString(m_songName); // Song comments m_songMessage.Read(file, file.ReadUint32LE(), SongMessage::leCR); // Song global config const uint32 songFlags = file.ReadUint32LE(); if(!(songFlags & ITP_ITPROJECT)) { return false; } m_SongFlags.set(SONG_IMPORTED); if(songFlags & ITP_ITOLDEFFECTS) m_SongFlags.set(SONG_ITOLDEFFECTS); if(songFlags & ITP_ITCOMPATGXX) m_SongFlags.set(SONG_ITCOMPATGXX); if(songFlags & ITP_LINEARSLIDES) m_SongFlags.set(SONG_LINEARSLIDES); if(songFlags & ITP_EXFILTERRANGE) m_SongFlags.set(SONG_EXFILTERRANGE); m_nDefaultGlobalVolume = file.ReadUint32LE(); m_nSamplePreAmp = file.ReadUint32LE(); m_nDefaultSpeed = std::max(uint32(1), file.ReadUint32LE()); m_nDefaultTempo.Set(std::max(uint32(32), file.ReadUint32LE())); m_nChannels = static_cast(file.ReadUint32LE()); if(m_nChannels == 0 || m_nChannels > MAX_BASECHANNELS) { return false; } // channel name string length (=MAX_CHANNELNAME) uint32 size = file.ReadUint32LE(); // Channels' data for(CHANNELINDEX chn = 0; chn < m_nChannels; chn++) { ChnSettings[chn].nPan = std::min(static_cast(file.ReadUint32LE()), uint16(256)); ChnSettings[chn].dwFlags.reset(); uint32 flags = file.ReadUint32LE(); if(flags & 0x100) ChnSettings[chn].dwFlags.set(CHN_MUTE); if(flags & 0x800) ChnSettings[chn].dwFlags.set(CHN_SURROUND); ChnSettings[chn].nVolume = std::min(static_cast(file.ReadUint32LE()), uint16(64)); file.ReadString(ChnSettings[chn].szName, size); } // Song mix plugins { FileReader plugChunk = file.ReadChunk(file.ReadUint32LE()); LoadMixPlugins(plugChunk); } // MIDI Macro config file.ReadStructPartial(m_MidiCfg, file.ReadUint32LE()); m_MidiCfg.Sanitize(); // Song Instruments m_nInstruments = static_cast(file.ReadUint32LE()); if(m_nInstruments >= MAX_INSTRUMENTS) { m_nInstruments = 0; return false; } // Instruments' paths if(version <= 0x102) { size = file.ReadUint32LE(); // path string length } std::vector instrPaths(GetNumInstruments()); for(INSTRUMENTINDEX ins = 0; ins < GetNumInstruments(); ins++) { if(version > 0x102) { size = file.ReadUint32LE(); // path string length } std::string path; file.ReadString(path, size); #ifdef MODPLUG_TRACKER if(version <= 0x102) { instrPaths[ins] = mpt::PathString::FromLocaleSilent(path); } else #endif // MODPLUG_TRACKER { instrPaths[ins] = mpt::PathString::FromUTF8(path); } #ifdef MODPLUG_TRACKER if(const auto fileName = file.GetOptionalFileName(); fileName.has_value()) { instrPaths[ins] = instrPaths[ins].RelativePathToAbsolute(fileName->GetPath()); } else if(GetpModDoc() != nullptr) { instrPaths[ins] = instrPaths[ins].RelativePathToAbsolute(GetpModDoc()->GetPathNameMpt().GetPath()); } #endif // MODPLUG_TRACKER } // Song Orders size = file.ReadUint32LE(); ReadOrderFromFile(Order(), file, size, 0xFF, 0xFE); // Song Patterns const PATTERNINDEX numPats = static_cast(file.ReadUint32LE()); const PATTERNINDEX numNamedPats = static_cast(file.ReadUint32LE()); size_t patNameLen = file.ReadUint32LE(); // Size of each pattern name FileReader pattNames = file.ReadChunk(numNamedPats * patNameLen); // modcommand data length size = file.ReadUint32LE(); if(size != sizeof(ITPModCommand)) { return false; } if(loadFlags & loadPatternData) Patterns.ResizeArray(numPats); for(PATTERNINDEX pat = 0; pat < numPats; pat++) { const ROWINDEX numRows = file.ReadUint32LE(); FileReader patternChunk = file.ReadChunk(numRows * size * GetNumChannels()); // Allocate pattern if(!(loadFlags & loadPatternData) || !Patterns.Insert(pat, numRows)) { pattNames.Skip(patNameLen); continue; } if(pat < numNamedPats) { char patName[32]; if(pattNames.ReadString(patName, patNameLen)) Patterns[pat].SetName(patName); } // Pattern data size_t numCommands = GetNumChannels() * numRows; if(patternChunk.CanRead(sizeof(ITPModCommand) * numCommands)) { ModCommand *target = Patterns[pat].GetpModCommand(0, 0); while(numCommands-- != 0) { ITPModCommand data; patternChunk.ReadStruct(data); *(target++) = data; } } } // Load embedded samples // Read original number of samples m_nSamples = static_cast(file.ReadUint32LE()); LimitMax(m_nSamples, SAMPLEINDEX(MAX_SAMPLES - 1)); // Read number of embedded samples - at most as many as there are real samples in a valid file uint32 embeddedSamples = file.ReadUint32LE(); if(embeddedSamples > m_nSamples) { return false; } // Read samples for(uint32 smp = 0; smp < embeddedSamples && file.CanRead(8 + sizeof(ITSample)); smp++) { uint32 realSample = file.ReadUint32LE(); ITSample sampleHeader; file.ReadStruct(sampleHeader); FileReader sampleData = file.ReadChunk(file.ReadUint32LE()); if((loadFlags & loadSampleData) && realSample >= 1 && realSample <= GetNumSamples() && Samples[realSample].pData.pSample == nullptr && !memcmp(sampleHeader.id, "IMPS", 4)) { sampleHeader.ConvertToMPT(Samples[realSample]); m_szNames[realSample] = mpt::String::ReadBuf(mpt::String::nullTerminated, sampleHeader.name); // Read sample data sampleHeader.GetSampleFormat().ReadSample(Samples[realSample], sampleData); } } // Load instruments for(INSTRUMENTINDEX ins = 0; ins < GetNumInstruments(); ins++) { if(instrPaths[ins].empty()) continue; #ifdef MPT_EXTERNAL_SAMPLES InputFile f(instrPaths[ins], SettingCacheCompleteFileBeforeLoading()); FileReader instrFile = GetFileReader(f); if(!ReadInstrumentFromFile(ins + 1, instrFile, true)) { AddToLog(LogWarning, U_("Unable to open instrument: ") + instrPaths[ins].ToUnicode()); } #else AddToLog(LogWarning, MPT_UFORMAT("Loading external instrument {} ('{}') failed: External instruments are not supported.")(ins + 1, instrPaths[ins].ToUnicode())); #endif // MPT_EXTERNAL_SAMPLES } // Extra info data uint32 code = file.ReadUint32LE(); // Embed instruments' header [v1.01] if(version >= 0x101 && (songFlags & ITP_ITPEMBEDIH) && code == MagicBE("EBIH")) { code = file.ReadUint32LE(); INSTRUMENTINDEX ins = 1; while(ins <= GetNumInstruments() && file.CanRead(4)) { if(code == MagicBE("MPTS")) { break; } else if(code == MagicBE("SEP@") || code == MagicBE("MPTX")) { // jump code - switch to next instrument ins++; } else { ReadExtendedInstrumentProperty(Instruments[ins], code, file); } code = file.ReadUint32LE(); } } for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++) { Samples[smp].SetDefaultCuePoints(); } // Song extensions if(code == MagicBE("MPTS")) { file.SkipBack(4); LoadExtendedSongProperties(file, true); } m_nMaxPeriod = 0xF000; m_nMinPeriod = 8; // Before OpenMPT 1.20.01.09, the MIDI macros were always read from the file, even if the "embed" flag was not set. if(m_dwLastSavedWithVersion >= MPT_V("1.20.01.09") && !(songFlags & ITP_EMBEDMIDICFG)) { m_MidiCfg.Reset(); } m_modFormat.formatName = U_("Impulse Tracker Project"); m_modFormat.type = U_("itp"); m_modFormat.madeWithTracker = U_("OpenMPT ") + mpt::ufmt::val(m_dwLastSavedWithVersion); m_modFormat.charset = mpt::Charset::Windows1252; return true; #endif // MPT_EXTERNAL_SAMPLES } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/load_j2b.cpp0000644000175000017500000006736514072320411020266 00000000000000/* * load_j2b.cpp * ------------ * Purpose: RIFF AM and RIFF AMFF (Galaxy Sound System) module loader * Notes : J2B is a compressed variant of RIFF AM and RIFF AMFF files used in Jazz Jackrabbit 2. * It seems like no other game used the AM(FF) format. * RIFF AM is the newer version of the format, generally following the RIFF "standard" closely. * Authors: Johannes Schultz (OpenMPT port, reverse engineering + loader implementation of the instrument format) * kode54 (foo_dumb - this is almost a complete port of his code, thanks) * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Loaders.h" #include "mpt/io/base.hpp" #if defined(MPT_WITH_ZLIB) #include #elif defined(MPT_WITH_MINIZ) #include #endif #ifdef MPT_ALL_LOGGING #define J2B_LOG #endif OPENMPT_NAMESPACE_BEGIN // First off, a nice vibrato translation LUT. static constexpr VibratoType j2bAutoVibratoTrans[] = { VIB_SINE, VIB_SQUARE, VIB_RAMP_UP, VIB_RAMP_DOWN, VIB_RANDOM, }; // header for compressed j2b files struct J2BFileHeader { // Magic Bytes // 32-Bit J2B header identifiers enum : uint32 { magicDEADBEAF = 0xAFBEADDEu, magicDEADBABE = 0xBEBAADDEu }; char signature[4]; // MUSE uint32le deadbeaf; // 0xDEADBEAF (AM) or 0xDEADBABE (AMFF) uint32le fileLength; // complete filesize uint32le crc32; // checksum of the compressed data block uint32le packedLength; // length of the compressed data block uint32le unpackedLength; // length of the decompressed module }; MPT_BINARY_STRUCT(J2BFileHeader, 24) // AM(FF) stuff struct AMFFRiffChunk { // 32-Bit chunk identifiers enum ChunkIdentifiers { idRIFF = MagicLE("RIFF"), idAMFF = MagicLE("AMFF"), idAM__ = MagicLE("AM "), idMAIN = MagicLE("MAIN"), idINIT = MagicLE("INIT"), idORDR = MagicLE("ORDR"), idPATT = MagicLE("PATT"), idINST = MagicLE("INST"), idSAMP = MagicLE("SAMP"), idAI__ = MagicLE("AI "), idAS__ = MagicLE("AS "), }; uint32le id; // See ChunkIdentifiers uint32le length; // Chunk size without header size_t GetLength() const { return length; } ChunkIdentifiers GetID() const { return static_cast(id.get()); } }; MPT_BINARY_STRUCT(AMFFRiffChunk, 8) // This header is used for both AM's "INIT" as well as AMFF's "MAIN" chunk struct AMFFMainChunk { // Main Chunk flags enum MainFlags { amigaSlides = 0x01, }; char songname[64]; uint8le flags; uint8le channels; uint8le speed; uint8le tempo; uint16le minPeriod; // 16x Amiga periods, but we should ignore them - otherwise some high notes in Medivo.j2b won't sound correct. uint16le maxPeriod; // Ditto uint8le globalvolume; }; MPT_BINARY_STRUCT(AMFFMainChunk, 73) // AMFF instrument envelope (old format) struct AMFFEnvelope { // Envelope flags (also used for RIFF AM) enum EnvelopeFlags { envEnabled = 0x01, envSustain = 0x02, envLoop = 0x04, }; struct EnvPoint { uint16le tick; uint8le value; // 0...64 }; uint8le envFlags; // high nibble = pan env flags, low nibble = vol env flags (both nibbles work the same way) uint8le envNumPoints; // high nibble = pan env length, low nibble = vol env length uint8le envSustainPoints; // you guessed it... high nibble = pan env sustain point, low nibble = vol env sustain point uint8le envLoopStarts; // I guess you know the pattern now. uint8le envLoopEnds; // same here. EnvPoint volEnv[10]; EnvPoint panEnv[10]; // Convert weird envelope data to OpenMPT's internal format. void ConvertEnvelope(uint8 flags, uint8 numPoints, uint8 sustainPoint, uint8 loopStart, uint8 loopEnd, const EnvPoint (&points)[10], InstrumentEnvelope &mptEnv) const { // The buggy mod2j2b converter will actually NOT limit this to 10 points if the envelope is longer. mptEnv.resize(std::min(numPoints, static_cast(10))); mptEnv.nSustainStart = mptEnv.nSustainEnd = sustainPoint; mptEnv.nLoopStart = loopStart; mptEnv.nLoopEnd = loopEnd; for(uint32 i = 0; i < mptEnv.size(); i++) { mptEnv[i].tick = points[i].tick >> 4; if(i == 0) mptEnv[0].tick = 0; else if(mptEnv[i].tick < mptEnv[i - 1].tick) mptEnv[i].tick = mptEnv[i - 1].tick + 1; mptEnv[i].value = Clamp(points[i].value, 0, 64); } mptEnv.dwFlags.set(ENV_ENABLED, (flags & AMFFEnvelope::envEnabled) != 0); mptEnv.dwFlags.set(ENV_SUSTAIN, (flags & AMFFEnvelope::envSustain) && mptEnv.nSustainStart <= mptEnv.size()); mptEnv.dwFlags.set(ENV_LOOP, (flags & AMFFEnvelope::envLoop) && mptEnv.nLoopStart <= mptEnv.nLoopEnd && mptEnv.nLoopStart <= mptEnv.size()); } void ConvertToMPT(ModInstrument &mptIns) const { // interleaved envelope data... meh. gotta split it up here and decode it separately. // note: mod2j2b is BUGGY and always writes ($original_num_points & 0x0F) in the header, // but just has room for 10 envelope points. That means that long (>= 16 points) // envelopes are cut off, and envelopes have to be trimmed to 10 points, even if // the header claims that they are longer. // For XM files the number of points also appears to be off by one, // but luckily there are no official J2Bs using envelopes anyway. ConvertEnvelope(envFlags & 0x0F, envNumPoints & 0x0F, envSustainPoints & 0x0F, envLoopStarts & 0x0F, envLoopEnds & 0x0F, volEnv, mptIns.VolEnv); ConvertEnvelope(envFlags >> 4, envNumPoints >> 4, envSustainPoints >> 4, envLoopStarts >> 4, envLoopEnds >> 4, panEnv, mptIns.PanEnv); } }; MPT_BINARY_STRUCT(AMFFEnvelope::EnvPoint, 3) MPT_BINARY_STRUCT(AMFFEnvelope, 65) // AMFF instrument header (old format) struct AMFFInstrumentHeader { uint8le unknown; // 0x00 uint8le index; // actual instrument number char name[28]; uint8le numSamples; uint8le sampleMap[120]; uint8le vibratoType; uint16le vibratoSweep; uint16le vibratoDepth; uint16le vibratoRate; AMFFEnvelope envelopes; uint16le fadeout; // Convert instrument data to OpenMPT's internal format. void ConvertToMPT(ModInstrument &mptIns, SAMPLEINDEX baseSample) { mptIns.name = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, name); static_assert(mpt::array_size::size <= mpt::array_size::size); for(size_t i = 0; i < std::size(sampleMap); i++) { mptIns.Keyboard[i] = sampleMap[i] + baseSample + 1; } mptIns.nFadeOut = fadeout << 5; envelopes.ConvertToMPT(mptIns); } }; MPT_BINARY_STRUCT(AMFFInstrumentHeader, 225) // AMFF sample header (old format) struct AMFFSampleHeader { // Sample flags (also used for RIFF AM) enum SampleFlags { smp16Bit = 0x04, smpLoop = 0x08, smpPingPong = 0x10, smpPanning = 0x20, smpExists = 0x80, // some flags are still missing... what is e.g. 0x8000? }; uint32le id; // "SAMP" uint32le chunkSize; // header + sample size char name[28]; uint8le pan; uint8le volume; uint16le flags; uint32le length; uint32le loopStart; uint32le loopEnd; uint32le sampleRate; uint32le reserved1; uint32le reserved2; // Convert sample header to OpenMPT's internal format. void ConvertToMPT(AMFFInstrumentHeader &instrHeader, ModSample &mptSmp) const { mptSmp.Initialize(); mptSmp.nPan = pan * 4; mptSmp.nVolume = volume * 4; mptSmp.nGlobalVol = 64; mptSmp.nLength = length; mptSmp.nLoopStart = loopStart; mptSmp.nLoopEnd = loopEnd; mptSmp.nC5Speed = sampleRate; if(instrHeader.vibratoType < std::size(j2bAutoVibratoTrans)) mptSmp.nVibType = j2bAutoVibratoTrans[instrHeader.vibratoType]; mptSmp.nVibSweep = static_cast(instrHeader.vibratoSweep); mptSmp.nVibRate = static_cast(instrHeader.vibratoRate / 16); mptSmp.nVibDepth = static_cast(instrHeader.vibratoDepth / 4); if((mptSmp.nVibRate | mptSmp.nVibDepth) != 0) { // Convert XM-style vibrato sweep to IT mptSmp.nVibSweep = 255 - mptSmp.nVibSweep; } if(flags & AMFFSampleHeader::smp16Bit) mptSmp.uFlags.set(CHN_16BIT); if(flags & AMFFSampleHeader::smpLoop) mptSmp.uFlags.set(CHN_LOOP); if(flags & AMFFSampleHeader::smpPingPong) mptSmp.uFlags.set(CHN_PINGPONGLOOP); if(flags & AMFFSampleHeader::smpPanning) mptSmp.uFlags.set(CHN_PANNING); } // Retrieve the internal sample format flags for this sample. SampleIO GetSampleFormat() const { return SampleIO( (flags & AMFFSampleHeader::smp16Bit) ? SampleIO::_16bit : SampleIO::_8bit, SampleIO::mono, SampleIO::littleEndian, SampleIO::signedPCM); } }; MPT_BINARY_STRUCT(AMFFSampleHeader, 64) // AM instrument envelope (new format) struct AMEnvelope { struct EnvPoint { uint16le tick; int16le value; }; uint16le flags; uint8le numPoints; // actually, it's num. points - 1, and 0xFF if there is no envelope uint8le sustainPoint; uint8le loopStart; uint8le loopEnd; EnvPoint values[10]; uint16le fadeout; // why is this here? it's only needed for the volume envelope... // Convert envelope data to OpenMPT's internal format. void ConvertToMPT(InstrumentEnvelope &mptEnv, EnvelopeType envType) const { if(numPoints == 0xFF || numPoints == 0) return; mptEnv.resize(std::min(numPoints + 1, 10)); mptEnv.nSustainStart = mptEnv.nSustainEnd = sustainPoint; mptEnv.nLoopStart = loopStart; mptEnv.nLoopEnd = loopEnd; int32 scale = 0, offset = 0; switch(envType) { case ENV_VOLUME: // 0....32767 default: scale = 32767 / ENVELOPE_MAX; break; case ENV_PITCH: // -4096....4096 scale = 8192 / ENVELOPE_MAX; offset = 4096; break; case ENV_PANNING: // -32768...32767 scale = 65536 / ENVELOPE_MAX; offset = 32768; break; } for(uint32 i = 0; i < mptEnv.size(); i++) { mptEnv[i].tick = values[i].tick >> 4; if(i == 0) mptEnv[i].tick = 0; else if(mptEnv[i].tick < mptEnv[i - 1].tick) mptEnv[i].tick = mptEnv[i - 1].tick + 1; int32 val = values[i].value + offset; val = (val + scale / 2) / scale; mptEnv[i].value = static_cast(std::clamp(val, int32(ENVELOPE_MIN), int32(ENVELOPE_MAX))); } mptEnv.dwFlags.set(ENV_ENABLED, (flags & AMFFEnvelope::envEnabled) != 0); mptEnv.dwFlags.set(ENV_SUSTAIN, (flags & AMFFEnvelope::envSustain) && mptEnv.nSustainStart <= mptEnv.size()); mptEnv.dwFlags.set(ENV_LOOP, (flags & AMFFEnvelope::envLoop) && mptEnv.nLoopStart <= mptEnv.nLoopEnd && mptEnv.nLoopStart <= mptEnv.size()); } }; MPT_BINARY_STRUCT(AMEnvelope::EnvPoint, 4) MPT_BINARY_STRUCT(AMEnvelope, 48) // AM instrument header (new format) struct AMInstrumentHeader { uint32le headSize; // Header size (i.e. the size of this struct) uint8le unknown1; // 0x00 uint8le index; // Actual instrument number char name[32]; uint8le sampleMap[128]; uint8le vibratoType; uint16le vibratoSweep; uint16le vibratoDepth; uint16le vibratoRate; uint8le unknown2[7]; AMEnvelope volEnv; AMEnvelope pitchEnv; AMEnvelope panEnv; uint16le numSamples; // Convert instrument data to OpenMPT's internal format. void ConvertToMPT(ModInstrument &mptIns, SAMPLEINDEX baseSample) { mptIns.name = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, name); static_assert(mpt::array_size::size <= mpt::array_size::size); for(uint8 i = 0; i < std::size(sampleMap); i++) { mptIns.Keyboard[i] = sampleMap[i] + baseSample + 1; } mptIns.nFadeOut = volEnv.fadeout << 5; volEnv.ConvertToMPT(mptIns.VolEnv, ENV_VOLUME); pitchEnv.ConvertToMPT(mptIns.PitchEnv, ENV_PITCH); panEnv.ConvertToMPT(mptIns.PanEnv, ENV_PANNING); if(numSamples == 0) { MemsetZero(mptIns.Keyboard); } } }; MPT_BINARY_STRUCT(AMInstrumentHeader, 326) // AM sample header (new format) struct AMSampleHeader { uint32le headSize; // Header size (i.e. the size of this struct), apparently not including headSize. char name[32]; uint16le pan; uint16le volume; uint16le flags; uint16le unknown; // 0x0000 / 0x0080? uint32le length; uint32le loopStart; uint32le loopEnd; uint32le sampleRate; // Convert sample header to OpenMPT's internal format. void ConvertToMPT(AMInstrumentHeader &instrHeader, ModSample &mptSmp) const { mptSmp.Initialize(); mptSmp.nPan = std::min(pan.get(), uint16(32767)) * 256 / 32767; mptSmp.nVolume = std::min(volume.get(), uint16(32767)) * 256 / 32767; mptSmp.nGlobalVol = 64; mptSmp.nLength = length; mptSmp.nLoopStart = loopStart; mptSmp.nLoopEnd = loopEnd; mptSmp.nC5Speed = sampleRate; if(instrHeader.vibratoType < std::size(j2bAutoVibratoTrans)) mptSmp.nVibType = j2bAutoVibratoTrans[instrHeader.vibratoType]; mptSmp.nVibSweep = static_cast(instrHeader.vibratoSweep); mptSmp.nVibRate = static_cast(instrHeader.vibratoRate / 16); mptSmp.nVibDepth = static_cast(instrHeader.vibratoDepth / 4); if((mptSmp.nVibRate | mptSmp.nVibDepth) != 0) { // Convert XM-style vibrato sweep to IT mptSmp.nVibSweep = 255 - mptSmp.nVibSweep; } if(flags & AMFFSampleHeader::smp16Bit) mptSmp.uFlags.set(CHN_16BIT); if(flags & AMFFSampleHeader::smpLoop) mptSmp.uFlags.set(CHN_LOOP); if(flags & AMFFSampleHeader::smpPingPong) mptSmp.uFlags.set(CHN_PINGPONGLOOP); if(flags & AMFFSampleHeader::smpPanning) mptSmp.uFlags.set(CHN_PANNING); } // Retrieve the internal sample format flags for this sample. SampleIO GetSampleFormat() const { return SampleIO( (flags & AMFFSampleHeader::smp16Bit) ? SampleIO::_16bit : SampleIO::_8bit, SampleIO::mono, SampleIO::littleEndian, SampleIO::signedPCM); } }; MPT_BINARY_STRUCT(AMSampleHeader, 60) // Convert RIFF AM(FF) pattern data to MPT pattern data. static bool ConvertAMPattern(FileReader chunk, PATTERNINDEX pat, bool isAM, CSoundFile &sndFile) { // Effect translation LUT static constexpr EffectCommand amEffTrans[] = { CMD_ARPEGGIO, CMD_PORTAMENTOUP, CMD_PORTAMENTODOWN, CMD_TONEPORTAMENTO, CMD_VIBRATO, CMD_TONEPORTAVOL, CMD_VIBRATOVOL, CMD_TREMOLO, CMD_PANNING8, CMD_OFFSET, CMD_VOLUMESLIDE, CMD_POSITIONJUMP, CMD_VOLUME, CMD_PATTERNBREAK, CMD_MODCMDEX, CMD_TEMPO, CMD_GLOBALVOLUME, CMD_GLOBALVOLSLIDE, CMD_KEYOFF, CMD_SETENVPOSITION, CMD_CHANNELVOLUME, CMD_CHANNELVOLSLIDE, CMD_PANNINGSLIDE, CMD_RETRIG, CMD_TREMOR, CMD_XFINEPORTAUPDOWN, }; enum { rowDone = 0, // Advance to next row channelMask = 0x1F, // Mask for retrieving channel information volFlag = 0x20, // Volume effect present noteFlag = 0x40, // Note + instr present effectFlag = 0x80, // Effect information present dataFlag = 0xE0, // Channel data present }; if(chunk.NoBytesLeft()) { return false; } ROWINDEX numRows = Clamp(static_cast(chunk.ReadUint8()) + 1, ROWINDEX(1), MAX_PATTERN_ROWS); if(!sndFile.Patterns.Insert(pat, numRows)) return false; const CHANNELINDEX channels = sndFile.GetNumChannels(); if(channels == 0) return false; ROWINDEX row = 0; while(row < numRows && chunk.CanRead(1)) { const uint8 flags = chunk.ReadUint8(); if(flags == rowDone) { row++; continue; } ModCommand &m = *sndFile.Patterns[pat].GetpModCommand(row, std::min(static_cast(flags & channelMask), static_cast(channels - 1))); if(flags & dataFlag) { if(flags & effectFlag) // effect { m.param = chunk.ReadUint8(); uint8 command = chunk.ReadUint8(); if(command < std::size(amEffTrans)) { // command translation m.command = amEffTrans[command]; } else { #ifdef J2B_LOG MPT_LOG_GLOBAL(LogDebug, "J2B", MPT_UFORMAT("J2B: Unknown command: 0x{}, param 0x{}")(mpt::ufmt::HEX0<2>(command), mpt::ufmt::HEX0<2>(m.param))); #endif m.command = CMD_NONE; } // Handling special commands switch(m.command) { case CMD_ARPEGGIO: if(m.param == 0) m.command = CMD_NONE; break; case CMD_VOLUME: if(m.volcmd == VOLCMD_NONE) { m.volcmd = VOLCMD_VOLUME; m.vol = Clamp(m.param, uint8(0), uint8(64)); m.command = CMD_NONE; m.param = 0; } break; case CMD_TONEPORTAVOL: case CMD_VIBRATOVOL: case CMD_VOLUMESLIDE: case CMD_GLOBALVOLSLIDE: case CMD_PANNINGSLIDE: if (m.param & 0xF0) m.param &= 0xF0; break; case CMD_PANNING8: if(m.param <= 0x80) m.param = mpt::saturate_cast(m.param * 2); else if(m.param == 0xA4) {m.command = CMD_S3MCMDEX; m.param = 0x91;} break; case CMD_PATTERNBREAK: m.param = ((m.param >> 4) * 10) + (m.param & 0x0F); break; case CMD_MODCMDEX: m.ExtendedMODtoS3MEffect(); break; case CMD_TEMPO: if(m.param <= 0x1F) m.command = CMD_SPEED; break; case CMD_XFINEPORTAUPDOWN: switch(m.param & 0xF0) { case 0x10: m.command = CMD_PORTAMENTOUP; break; case 0x20: m.command = CMD_PORTAMENTODOWN; break; } m.param = (m.param & 0x0F) | 0xE0; break; } } if (flags & noteFlag) // note + ins { const auto [instr, note] = chunk.ReadArray(); m.instr = instr; m.note = note; if(m.note == 0x80) m.note = NOTE_KEYOFF; else if(m.note > 0x80) m.note = NOTE_FADE; // I guess the support for IT "note fade" notes was not intended in mod2j2b, but hey, it works! :-D } if (flags & volFlag) // volume { m.volcmd = VOLCMD_VOLUME; m.vol = chunk.ReadUint8(); if(isAM) { m.vol = m.vol * 64 / 127; } } } } return true; } struct AMFFRiffChunkFormat { uint32le format; }; MPT_BINARY_STRUCT(AMFFRiffChunkFormat, 4) static bool ValidateHeader(const AMFFRiffChunk &fileHeader) { if(fileHeader.id != AMFFRiffChunk::idRIFF) { return false; } if(fileHeader.GetLength() < 8 + sizeof(AMFFMainChunk)) { return false; } return true; } static bool ValidateHeader(const AMFFRiffChunkFormat &formatHeader) { if(formatHeader.format != AMFFRiffChunk::idAMFF && formatHeader.format != AMFFRiffChunk::idAM__) { return false; } return true; } CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderAM(MemoryFileReader file, const uint64 *pfilesize) { AMFFRiffChunk fileHeader; if(!file.ReadStruct(fileHeader)) { return ProbeWantMoreData; } if(!ValidateHeader(fileHeader)) { return ProbeFailure; } AMFFRiffChunkFormat formatHeader; if(!file.ReadStruct(formatHeader)) { return ProbeWantMoreData; } if(!ValidateHeader(formatHeader)) { return ProbeFailure; } MPT_UNREFERENCED_PARAMETER(pfilesize); return ProbeSuccess; } bool CSoundFile::ReadAM(FileReader &file, ModLoadingFlags loadFlags) { file.Rewind(); AMFFRiffChunk fileHeader; if(!file.ReadStruct(fileHeader)) { return false; } if(!ValidateHeader(fileHeader)) { return false; } AMFFRiffChunkFormat formatHeader; if(!file.ReadStruct(formatHeader)) { return false; } if(!ValidateHeader(formatHeader)) { return false; } bool isAM; // false: AMFF, true: AM uint32 format = formatHeader.format; if(format == AMFFRiffChunk::idAMFF) isAM = false; // "AMFF" else if(format == AMFFRiffChunk::idAM__) isAM = true; // "AM " else return false; ChunkReader chunkFile(file); // The main chunk is almost identical in both formats but uses different chunk IDs. // "MAIN" - Song info (AMFF) // "INIT" - Song info (AM) AMFFRiffChunk::ChunkIdentifiers mainChunkID = isAM ? AMFFRiffChunk::idINIT : AMFFRiffChunk::idMAIN; // RIFF AM has a padding byte so that all chunks have an even size. ChunkReader::ChunkList chunks; if(loadFlags == onlyVerifyHeader) chunks = chunkFile.ReadChunksUntil(isAM ? 2 : 1, mainChunkID); else chunks = chunkFile.ReadChunks(isAM ? 2 : 1); FileReader chunkMain(chunks.GetChunk(mainChunkID)); AMFFMainChunk mainChunk; if(!chunkMain.IsValid() || !chunkMain.ReadStruct(mainChunk) || mainChunk.channels < 1 || !chunkMain.CanRead(mainChunk.channels)) { return false; } else if(loadFlags == onlyVerifyHeader) { return true; } InitializeGlobals(MOD_TYPE_J2B); m_SongFlags = SONG_ITOLDEFFECTS | SONG_ITCOMPATGXX; m_SongFlags.set(SONG_LINEARSLIDES, !(mainChunk.flags & AMFFMainChunk::amigaSlides)); m_nChannels = std::min(static_cast(mainChunk.channels), static_cast(MAX_BASECHANNELS)); m_nDefaultSpeed = mainChunk.speed; m_nDefaultTempo.Set(mainChunk.tempo); m_nDefaultGlobalVolume = mainChunk.globalvolume * 2; m_modFormat.formatName = isAM ? UL_("Galaxy Sound System (new version)") : UL_("Galaxy Sound System (old version)"); m_modFormat.type = U_("j2b"); m_modFormat.charset = mpt::Charset::CP437; m_songName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, mainChunk.songname); // It seems like there's no way to differentiate between // Muted and Surround channels (they're all 0xA0) - might // be a limitation in mod2j2b. for(CHANNELINDEX nChn = 0; nChn < m_nChannels; nChn++) { ChnSettings[nChn].Reset(); uint8 pan = chunkMain.ReadUint8(); if(isAM) { if(pan > 128) ChnSettings[nChn].dwFlags = CHN_MUTE; else ChnSettings[nChn].nPan = pan * 2; } else { if(pan >= 128) ChnSettings[nChn].dwFlags = CHN_MUTE; else ChnSettings[nChn].nPan = static_cast(std::min(pan * 4, 256)); } } if(chunks.ChunkExists(AMFFRiffChunk::idORDR)) { // "ORDR" - Order list FileReader chunk(chunks.GetChunk(AMFFRiffChunk::idORDR)); uint8 numOrders = chunk.ReadUint8() + 1; ReadOrderFromFile(Order(), chunk, numOrders, 0xFF, 0xFE); } // "PATT" - Pattern data for one pattern if(loadFlags & loadPatternData) { PATTERNINDEX maxPattern = 0; auto pattChunks = chunks.GetAllChunks(AMFFRiffChunk::idPATT); Patterns.ResizeArray(static_cast(pattChunks.size())); for(auto chunk : pattChunks) { PATTERNINDEX pat = chunk.ReadUint8(); size_t patternSize = chunk.ReadUint32LE(); ConvertAMPattern(chunk.ReadChunk(patternSize), pat, isAM, *this); maxPattern = std::max(maxPattern, pat); } for(PATTERNINDEX pat = 0; pat < maxPattern; pat++) { if(!Patterns.IsValidPat(pat)) Patterns.Insert(pat, 64); } } if(!isAM) { // "INST" - Instrument (only in RIFF AMFF) auto instChunks = chunks.GetAllChunks(AMFFRiffChunk::idINST); for(auto chunk : instChunks) { AMFFInstrumentHeader instrHeader; if(!chunk.ReadStruct(instrHeader)) { continue; } const INSTRUMENTINDEX instr = instrHeader.index + 1; if(instr >= MAX_INSTRUMENTS) continue; ModInstrument *pIns = AllocateInstrument(instr); if(pIns == nullptr) { continue; } instrHeader.ConvertToMPT(*pIns, m_nSamples); // read sample sub-chunks - this is a rather "flat" format compared to RIFF AM and has no nested RIFF chunks. for(size_t samples = 0; samples < instrHeader.numSamples; samples++) { AMFFSampleHeader sampleHeader; if(!CanAddMoreSamples() || !chunk.ReadStruct(sampleHeader)) { continue; } const SAMPLEINDEX smp = ++m_nSamples; if(sampleHeader.id != AMFFRiffChunk::idSAMP) { continue; } m_szNames[smp] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, sampleHeader.name); sampleHeader.ConvertToMPT(instrHeader, Samples[smp]); if(loadFlags & loadSampleData) sampleHeader.GetSampleFormat().ReadSample(Samples[smp], chunk); else chunk.Skip(Samples[smp].GetSampleSizeInBytes()); } } } else { // "RIFF" - Instrument (only in RIFF AM) auto instChunks = chunks.GetAllChunks(AMFFRiffChunk::idRIFF); for(ChunkReader chunk : instChunks) { if(chunk.ReadUint32LE() != AMFFRiffChunk::idAI__) { continue; } AMFFRiffChunk instChunk; if(!chunk.ReadStruct(instChunk) || instChunk.id != AMFFRiffChunk::idINST) { continue; } AMInstrumentHeader instrHeader; if(!chunk.ReadStruct(instrHeader)) { continue; } MPT_ASSERT(instrHeader.headSize + 4 == sizeof(instrHeader)); const INSTRUMENTINDEX instr = instrHeader.index + 1; if(instr >= MAX_INSTRUMENTS) continue; ModInstrument *pIns = AllocateInstrument(instr); if(pIns == nullptr) { continue; } instrHeader.ConvertToMPT(*pIns, m_nSamples); // Read sample sub-chunks (RIFF nesting ftw) auto sampleChunks = chunk.ReadChunks(2).GetAllChunks(AMFFRiffChunk::idRIFF); MPT_ASSERT(sampleChunks.size() == instrHeader.numSamples); for(auto sampleChunk : sampleChunks) { if(sampleChunk.ReadUint32LE() != AMFFRiffChunk::idAS__ || !CanAddMoreSamples()) { continue; } // Don't read more samples than the instrument header claims to have. if((instrHeader.numSamples--) == 0) { break; } const SAMPLEINDEX smp = ++m_nSamples; // Aaand even more nested chunks! Great, innit? AMFFRiffChunk sampleHeaderChunk; if(!sampleChunk.ReadStruct(sampleHeaderChunk) || sampleHeaderChunk.id != AMFFRiffChunk::idSAMP) { break; } FileReader sampleFileChunk = sampleChunk.ReadChunk(sampleHeaderChunk.length); AMSampleHeader sampleHeader; if(!sampleFileChunk.ReadStruct(sampleHeader)) { break; } m_szNames[smp] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, sampleHeader.name); sampleHeader.ConvertToMPT(instrHeader, Samples[smp]); if(loadFlags & loadSampleData) { sampleFileChunk.Seek(sampleHeader.headSize + 4); sampleHeader.GetSampleFormat().ReadSample(Samples[smp], sampleFileChunk); } } } } return true; } static bool ValidateHeader(const J2BFileHeader &fileHeader) { if(std::memcmp(fileHeader.signature, "MUSE", 4) || (fileHeader.deadbeaf != J2BFileHeader::magicDEADBEAF // 0xDEADBEAF (RIFF AM) && fileHeader.deadbeaf != J2BFileHeader::magicDEADBABE) // 0xDEADBABE (RIFF AMFF) ) { return false; } if(fileHeader.packedLength == 0) { return false; } if(fileHeader.fileLength != fileHeader.packedLength + sizeof(J2BFileHeader)) { return false; } return true; } static bool ValidateHeaderFileSize(const J2BFileHeader &fileHeader, uint64 filesize) { if(filesize != fileHeader.fileLength) { return false; } return true; } CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderJ2B(MemoryFileReader file, const uint64 *pfilesize) { J2BFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return ProbeWantMoreData; } if(!ValidateHeader(fileHeader)) { return ProbeFailure; } if(pfilesize) { if(!ValidateHeaderFileSize(fileHeader, *pfilesize)) { return ProbeFailure; } } MPT_UNREFERENCED_PARAMETER(pfilesize); return ProbeSuccess; } bool CSoundFile::ReadJ2B(FileReader &file, ModLoadingFlags loadFlags) { #if !defined(MPT_WITH_ZLIB) && !defined(MPT_WITH_MINIZ) MPT_UNREFERENCED_PARAMETER(file); MPT_UNREFERENCED_PARAMETER(loadFlags); return false; #else file.Rewind(); J2BFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return false; } if(!ValidateHeader(fileHeader)) { return false; } if(fileHeader.fileLength != file.GetLength() || fileHeader.packedLength != file.BytesLeft() ) { return false; } if(loadFlags == onlyVerifyHeader) { return true; } // Header is valid, now unpack the RIFF AM file using inflate z_stream strm{}; if(inflateInit(&strm) != Z_OK) return false; uint32 remainRead = fileHeader.packedLength, remainWrite = fileHeader.unpackedLength, totalWritten = 0; uint32 crc = 0; std::vector amFileData(remainWrite); int retVal = Z_OK; while(remainRead && remainWrite && retVal != Z_STREAM_END) { Bytef buffer[mpt::IO::BUFFERSIZE_TINY]; uint32 readSize = std::min(static_cast(sizeof(buffer)), remainRead); file.ReadRaw(mpt::span(buffer, readSize)); crc = crc32(crc, buffer, readSize); strm.avail_in = readSize; strm.next_in = buffer; do { strm.avail_out = remainWrite; strm.next_out = amFileData.data() + totalWritten; retVal = inflate(&strm, Z_NO_FLUSH); uint32 written = remainWrite - strm.avail_out; totalWritten += written; remainWrite -= written; } while(remainWrite && strm.avail_out == 0); remainRead -= readSize; } inflateEnd(&strm); bool result = false; #ifndef MPT_BUILD_FUZZER if(fileHeader.crc32 == crc && !remainWrite && retVal == Z_STREAM_END) #endif { // Success, now load the RIFF AM(FF) module. FileReader amFile(mpt::as_span(amFileData)); result = ReadAM(amFile, loadFlags); } return result; #endif } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Load_mdl.cpp0000644000175000017500000005152414137251202020316 00000000000000/* * Load_mdl.cpp * ------------ * Purpose: Digitrakker (MDL) module loader * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Loaders.h" OPENMPT_NAMESPACE_BEGIN // MDL file header struct MDLFileHeader { char id[4]; // "DMDL" uint8 version; }; MPT_BINARY_STRUCT(MDLFileHeader, 5) // RIFF-style Chunk struct MDLChunk { // 16-Bit chunk identifiers enum ChunkIdentifiers { idInfo = MagicLE("IN"), idMessage = MagicLE("ME"), idPats = MagicLE("PA"), idPatNames = MagicLE("PN"), idTracks = MagicLE("TR"), idInstrs = MagicLE("II"), idVolEnvs = MagicLE("VE"), idPanEnvs = MagicLE("PE"), idFreqEnvs = MagicLE("FE"), idSampleInfo = MagicLE("IS"), ifSampleData = MagicLE("SA"), }; uint16le id; uint32le length; size_t GetLength() const { return length; } ChunkIdentifiers GetID() const { return static_cast(id.get()); } }; MPT_BINARY_STRUCT(MDLChunk, 6) struct MDLInfoBlock { char title[32]; char composer[20]; uint16le numOrders; uint16le restartPos; uint8le globalVol; // 1...255 uint8le speed; // 1...255 uint8le tempo; // 4...255 uint8le chnSetup[32]; }; MPT_BINARY_STRUCT(MDLInfoBlock, 91) // Sample header in II block struct MDLSampleHeader { uint8le smpNum; uint8le lastNote; uint8le volume; uint8le volEnvFlags; // 6 bits env #, 2 bits flags uint8le panning; uint8le panEnvFlags; uint16le fadeout; uint8le vibSpeed; uint8le vibDepth; uint8le vibSweep; uint8le vibType; uint8le reserved; // zero uint8le freqEnvFlags; }; MPT_BINARY_STRUCT(MDLSampleHeader, 14) struct MDLEnvelope { uint8 envNum; struct { uint8 x; // Delta value from last point, 0 means no more points defined uint8 y; // 0...63 } nodes[15]; uint8 flags; uint8 loop; // Lower 4 bits = start, upper 4 bits = end void ConvertToMPT(InstrumentEnvelope &mptEnv) const { mptEnv.dwFlags.reset(); mptEnv.clear(); mptEnv.reserve(15); int16 tick = -nodes[0].x; for(uint8 n = 0; n < 15; n++) { if(!nodes[n].x) break; tick += nodes[n].x; mptEnv.push_back(EnvelopeNode(tick, std::min(nodes[n].y, uint8(64)))); // actually 0-63 } mptEnv.nLoopStart = (loop & 0x0F); mptEnv.nLoopEnd = (loop >> 4); mptEnv.nSustainStart = mptEnv.nSustainEnd = (flags & 0x0F); if(flags & 0x10) mptEnv.dwFlags.set(ENV_SUSTAIN); if(flags & 0x20) mptEnv.dwFlags.set(ENV_LOOP); } }; MPT_BINARY_STRUCT(MDLEnvelope, 33) struct MDLPatternHeader { uint8le channels; uint8le lastRow; char name[16]; }; MPT_BINARY_STRUCT(MDLPatternHeader, 18) enum { MDLNOTE_NOTE = 1 << 0, MDLNOTE_SAMPLE = 1 << 1, MDLNOTE_VOLUME = 1 << 2, MDLNOTE_EFFECTS = 1 << 3, MDLNOTE_PARAM1 = 1 << 4, MDLNOTE_PARAM2 = 1 << 5, }; static constexpr VibratoType MDLVibratoType[] = { VIB_SINE, VIB_RAMP_DOWN, VIB_SQUARE, VIB_SINE }; static constexpr ModCommand::COMMAND MDLEffTrans[] = { /* 0 */ CMD_NONE, /* 1st column only */ /* 1 */ CMD_PORTAMENTOUP, /* 2 */ CMD_PORTAMENTODOWN, /* 3 */ CMD_TONEPORTAMENTO, /* 4 */ CMD_VIBRATO, /* 5 */ CMD_ARPEGGIO, /* 6 */ CMD_NONE, /* Either column */ /* 7 */ CMD_TEMPO, /* 8 */ CMD_PANNING8, /* 9 */ CMD_SETENVPOSITION, /* A */ CMD_NONE, /* B */ CMD_POSITIONJUMP, /* C */ CMD_GLOBALVOLUME, /* D */ CMD_PATTERNBREAK, /* E */ CMD_S3MCMDEX, /* F */ CMD_SPEED, /* 2nd column only */ /* G */ CMD_VOLUMESLIDE, // up /* H */ CMD_VOLUMESLIDE, // down /* I */ CMD_RETRIG, /* J */ CMD_TREMOLO, /* K */ CMD_TREMOR, /* L */ CMD_NONE, }; // receive an MDL effect, give back a 'normal' one. static void ConvertMDLCommand(uint8 &cmd, uint8 ¶m) { if(cmd >= std::size(MDLEffTrans)) return; uint8 origCmd = cmd; cmd = MDLEffTrans[cmd]; switch(origCmd) { #ifdef MODPLUG_TRACKER case 0x07: // Tempo // MDL supports any nonzero tempo value, but OpenMPT doesn't param = std::max(param, uint8(0x20)); break; #endif // MODPLUG_TRACKER case 0x08: // Panning param = (param & 0x7F) * 2u; break; case 0x0C: // Global volume param = (param + 1) / 2u; break; case 0x0D: // Pattern Break // Convert from BCD param = 10 * (param >> 4) + (param & 0x0F); break; case 0x0E: // Special switch(param >> 4) { case 0x0: // unused case 0x3: // unused case 0x8: // Set Samplestatus (loop type) cmd = CMD_NONE; break; case 0x1: // Pan Slide Left cmd = CMD_PANNINGSLIDE; param = (std::min(static_cast(param & 0x0F), uint8(0x0E)) << 4) | 0x0F; break; case 0x2: // Pan Slide Right cmd = CMD_PANNINGSLIDE; param = 0xF0 | std::min(static_cast(param & 0x0F), uint8(0x0E)); break; case 0x4: // Vibrato Waveform param = 0x30 | (param & 0x0F); break; case 0x5: // Set Finetune cmd = CMD_FINETUNE; param = (param << 4) ^ 0x80; break; case 0x6: // Pattern Loop param = 0xB0 | (param & 0x0F); break; case 0x7: // Tremolo Waveform param = 0x40 | (param & 0x0F); break; case 0x9: // Retrig cmd = CMD_RETRIG; param &= 0x0F; break; case 0xA: // Global vol slide up cmd = CMD_GLOBALVOLSLIDE; param = 0xF0 & (((param & 0x0F) + 1) << 3); break; case 0xB: // Global vol slide down cmd = CMD_GLOBALVOLSLIDE; param = ((param & 0x0F) + 1) >> 1; break; case 0xC: // Note cut case 0xD: // Note delay case 0xE: // Pattern delay // Nothing to change here break; case 0xF: // Offset -- further mangled later. cmd = CMD_OFFSET; break; } break; case 0x10: // Volslide up if(param < 0xE0) { // 00...DF regular slide - four times more precise than in XM param >>= 2; if(param > 0x0F) param = 0x0F; param <<= 4; } else if(param < 0xF0) { // E0...EF extra fine slide (on first tick, 4 times finer) param = (((param & 0x0F) << 2) | 0x0F); } else { // F0...FF regular fine slide (on first tick) - like in XM param = ((param << 4) | 0x0F); } break; case 0x11: // Volslide down if(param < 0xE0) { // 00...DF regular slide - four times more precise than in XM param >>= 2; if(param > 0x0F) param = 0x0F; } else if(param < 0xF0) { // E0...EF extra fine slide (on first tick, 4 times finer) param = (((param & 0x0F) >> 2) | 0xF0); } else { // F0...FF regular fine slide (on first tick) - like in XM } break; } } // Returns true if command was lost static bool ImportMDLCommands(ModCommand &m, uint8 vol, uint8 e1, uint8 e2, uint8 p1, uint8 p2) { // Map second effect values 1-6 to effects G-L if(e2 >= 1 && e2 <= 6) e2 += 15; ConvertMDLCommand(e1, p1); ConvertMDLCommand(e2, p2); /* From the Digitrakker documentation: * EFx -xx - Set Sample Offset This is a double-command. It starts the sample at adress xxx*256. Example: C-5 01 -- EF1 -23 ->starts sample 01 at address 12300 (in hex). Kind of screwy, but I guess it's better than the mess required to do it with IT (which effectively requires 3 rows in order to set the offset past 0xff00). If we had access to the entire track, we *might* be able to shove the high offset SAy into surrounding rows (or 2x MPTM #xx), but it wouldn't always be possible, it'd make the loader a lot uglier, and generally would be more trouble than it'd be worth to implement. What's more is, if there's another effect in the second column, it's ALSO processed in addition to the offset, and the second data byte is shared between the two effects. */ uint32 offset = uint32_max; uint8 otherCmd = CMD_NONE; if(e1 == CMD_OFFSET) { // EFy -xx => offset yxx00 offset = ((p1 & 0x0F) << 8) | p2; p1 = (p1 & 0x0F) ? 0xFF : p2; if(e2 == CMD_OFFSET) e2 = CMD_NONE; else otherCmd = e2; } else if (e2 == CMD_OFFSET) { // --- EFy => offset y0000 offset = (p2 & 0x0F) << 8; p2 = (p2 & 0x0F) ? 0xFF : 0; otherCmd = e1; } if(offset != uint32_max && offset > 0xFF && ModCommand::GetEffectWeight(otherCmd) < ModCommand::GetEffectWeight(CMD_OFFSET)) { m.command = CMD_OFFSET; m.param = static_cast(offset & 0xFF); m.volcmd = VOLCMD_OFFSET; m.vol = static_cast(offset >> 8); return otherCmd != CMD_NONE || vol != 0; } if(vol) { m.volcmd = VOLCMD_VOLUME; m.vol = (vol + 2) / 4u; } // If we have Dxx + G00, or Dxx + H00, combine them into Lxx/Kxx. ModCommand::CombineEffects(e1, p1, e2, p2); bool lostCommand = false; // Try to fit the "best" effect into e2. if(e1 == CMD_NONE) { // Easy } else if(e2 == CMD_NONE) { // Almost as easy e2 = e1; p2 = p1; e1 = CMD_NONE; } else if(e1 == e2 && e1 != CMD_S3MCMDEX) { // Digitrakker processes the effects left-to-right, so if both effects are the same, the // second essentially overrides the first. e1 = CMD_NONE; } else if(!vol) { lostCommand |= (ModCommand::TwoRegularCommandsToMPT(e1, p1, e2, p2).first != CMD_NONE); m.volcmd = e1; m.vol = p1; } else { if(ModCommand::GetEffectWeight((ModCommand::COMMAND)e1) > ModCommand::GetEffectWeight((ModCommand::COMMAND)e2)) { std::swap(e1, e2); std::swap(p1, p2); } } m.command = e2; m.param = p2; return lostCommand; } static void MDLReadEnvelopes(FileReader file, std::vector &envelopes) { if(!file.CanRead(1)) return; envelopes.resize(64); uint8 numEnvs = file.ReadUint8(); while(numEnvs--) { MDLEnvelope mdlEnv; if(!file.ReadStruct(mdlEnv) || mdlEnv.envNum > 63) continue; envelopes[mdlEnv.envNum] = mdlEnv; } } static void CopyEnvelope(InstrumentEnvelope &mptEnv, uint8 flags, std::vector &envelopes) { uint8 envNum = flags & 0x3F; if(envNum < envelopes.size()) envelopes[envNum].ConvertToMPT(mptEnv); mptEnv.dwFlags.set(ENV_ENABLED, (flags & 0x80) && !mptEnv.empty()); } static bool ValidateHeader(const MDLFileHeader &fileHeader) { if(std::memcmp(fileHeader.id, "DMDL", 4) || fileHeader.version >= 0x20) { return false; } return true; } CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderMDL(MemoryFileReader file, const uint64 *pfilesize) { MDLFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return ProbeWantMoreData; } if(!ValidateHeader(fileHeader)) { return ProbeFailure; } MPT_UNREFERENCED_PARAMETER(pfilesize); return ProbeSuccess; } bool CSoundFile::ReadMDL(FileReader &file, ModLoadingFlags loadFlags) { file.Rewind(); MDLFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return false; } if(!ValidateHeader(fileHeader)) { return false; } if(loadFlags == onlyVerifyHeader) { return true; } ChunkReader chunkFile(file); ChunkReader::ChunkList chunks = chunkFile.ReadChunks(0); // Read global info FileReader chunk = chunks.GetChunk(MDLChunk::idInfo); MDLInfoBlock info; if(!chunk.IsValid() || !chunk.ReadStruct(info)) { return false; } InitializeGlobals(MOD_TYPE_MDL); m_SongFlags = SONG_ITCOMPATGXX; m_playBehaviour.set(kPerChannelGlobalVolSlide); m_playBehaviour.set(kApplyOffsetWithoutNote); m_playBehaviour.reset(kITVibratoTremoloPanbrello); m_playBehaviour.reset(kITSCxStopsSample); // Gate effect in underbeat.mdl m_modFormat.formatName = U_("Digitrakker"); m_modFormat.type = U_("mdl"); m_modFormat.madeWithTracker = U_("Digitrakker ") + ( (fileHeader.version == 0x11) ? U_("3") // really could be 2.99b - close enough : (fileHeader.version == 0x10) ? U_("2.3") : (fileHeader.version == 0x00) ? U_("2.0 - 2.2b") // there was no 1.x release : U_("")); m_modFormat.charset = mpt::Charset::CP437; m_songName = mpt::String::ReadBuf(mpt::String::spacePadded, info.title); m_songArtist = mpt::ToUnicode(mpt::Charset::CP437, mpt::String::ReadBuf(mpt::String::spacePadded, info.composer)); m_nDefaultGlobalVolume = info.globalVol + 1; m_nDefaultSpeed = Clamp(info.speed, 1, 255); m_nDefaultTempo.Set(Clamp(info.tempo, 4, 255)); ReadOrderFromFile(Order(), chunk, info.numOrders); Order().SetRestartPos(info.restartPos); m_nChannels = 0; for(CHANNELINDEX c = 0; c < 32; c++) { ChnSettings[c].Reset(); ChnSettings[c].nPan = (info.chnSetup[c] & 0x7F) * 2u; if(ChnSettings[c].nPan == 254) ChnSettings[c].nPan = 256; if(info.chnSetup[c] & 0x80) ChnSettings[c].dwFlags.set(CHN_MUTE); else m_nChannels = c + 1; chunk.ReadString(ChnSettings[c].szName, 8); } // Read song message chunk = chunks.GetChunk(MDLChunk::idMessage); m_songMessage.Read(chunk, chunk.GetLength(), SongMessage::leCR); // Read sample info and data chunk = chunks.GetChunk(MDLChunk::idSampleInfo); if(chunk.IsValid()) { FileReader dataChunk = chunks.GetChunk(MDLChunk::ifSampleData); uint8 numSamples = chunk.ReadUint8(); for(uint8 smp = 0; smp < numSamples; smp++) { const SAMPLEINDEX sampleIndex = chunk.ReadUint8(); if(sampleIndex == 0 || sampleIndex >= MAX_SAMPLES || !chunk.CanRead(32 + 8 + 2 + 12 + 2)) break; if(sampleIndex > GetNumSamples()) m_nSamples = sampleIndex; ModSample &sample = Samples[sampleIndex]; sample.Initialize(); sample.Set16BitCuePoints(); chunk.ReadString(m_szNames[sampleIndex], 32); chunk.ReadString(sample.filename, 8); uint32 c4speed; if(fileHeader.version < 0x10) c4speed = chunk.ReadUint16LE(); else c4speed = chunk.ReadUint32LE(); sample.nC5Speed = c4speed * 2u; sample.nLength = chunk.ReadUint32LE(); sample.nLoopStart = chunk.ReadUint32LE(); sample.nLoopEnd = chunk.ReadUint32LE(); if(sample.nLoopEnd != 0) { sample.uFlags.set(CHN_LOOP); sample.nLoopEnd += sample.nLoopStart; } uint8 volume = chunk.ReadUint8(); if(fileHeader.version < 0x10) sample.nVolume = volume; uint8 flags = chunk.ReadUint8(); if(flags & 0x01) { sample.uFlags.set(CHN_16BIT); sample.nLength /= 2u; sample.nLoopStart /= 2u; sample.nLoopEnd /= 2u; } sample.uFlags.set(CHN_PINGPONGLOOP, (flags & 0x02) != 0); SampleIO sampleIO( (flags & 0x01) ? SampleIO::_16bit : SampleIO::_8bit, SampleIO::mono, SampleIO::littleEndian, (flags & 0x0C) ? SampleIO::MDL : SampleIO::signedPCM); if(loadFlags & loadSampleData) { sampleIO.ReadSample(sample, dataChunk); } } } chunk = chunks.GetChunk(MDLChunk::idInstrs); if(chunk.IsValid()) { std::vector volEnvs, panEnvs, pitchEnvs; MDLReadEnvelopes(chunks.GetChunk(MDLChunk::idVolEnvs), volEnvs); MDLReadEnvelopes(chunks.GetChunk(MDLChunk::idPanEnvs), panEnvs); MDLReadEnvelopes(chunks.GetChunk(MDLChunk::idFreqEnvs), pitchEnvs); uint8 numInstruments = chunk.ReadUint8(); for(uint8 i = 0; i < numInstruments; i++) { const auto [ins, numSamples] = chunk.ReadArray(); uint8 firstNote = 0; ModInstrument *mptIns = nullptr; if(ins == 0 || !chunk.CanRead(32 + sizeof(MDLSampleHeader) * numSamples) || (mptIns = AllocateInstrument(ins)) == nullptr) { chunk.Skip(32 + sizeof(MDLSampleHeader) * numSamples); continue; } chunk.ReadString(mptIns->name, 32); for(uint8 smp = 0; smp < numSamples; smp++) { MDLSampleHeader sampleHeader; chunk.ReadStruct(sampleHeader); if(sampleHeader.smpNum == 0 || sampleHeader.smpNum > GetNumSamples()) continue; LimitMax(sampleHeader.lastNote, static_cast(std::size(mptIns->Keyboard))); for(uint8 n = firstNote; n <= sampleHeader.lastNote; n++) { mptIns->Keyboard[n] = sampleHeader.smpNum; } firstNote = sampleHeader.lastNote + 1; CopyEnvelope(mptIns->VolEnv, sampleHeader.volEnvFlags, volEnvs); CopyEnvelope(mptIns->PanEnv, sampleHeader.panEnvFlags, panEnvs); CopyEnvelope(mptIns->PitchEnv, sampleHeader.freqEnvFlags, pitchEnvs); mptIns->nFadeOut = (sampleHeader.fadeout + 1u) / 2u; #ifdef MODPLUG_TRACKER if((mptIns->VolEnv.dwFlags & (ENV_ENABLED | ENV_LOOP)) == ENV_ENABLED) { // Fade-out is only supposed to happen on key-off, not at the end of a volume envelope. // Fake it by putting a loop at the end. mptIns->VolEnv.nLoopStart = mptIns->VolEnv.nLoopEnd = static_cast(mptIns->VolEnv.size() - 1); mptIns->VolEnv.dwFlags.set(ENV_LOOP); } for(auto &p : mptIns->PitchEnv) { // Scale pitch envelope p.value = (p.value * 6u) / 16u; } #endif // MODPLUG_TRACKER // Samples were already initialized above. Let's hope they are not going to be re-used with different volume / panning / vibrato... ModSample &mptSmp = Samples[sampleHeader.smpNum]; // This flag literally enables and disables the default volume of a sample. If you disable this flag, // the sample volume of a previously sample is re-used, even if you put an instrument number next to the note. if(sampleHeader.volEnvFlags & 0x40) mptSmp.nVolume = sampleHeader.volume; else mptSmp.uFlags.set(SMP_NODEFAULTVOLUME); mptSmp.nPan = std::min(static_cast(sampleHeader.panning * 2), uint16(254)); mptSmp.nVibType = MDLVibratoType[sampleHeader.vibType & 3]; mptSmp.nVibSweep = sampleHeader.vibSweep; mptSmp.nVibDepth = (sampleHeader.vibDepth + 3u) / 4u; mptSmp.nVibRate = sampleHeader.vibSpeed; // Convert to IT-like vibrato sweep if(mptSmp.nVibSweep != 0) mptSmp.nVibSweep = mpt::saturate_cast(Util::muldivr_unsigned(mptSmp.nVibDepth, 256, mptSmp.nVibSweep)); else mptSmp.nVibSweep = 255; if(sampleHeader.panEnvFlags & 0x40) mptSmp.uFlags.set(CHN_PANNING); } } } // Read pattern tracks std::vector tracks; if((loadFlags & loadPatternData) && (chunk = chunks.GetChunk(MDLChunk::idTracks)).IsValid()) { uint32 numTracks = chunk.ReadUint16LE(); tracks.resize(numTracks + 1); for(uint32 i = 1; i <= numTracks; i++) { tracks[i] = chunk.ReadChunk(chunk.ReadUint16LE()); } } // Read actual patterns if((loadFlags & loadPatternData) && (chunk = chunks.GetChunk(MDLChunk::idPats)).IsValid()) { PATTERNINDEX numPats = chunk.ReadUint8(); // In case any muted channels contain data, be sure that we import them as well. for(PATTERNINDEX pat = 0; pat < numPats; pat++) { CHANNELINDEX numChans = 32; if(fileHeader.version >= 0x10) { MDLPatternHeader patHead; chunk.ReadStruct(patHead); if(patHead.channels > m_nChannels && patHead.channels <= 32) m_nChannels = patHead.channels; numChans = patHead.channels; } for(CHANNELINDEX chn = 0; chn < numChans; chn++) { if(chunk.ReadUint16LE() > 0 && chn >= m_nChannels && chn < 32) m_nChannels = chn + 1; } } chunk.Seek(1); Patterns.ResizeArray(numPats); for(PATTERNINDEX pat = 0; pat < numPats; pat++) { CHANNELINDEX numChans = 32; ROWINDEX numRows = 64; std::string name; if(fileHeader.version >= 0x10) { MDLPatternHeader patHead; chunk.ReadStruct(patHead); numChans = patHead.channels; numRows = patHead.lastRow + 1; name = mpt::String::ReadBuf(mpt::String::spacePadded, patHead.name); } if(!Patterns.Insert(pat, numRows)) { chunk.Skip(2 * numChans); continue; } Patterns[pat].SetName(name); for(CHANNELINDEX chn = 0; chn < numChans; chn++) { uint16 trkNum = chunk.ReadUint16LE(); if(!trkNum || trkNum >= tracks.size() || chn >= m_nChannels) continue; FileReader &track = tracks[trkNum]; track.Rewind(); ROWINDEX row = 0; while(row < numRows && track.CanRead(1)) { ModCommand *m = Patterns[pat].GetpModCommand(row, chn); uint8 b = track.ReadUint8(); uint8 x = (b >> 2), y = (b & 3); switch(y) { case 0: // (x + 1) empty notes follow row += x + 1; break; case 1: // Repeat previous note (x + 1) times if(row > 0) { ModCommand &orig = *Patterns[pat].GetpModCommand(row - 1, chn); do { *m = orig; m += m_nChannels; row++; } while (row < numRows && x--); } break; case 2: // Copy note from row x if(row > x) { *m = *Patterns[pat].GetpModCommand(x, chn); } row++; break; case 3: // New note data if(x & MDLNOTE_NOTE) { b = track.ReadUint8(); m->note = (b > 120) ? static_cast(NOTE_KEYOFF) : static_cast(b); } if(x & MDLNOTE_SAMPLE) { m->instr = track.ReadUint8(); } { uint8 vol = 0, e1 = 0, e2 = 0, p1 = 0, p2 = 0; if(x & MDLNOTE_VOLUME) { vol = track.ReadUint8(); } if(x & MDLNOTE_EFFECTS) { b = track.ReadUint8(); e1 = (b & 0x0F); e2 = (b >> 4); } if(x & MDLNOTE_PARAM1) p1 = track.ReadUint8(); if(x & MDLNOTE_PARAM2) p2 = track.ReadUint8(); ImportMDLCommands(*m, vol, e1, e2, p1, p2); } row++; break; } } } } } if((loadFlags & loadPatternData) && (chunk = chunks.GetChunk(MDLChunk::idPatNames)).IsValid()) { PATTERNINDEX i = 0; while(i < Patterns.Size() && chunk.CanRead(16)) { char name[17]; chunk.ReadString(name, 16); Patterns[i].SetName(name); } } return true; } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Load_med.cpp0000644000175000017500000012013614175523016020312 00000000000000/* * Load_med.cpp * ------------ * Purpose: OctaMED / MED Soundstudio module loader * Notes : Support for synthesized instruments is still missing. * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Loaders.h" #ifdef MPT_WITH_VST #include "../mptrack/Vstplug.h" #include "plugins/PluginManager.h" #endif // MPT_WITH_VST #include "mpt/io/base.hpp" #include "mpt/io/io.hpp" #include "mpt/io/io_span.hpp" #include "mpt/io/io_stdstream.hpp" #include OPENMPT_NAMESPACE_BEGIN struct MMD0FileHeader { char mmd[3]; // "MMD" for the first song in file, "MCN" for the rest uint8be version; // '0'-'3' uint32be modLength; // Size of file uint32be songOffset; // Position in file for the first song uint16be playerSettings1[2]; // Internal variables for the play routine uint32be blockArrOffset; // Position in file for blocks (patterns) uint8be flags; uint8be reserved1[3]; uint32be sampleArrOffset; // Position in file for samples (should be identical between songs) uint32be reserved2; uint32be expDataOffset; // Absolute offset in file for ExpData (0 if not present) uint32be reserved3; char playerSettings2[11]; // Internal variables for the play routine uint8be extraSongs; // Number of songs - 1 }; MPT_BINARY_STRUCT(MMD0FileHeader, 52) struct MMD0Sample { uint16be loopStart; uint16be loopLength; uint8be midiChannel; uint8be midiPreset; uint8be sampleVolume; int8be sampleTranspose; }; MPT_BINARY_STRUCT(MMD0Sample, 8) // Song header for MMD0/MMD1 struct MMD0Song { uint8be sequence[256]; }; MPT_BINARY_STRUCT(MMD0Song, 256) // Song header for MMD2/MMD3 struct MMD2Song { enum Flags3 { FLAG3_STEREO = 0x01, // Mixing in stereo FLAG3_FREEPAN = 0x02, // Mixing flag: free pan }; uint32be playSeqTableOffset; uint32be sectionTableOffset; uint32be trackVolsOffset; uint16be numTracks; uint16be numPlaySeqs; uint32be trackPanOffset; // 0: all centered (according to docs, MED Soundstudio uses Amiga hard-panning instead) uint32be flags3; uint16be volAdjust; // Volume adjust (%) uint16be mixChannels; // Mixing channels, 0 means 4 uint8be mixEchoType; // 0 = nothing, 1 = normal, 2 = cross uint8be mixEchoDepth; // 1 - 6, 0 = default uint16be mixEchoLength; // Echo length in milliseconds int8be mixStereoSep; // Stereo separation char pad0[223]; }; MPT_BINARY_STRUCT(MMD2Song, 256) // Common song header struct MMDSong { enum Flags { FLAG_FILTERON = 0x01, // The hardware audio filter is on FLAG_JUMPINGON = 0x02, // Mouse pointer jumping on FLAG_JUMP8TH = 0x04, // ump every 8th line (not in OctaMED Pro) FLAG_INSTRSATT = 0x08, // sng+samples indicator (not useful in MMDs) FLAG_VOLHEX = 0x10, // volumes are HEX FLAG_STSLIDE = 0x20, // use ST/NT/PT compatible sliding FLAG_8CHANNEL = 0x40, // this is OctaMED 5-8 channel song FLAG_SLOWHQ = 0x80, // HQ V2-4 compatibility mode }; enum Flags2 { FLAG2_BMASK = 0x1F, // (bits 0-4) BPM beat length (in lines) FLAG2_BPM = 0x20, // BPM mode on FLAG2_MIX = 0x80, // Module uses mixing }; uint16be numBlocks; // Number of blocks in current song uint16be songLength; // MMD0: Number of sequence numbers in the play sequence list, MMD2: Number of sections char song[256]; MMD0Song GetMMD0Song() const { static_assert(sizeof(MMD0Song) == sizeof(song)); return mpt::bit_cast(song); } MMD2Song GetMMD2Song() const { static_assert(sizeof(MMD2Song) == sizeof(song)); return mpt::bit_cast(song); } uint16be defaultTempo; int8be playTranspose; // The global play transpose value for current song uint8be flags; uint8be flags2; uint8be tempo2; // Timing pulses per line (ticks) uint8be trackVol[16]; // 1...64 in MMD0/MMD1, reserved in MMD2 uint8be masterVol; // 1...64 uint8be numSamples; }; MPT_BINARY_STRUCT(MMDSong, 284) struct MMD2PlaySeq { char name[32]; uint32be commandTableOffset; uint32be reserved; uint16be length; // Number of entries }; MPT_BINARY_STRUCT(MMD2PlaySeq, 42) struct MMD0PatternHeader { uint8be numTracks; uint8be numRows; }; MPT_BINARY_STRUCT(MMD0PatternHeader, 2) struct MMD1PatternHeader { uint16be numTracks; uint16be numRows; uint32be blockInfoOffset; }; MPT_BINARY_STRUCT(MMD1PatternHeader, 8) struct MMDPlaySeqCommand { enum Command { kStop = 1, kJump = 2, }; uint16be offset; // Offset within current play sequence, 0xFFFF = end of list uint8be command; // Stop = 1, Jump = 2 uint8be extraSize; }; MPT_BINARY_STRUCT(MMDPlaySeqCommand, 4) struct MMDBlockInfo { uint32be highlightMaskOffset; uint32be nameOffset; uint32be nameLength; uint32be pageTableOffset; // File offset of command page table uint32be cmdExtTableOffset; // File offset of command extension table (second parameter) uint32be reserved[4]; }; MPT_BINARY_STRUCT(MMDBlockInfo, 36) struct MMDInstrHeader { enum Types { VSTI = -4, HIGHLIFE = -3, HYBRID = -2, SYNTHETIC = -1, SAMPLE = 0, // an ordinary 1-octave sample (or MIDI) IFF5OCT = 1, // 5 octaves IFF3OCT = 2, // 3 octaves // The following ones are recognized by OctaMED Pro only IFF2OCT = 3, // 2 octaves IFF4OCT = 4, // 4 octaves IFF6OCT = 5, // 6 octaves IFF7OCT = 6, // 7 octaves // OctaMED Pro V5 + later EXTSAMPLE = 7, // two extra-low octaves TYPEMASK = 0x0F, S_16 = 0x10, STEREO = 0x20, DELTA = 0x40, PACKED = 0x80, // MMDPackedSampleHeader follows OBSOLETE_MD16 = 0x18, }; uint32be length; int16be type; }; MPT_BINARY_STRUCT(MMDInstrHeader, 6) struct MMDPackedSampleHeader { uint16be packType; // Only 1 = ADPCM is supported uint16be subType; // Packing subtype // ADPCM subtype // 1: g723_40 // 2: g721 // 3: g723_24 uint8be commonFlags; // flags common to all packtypes (none defined so far) uint8be packerFlags; // flags for the specific packtype uint32be leftChLen; // packed length of left channel in bytes uint32be rightChLen; // packed length of right channel in bytes (ONLY PRESENT IN STEREO SAMPLES) }; MPT_BINARY_STRUCT(MMDPackedSampleHeader, 14) struct MMDInstrExt { enum { SSFLG_LOOP = 0x01, // Loop On / Off SSFLG_EXTPSET = 0x02, // Ext.Preset SSFLG_DISABLED = 0x04, // Disabled SSFLG_PINGPONG = 0x08, // Ping-pong looping }; uint8be hold; // 0...127 uint8be decay; // 0...127 uint8be suppressMidiOff; int8be finetune; // Below fields saved by >= V5 uint8be defaultPitch; uint8be instrFlags; uint16be longMidiPreset; // Legacy MIDI program mode that doesn't use banks but a combination of two program change commands // Below fields saved by >= V5.02 uint8be outputDevice; uint8be reserved; // Below fields saved by >= V7 uint32be loopStart; uint32be loopLength; // Not sure which version starts saving those but they are saved by MED Soundstudio for Windows uint8 volume; // 0...127 uint8 outputPort; // Index into user-configurable device list (NOT WinAPI port index) uint16le midiBank; }; MPT_BINARY_STRUCT(MMDInstrExt, 22) struct MMDInstrInfo { char name[40]; }; MPT_BINARY_STRUCT(MMDInstrInfo, 40) struct MMD0Exp { uint32be nextModOffset; uint32be instrExtOffset; uint16be instrExtEntries; uint16be instrExtEntrySize; uint32be annoText; uint32be annoLength; uint32be instrInfoOffset; uint16be instrInfoEntries; uint16be instrInfoEntrySize; uint32be jumpMask; uint32be rgbTable; uint8be channelSplit[4]; uint32be notationInfoOffset; uint32be songNameOffset; uint32be songNameLength; uint32be midiDumpOffset; uint32be mmdInfoOffset; uint32be arexxOffset; uint32be midiCmd3xOffset; uint32be trackInfoOffset; // Pointer to song->numtracks pointers to tag lists uint32be effectInfoOffset; // Pointers to group pointers uint32be tagEnd; }; MPT_BINARY_STRUCT(MMD0Exp, 80) struct MMDTag { enum TagType { // Generic MMD tags MMDTAG_END = 0x00000000, MMDTAG_PTR = 0x80000000, // Data needs relocation MMDTAG_MUSTKNOW = 0x40000000, // Loader must fail if this isn't recognized MMDTAG_MUSTWARN = 0x20000000, // Loader must warn if this isn't recognized MMDTAG_MASK = 0x1FFFFFFF, // ExpData tags // # of effect groups, including the global group (will override settings in MMDSong struct), default = 1 MMDTAG_EXP_NUMFXGROUPS = 1, MMDTAG_TRK_FXGROUP = 3, MMDTAG_TRK_NAME = 1, // trackinfo tags MMDTAG_TRK_NAMELEN = 2, // namelen includes zero term. // effectinfo tags MMDTAG_FX_ECHOTYPE = 1, MMDTAG_FX_ECHOLEN = 2, MMDTAG_FX_ECHODEPTH = 3, MMDTAG_FX_STEREOSEP = 4, MMDTAG_FX_GROUPNAME = 5, // the Global Effects group shouldn't have name saved! MMDTAG_FX_GRPNAMELEN = 6, // namelen includes zero term. }; uint32be type; uint32be data; }; MPT_BINARY_STRUCT(MMDTag, 8) struct MMDDump { uint32be length; uint32be dataPointer; uint16be extLength; // If >= 20: name follows as char[20] }; MPT_BINARY_STRUCT(MMDDump, 10) static TEMPO MMDTempoToBPM(uint32 tempo, bool is8Ch, bool bpmMode, uint8 rowsPerBeat) { if(bpmMode && !is8Ch) { // You would have thought that we could use modern tempo mode here. // Alas, the number of ticks per row still influences the tempo. :( return TEMPO((tempo * rowsPerBeat) / 4.0); } if(is8Ch && tempo > 0) { LimitMax(tempo, 10u); // MED Soundstudio uses these tempos when importing old files static constexpr uint8 tempos[10] = {179, 164, 152, 141, 131, 123, 116, 110, 104, 99}; return TEMPO(tempos[tempo - 1], 0); } else if(tempo > 0 && tempo <= 10) { // SoundTracker compatible tempo return TEMPO((6.0 * 1773447.0 / 14500.0) / tempo); } return TEMPO(tempo / 0.264); } static void ConvertMEDEffect(ModCommand &m, bool is8ch, bool bpmMode, uint8 rowsPerBeat, bool volHex) { switch(m.command) { case 0x04: // Vibrato (twice as deep as in ProTracker) m.command = CMD_VIBRATO; m.param = (std::min(m.param >> 3, 0x0F) << 4) | std::min((m.param & 0x0F) * 2, 0x0F); break; case 0x08: // Hold and decay m.command = CMD_NONE; break; case 0x09: // Set secondary speed if(m.param > 0 && m.param <= 20) m.command = CMD_SPEED; else m.command = CMD_NONE; break; case 0x0C: // Set Volume m.command = CMD_VOLUME; if(!volHex && m.param < 0x99) m.param = (m.param >> 4) * 10 + (m.param & 0x0F); else if(volHex) m.param = ((m.param & 0x7F) + 1) / 2; else m.command = CMD_NONE; break; case 0x0D: m.command = CMD_VOLUMESLIDE; break; case 0x0E: // Synth jump m.command = CMD_NONE; break; case 0x0F: // Misc if(m.param == 0) { m.command = CMD_PATTERNBREAK; } else if(m.param <= 0xF0) { m.command = CMD_TEMPO; if(m.param < 0x03) // This appears to be a bug in OctaMED which is not emulated in MED Soundstudio on Windows. m.param = 0x70; else m.param = mpt::saturate_round(MMDTempoToBPM(m.param, is8ch, bpmMode, rowsPerBeat).ToDouble()); #ifdef MODPLUG_TRACKER if(m.param < 0x20) m.param = 0x20; #endif // MODPLUG_TRACKER } else switch(m.command) { case 0xF1: // Play note twice m.command = CMD_MODCMDEX; m.param = 0x93; break; case 0xF2: // Delay note m.command = CMD_MODCMDEX; m.param = 0xD3; break; case 0xF3: // Play note three times m.command = CMD_MODCMDEX; m.param = 0x92; break; case 0xF8: // Turn filter off case 0xF9: // Turn filter on m.command = CMD_MODCMDEX; m.param = 0xF9 - m.param; break; case 0xFA: // MIDI pedal on case 0xFB: // MIDI pedal off case 0xFD: // Set pitch case 0xFE: // End of song m.command = CMD_NONE; break; case 0xFF: // Turn note off m.note = NOTE_NOTECUT; m.command = CMD_NONE; break; default: m.command = CMD_NONE; break; } break; case 0x10: // MIDI message m.command = CMD_MIDI; m.param |= 0x80; break; case 0x11: // Slide pitch up m.command = CMD_MODCMDEX; m.param = 0x10 | std::min(m.param, 0x0F); break; case 0x12: // Slide pitch down m.command = CMD_MODCMDEX; m.param = 0x20 | std::min(m.param, 0x0F); break; case 0x14: // Vibrato (ProTracker compatible depth, but faster) m.command = CMD_VIBRATO; m.param = (std::min((m.param >> 4) + 1, 0x0F) << 4) | (m.param & 0x0F); break; case 0x15: // Set finetune m.command = CMD_MODCMDEX; m.param = 0x50 | (m.param & 0x0F); break; case 0x16: // Loop m.command = CMD_MODCMDEX; m.param = 0x60 | std::min(m.param, 0x0F); break; case 0x18: // Stop note m.command = CMD_MODCMDEX; m.param = 0xC0 | std::min(m.param, 0x0F); break; case 0x19: // Sample Offset m.command = CMD_OFFSET; break; case 0x1A: // Slide volume up once m.command = CMD_MODCMDEX; m.param = 0xA0 | std::min(m.param, 0x0F); break; case 0x1B: // Slide volume down once m.command = CMD_MODCMDEX; m.param = 0xB0 | std::min(m.param, 0x0F); break; case 0x1C: // MIDI program if(m.param > 0 && m.param <= 128) { m.command = CMD_MIDI; m.param--; } else { m.command = CMD_NONE; } break; case 0x1D: // Pattern break (in hex) m.command = CMD_PATTERNBREAK; break; case 0x1E: // Repeat row m.command = CMD_MODCMDEX; m.param = 0xE0 | std::min(m.param, 0x0F); break; case 0x1F: // Note delay and retrigger { if(m.param & 0xF0) { m.command = CMD_MODCMDEX; m.param = 0xD0 | (m.param >> 4); } else if(m.param & 0x0F) { m.command = CMD_MODCMDEX; m.param = 0x90 | m.param; } else { m.command = CMD_NONE; } break; } case 0x20: // Reverse sample + skip samples if(m.param == 0 && m.vol == 0) { if(m.IsNote()) { m.command = CMD_S3MCMDEX; m.param = 0x9F; } } else { // Skip given number of samples m.command = CMD_NONE; } break; case 0x29: // Relative sample offset if(m.vol > 0) { m.command = CMD_OFFSETPERCENTAGE; m.param = mpt::saturate_cast(Util::muldiv_unsigned(m.param, 0x100, m.vol)); } else { m.command = CMD_NONE; } break; case 0x2E: // Set panning if(m.param <= 0x10 || m.param >= 0xF0) { m.command = CMD_PANNING8; m.param = mpt::saturate_cast(((m.param ^ 0x80) - 0x70) * 8); } else { m.command = CMD_NONE; } break; default: if(m.command < 0x10) CSoundFile::ConvertModCommand(m); else m.command = CMD_NONE; break; } } #ifdef MPT_WITH_VST static std::wstring ReadMEDStringUTF16BE(FileReader &file) { FileReader chunk = file.ReadChunk(file.ReadUint32BE()); std::wstring s(chunk.GetLength() / 2u, L'\0'); for(auto &c : s) { c = chunk.ReadUint16BE(); } return s; } #endif // MPT_WITH_VST static void MEDReadNextSong(FileReader &file, MMD0FileHeader &fileHeader, MMD0Exp &expData, MMDSong &songHeader) { file.ReadStruct(fileHeader); file.Seek(fileHeader.songOffset + 63 * sizeof(MMD0Sample)); file.ReadStruct(songHeader); if(fileHeader.expDataOffset && file.Seek(fileHeader.expDataOffset)) file.ReadStruct(expData); else expData = {}; } static std::pair MEDScanNumChannels(FileReader &file, const uint8 version) { MMD0FileHeader fileHeader; MMD0Exp expData; MMDSong songHeader; file.Rewind(); uint32 songOffset = 0; MEDReadNextSong(file, fileHeader, expData, songHeader); SEQUENCEINDEX numSongs = std::min(MAX_SEQUENCES, mpt::saturate_cast(fileHeader.expDataOffset ? fileHeader.extraSongs + 1 : 1)); CHANNELINDEX numChannels = 4; // Scan patterns for max number of channels for(SEQUENCEINDEX song = 0; song < numSongs; song++) { const PATTERNINDEX numPatterns = songHeader.numBlocks; if(songHeader.numSamples > 63 || numPatterns > 0x7FFF) return {}; for(PATTERNINDEX pat = 0; pat < numPatterns; pat++) { if(!file.Seek(fileHeader.blockArrOffset + pat * 4u) || !file.Seek(file.ReadUint32BE())) { continue; } numChannels = std::max(numChannels, static_cast(version < 1 ? file.ReadUint8() : file.ReadUint16BE())); } // If song offsets are going backwards, reject the file if(expData.nextModOffset <= songOffset || !file.Seek(expData.nextModOffset)) { numSongs = song + 1; break; } songOffset = expData.nextModOffset; MEDReadNextSong(file, fileHeader, expData, songHeader); } return {numChannels, numSongs}; } static bool ValidateHeader(const MMD0FileHeader &fileHeader) { if(std::memcmp(fileHeader.mmd, "MMD", 3) || fileHeader.version < '0' || fileHeader.version > '3' || fileHeader.songOffset < sizeof(MMD0FileHeader) || fileHeader.songOffset > uint32_max - 63 * sizeof(MMD0Sample) - sizeof(MMDSong) || fileHeader.blockArrOffset < sizeof(MMD0FileHeader) || (fileHeader.sampleArrOffset > 0 && fileHeader.sampleArrOffset < sizeof(MMD0FileHeader)) || fileHeader.expDataOffset > uint32_max - sizeof(MMD0Exp)) { return false; } return true; } static uint64 GetHeaderMinimumAdditionalSize(const MMD0FileHeader &fileHeader) { return std::max({ fileHeader.songOffset + 63 * sizeof(MMD0Sample) + sizeof(MMDSong), fileHeader.blockArrOffset, fileHeader.sampleArrOffset ? fileHeader.sampleArrOffset : sizeof(MMD0FileHeader), fileHeader.expDataOffset + sizeof(MMD0Exp) }) - sizeof(MMD0FileHeader); } CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderMED(MemoryFileReader file, const uint64 *pfilesize) { MMD0FileHeader fileHeader; if(!file.ReadStruct(fileHeader)) return ProbeWantMoreData; if(!ValidateHeader(fileHeader)) return ProbeFailure; return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); } bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags) { file.Rewind(); MMD0FileHeader fileHeader; if(!file.ReadStruct(fileHeader)) return false; if(!ValidateHeader(fileHeader)) return false; if(!file.CanRead(mpt::saturate_cast(GetHeaderMinimumAdditionalSize(fileHeader)))) return false; if(loadFlags == onlyVerifyHeader) return true; InitializeGlobals(MOD_TYPE_MED); InitializeChannels(); const uint8 version = fileHeader.version - '0'; file.Seek(fileHeader.songOffset); FileReader sampleHeaderChunk = file.ReadChunk(63 * sizeof(MMD0Sample)); MMDSong songHeader; file.ReadStruct(songHeader); if(songHeader.numSamples > 63 || songHeader.numBlocks > 0x7FFF) return false; MMD0Exp expData{}; if(fileHeader.expDataOffset && file.Seek(fileHeader.expDataOffset)) { file.ReadStruct(expData); } const auto [numChannels, numSongs] = MEDScanNumChannels(file, version); if(numChannels < 1 || numChannels > MAX_BASECHANNELS) return false; m_nChannels = numChannels; // Start with the instruments, as those are shared between songs std::vector instrOffsets; if(fileHeader.sampleArrOffset) { file.Seek(fileHeader.sampleArrOffset); file.ReadVector(instrOffsets, songHeader.numSamples); } else if(songHeader.numSamples > 0) { return false; } m_nInstruments = m_nSamples = songHeader.numSamples; // In MMD0 / MMD1, octave wrapping is not done for synth instruments // - It's required e.g. for automatic terminated to.mmd0 and you got to let the music.mmd1 // - starkelsesirap.mmd0 (synth instruments) on the other hand don't need it // In MMD2 / MMD3, the mix flag is used instead. const bool hardwareMixSamples = (version < 2) || (version >= 2 && !(songHeader.flags2 & MMDSong::FLAG2_MIX)); bool needInstruments = false; bool anySynthInstrs = false; #ifdef MPT_WITH_VST PLUGINDEX numPlugins = 0; #endif // MPT_WITH_VST for(SAMPLEINDEX ins = 1, smp = 1; ins <= m_nInstruments; ins++) { if(!AllocateInstrument(ins, smp)) return false; ModInstrument &instr = *Instruments[ins]; MMDInstrHeader instrHeader{}; FileReader sampleChunk; if(instrOffsets[ins - 1] != 0 && file.Seek(instrOffsets[ins - 1])) { file.ReadStruct(instrHeader); sampleChunk = file.ReadChunk(instrHeader.length); } const bool isSynth = instrHeader.type < 0; const size_t maskedType = static_cast(instrHeader.type & MMDInstrHeader::TYPEMASK); #ifdef MPT_WITH_VST if(instrHeader.type == MMDInstrHeader::VSTI) { needInstruments = true; sampleChunk.Skip(6); // 00 00 const std::wstring type = ReadMEDStringUTF16BE(sampleChunk); const std::wstring name = ReadMEDStringUTF16BE(sampleChunk); if(type == L"VST") { auto &mixPlug = m_MixPlugins[numPlugins]; mixPlug = {}; mixPlug.Info.dwPluginId1 = Vst::kEffectMagic; mixPlug.Info.gain = 10; mixPlug.Info.szName = mpt::ToCharset(mpt::Charset::Locale, name); mixPlug.Info.szLibraryName = mpt::ToCharset(mpt::Charset::UTF8, name); instr.nMixPlug = numPlugins + 1; instr.nMidiChannel = MidiFirstChannel; instr.Transpose(-24); instr.AssignSample(0); // TODO: Figure out patch and routing data numPlugins++; } } else #endif // MPT_WITH_VST if(isSynth) { // TODO: Figure out synth instruments anySynthInstrs = true; instr.AssignSample(0); } uint8 numSamples = 1; static constexpr uint8 SamplesPerType[] = {1, 5, 3, 2, 4, 6, 7}; if(!isSynth && maskedType < std::size(SamplesPerType)) numSamples = SamplesPerType[maskedType]; if(numSamples > 1) { static_assert(MAX_SAMPLES > 63 * 9, "Check IFFOCT multisample code"); m_nSamples += numSamples - 1; needInstruments = true; static constexpr uint8 OctSampleMap[][8] = { {1, 1, 0, 0, 0, 0, 0, 0}, // 2 {2, 2, 1, 1, 0, 0, 0, 0}, // 3 {3, 3, 2, 2, 1, 0, 0, 0}, // 4 {4, 3, 2, 1, 1, 0, 0, 0}, // 5 {5, 4, 3, 2, 1, 0, 0, 0}, // 6 {6, 5, 4, 3, 2, 1, 0, 0}, // 7 }; static constexpr int8 OctTransposeMap[][8] = { { 0, 0, -12, -12, -24, -36, -48, -60}, // 2 { 0, 0, -12, -12, -24, -36, -48, -60}, // 3 { 0, 0, -12, -12, -24, -36, -48, -60}, // 4 {12, 0, -12, -24, -24, -36, -48, -60}, // 5 {12, 0, -12, -24, -36, -48, -48, -60}, // 6 {12, 0, -12, -24, -36, -48, -60, -72}, // 7 }; // TODO: Move octaves so that they align better (C-4 = lowest, we don't have access to the highest four octaves) for(int octave = 4; octave < 10; octave++) { for(int note = 0; note < 12; note++) { instr.Keyboard[12 * octave + note] = smp + OctSampleMap[numSamples - 2][octave - 4]; instr.NoteMap[12 * octave + note] += OctTransposeMap[numSamples - 2][octave - 4]; } } } else if(maskedType == MMDInstrHeader::EXTSAMPLE) { needInstruments = true; instr.Transpose(-24); } else if(!isSynth && hardwareMixSamples) { for(int octave = 7; octave < 10; octave++) { for(int note = 0; note < 12; note++) { instr.NoteMap[12 * octave + note] -= static_cast((octave - 6) * 12); } } } MMD0Sample sampleHeader; sampleHeaderChunk.ReadStruct(sampleHeader); // midiChannel = 0xFF == midi instrument but with invalid channel, midiChannel = 0x00 == sample-based instrument? if(sampleHeader.midiChannel > 0 && sampleHeader.midiChannel <= 16) { instr.nMidiChannel = sampleHeader.midiChannel - 1 + MidiFirstChannel; needInstruments = true; #ifdef MPT_WITH_VST if(!isSynth) { auto &mixPlug = m_MixPlugins[numPlugins]; mixPlug = {}; mixPlug.Info.dwPluginId1 = PLUGMAGIC('V', 's', 't', 'P'); mixPlug.Info.dwPluginId2 = PLUGMAGIC('M', 'M', 'I', 'D'); mixPlug.Info.gain = 10; mixPlug.Info.szName = "MIDI Input Output"; mixPlug.Info.szLibraryName = "MIDI Input Output"; instr.nMixPlug = numPlugins + 1; instr.Transpose(-24); numPlugins++; } #endif // MPT_WITH_VST } if(sampleHeader.midiPreset > 0 && sampleHeader.midiPreset <= 128) { instr.nMidiProgram = sampleHeader.midiPreset; } for(SAMPLEINDEX i = 0; i < numSamples; i++) { ModSample &mptSmp = Samples[smp + i]; mptSmp.Initialize(MOD_TYPE_MED); mptSmp.nVolume = 4u * std::min(sampleHeader.sampleVolume, 64u); mptSmp.RelativeTone = sampleHeader.sampleTranspose; } if(isSynth || !(loadFlags & loadSampleData)) { smp += numSamples; continue; } SampleIO sampleIO( SampleIO::_8bit, SampleIO::mono, SampleIO::bigEndian, SampleIO::signedPCM); const bool hasLoop = sampleHeader.loopLength > 1; SmpLength loopStart = sampleHeader.loopStart * 2; SmpLength loopEnd = loopStart + sampleHeader.loopLength * 2; SmpLength length = mpt::saturate_cast(sampleChunk.GetLength()); if(instrHeader.type & MMDInstrHeader::S_16) { sampleIO |= SampleIO::_16bit; length /= 2; } if (instrHeader.type & MMDInstrHeader::STEREO) { sampleIO |= SampleIO::stereoSplit; length /= 2; } if(instrHeader.type & MMDInstrHeader::DELTA) { sampleIO |= SampleIO::deltaPCM; } if(numSamples > 1) length = length / ((1u << numSamples) - 1); for(SAMPLEINDEX i = 0; i < numSamples; i++) { ModSample &mptSmp = Samples[smp + i]; mptSmp.nLength = length; sampleIO.ReadSample(mptSmp, sampleChunk); if(hasLoop) { mptSmp.nLoopStart = loopStart; mptSmp.nLoopEnd = loopEnd; mptSmp.uFlags.set(CHN_LOOP); } length *= 2; loopStart *= 2; loopEnd *= 2; } smp += numSamples; } if(expData.instrExtOffset != 0 && expData.instrExtEntries != 0 && file.Seek(expData.instrExtOffset)) { const uint16 entries = std::min(expData.instrExtEntries, songHeader.numSamples); const uint16 size = expData.instrExtEntrySize; for(uint16 i = 0; i < entries; i++) { MMDInstrExt instrExt; file.ReadStructPartial(instrExt, size); ModInstrument &ins = *Instruments[i + 1]; if(instrExt.hold) { ins.VolEnv.assign({ EnvelopeNode{0u, ENVELOPE_MAX}, EnvelopeNode{static_cast(instrExt.hold - 1), ENVELOPE_MAX}, EnvelopeNode{static_cast(instrExt.hold + (instrExt.decay ? 64u / instrExt.decay : 0u)), ENVELOPE_MIN}, }); if(instrExt.hold == 1) ins.VolEnv.erase(ins.VolEnv.begin()); ins.nFadeOut = instrExt.decay ? (instrExt.decay * 512) : 32767; ins.VolEnv.dwFlags.set(ENV_ENABLED); needInstruments = true; } if(size > offsetof(MMDInstrExt, volume)) ins.nGlobalVol = (instrExt.volume + 1u) / 2u; if(size > offsetof(MMDInstrExt, midiBank)) ins.wMidiBank = instrExt.midiBank; #ifdef MPT_WITH_VST if(ins.nMixPlug > 0) { PLUGINDEX plug = ins.nMixPlug - 1; auto &mixPlug = m_MixPlugins[plug]; if(mixPlug.Info.dwPluginId2 == PLUGMAGIC('M', 'M', 'I', 'D')) { float dev = (instrExt.outputDevice + 1) / 65536.0f; // Magic code from MidiInOut.h :( mixPlug.pluginData.resize(3 * sizeof(uint32)); auto memFile = std::make_pair(mpt::as_span(mixPlug.pluginData), mpt::IO::Offset(0)); mpt::IO::WriteIntLE(memFile, 0); // Plugin data type mpt::IO::Write(memFile, IEEE754binary32LE{0}); // Input device mpt::IO::Write(memFile, IEEE754binary32LE{dev}); // Output device // Check if we already have another plugin referencing this output device for(PLUGINDEX p = 0; p < plug; p++) { const auto &otherPlug = m_MixPlugins[p]; if(otherPlug.Info.dwPluginId1 == mixPlug.Info.dwPluginId1 && otherPlug.Info.dwPluginId2 == mixPlug.Info.dwPluginId2 && otherPlug.pluginData == mixPlug.pluginData) { ins.nMixPlug = p + 1; mixPlug = {}; break; } } } } #endif // MPT_WITH_VST ModSample &sample = Samples[ins.Keyboard[NOTE_MIDDLEC]]; sample.nFineTune = MOD2XMFineTune(instrExt.finetune); if(size > offsetof(MMDInstrExt, loopLength)) { sample.nLoopStart = instrExt.loopStart; sample.nLoopEnd = instrExt.loopStart + instrExt.loopLength; } if(size > offsetof(MMDInstrExt, instrFlags)) { sample.uFlags.set(CHN_LOOP, (instrExt.instrFlags & MMDInstrExt::SSFLG_LOOP) != 0); sample.uFlags.set(CHN_PINGPONGLOOP, (instrExt.instrFlags & MMDInstrExt::SSFLG_PINGPONG) != 0); if(instrExt.instrFlags & MMDInstrExt::SSFLG_DISABLED) sample.nGlobalVol = 0; } } } if(expData.instrInfoOffset != 0 && expData.instrInfoEntries != 0 && file.Seek(expData.instrInfoOffset)) { const uint16 entries = std::min(expData.instrInfoEntries, songHeader.numSamples); const uint16 size = expData.instrInfoEntrySize; for(uint16 i = 0; i < entries; i++) { MMDInstrInfo instrInfo; file.ReadStructPartial(instrInfo, size); Instruments[i + 1]->name = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, instrInfo.name); for(auto smp : Instruments[i + 1]->GetSamples()) { m_szNames[smp] = Instruments[i + 1]->name; } } } // Setup a program change macro for command 1C (even if MIDI plugin is disabled, as otherwise these commands may act as filter commands) m_MidiCfg.ClearZxxMacros(); m_MidiCfg.SFx[0] = "Cc z"; file.Rewind(); PATTERNINDEX basePattern = 0; for(SEQUENCEINDEX song = 0; song < numSongs; song++) { MEDReadNextSong(file, fileHeader, expData, songHeader); if(song != 0) { if(Order.AddSequence() == SEQUENCEINDEX_INVALID) return false; } ModSequence &order = Order(song); std::map jumpTargets; order.clear(); uint32 preamp = 32; if(version < 2) { if(songHeader.songLength > 256 || m_nChannels > 16) return false; ReadOrderFromArray(order, songHeader.GetMMD0Song().sequence, songHeader.songLength); for(auto &ord : order) { ord += basePattern; } SetupMODPanning(true); for(CHANNELINDEX chn = 0; chn < m_nChannels; chn++) { ChnSettings[chn].nVolume = std::min(songHeader.trackVol[chn], 64); } } else { const MMD2Song header = songHeader.GetMMD2Song(); if(header.numTracks < 1 || header.numTracks > 64 || m_nChannels > 64) return false; const bool freePan = (header.flags3 & MMD2Song::FLAG3_FREEPAN); if(header.volAdjust) preamp = Util::muldivr_unsigned(preamp, std::min(header.volAdjust, 800), 100); if (freePan) preamp /= 2; if(file.Seek(header.trackVolsOffset)) { for(CHANNELINDEX chn = 0; chn < m_nChannels; chn++) { ChnSettings[chn].nVolume = std::min(file.ReadUint8(), 64); } } if(header.trackPanOffset && file.Seek(header.trackPanOffset)) { for(CHANNELINDEX chn = 0; chn < m_nChannels; chn++) { ChnSettings[chn].nPan = (Clamp(file.ReadInt8(), -16, 16) + 16) * 8; } } else { SetupMODPanning(true); } std::vector sections; if(!file.Seek(header.sectionTableOffset) || !file.CanRead(songHeader.songLength * 2) || !file.ReadVector(sections, songHeader.songLength)) continue; for(uint16 section : sections) { if(section > header.numPlaySeqs) continue; file.Seek(header.playSeqTableOffset + section * 4); if(!file.Seek(file.ReadUint32BE()) || !file.CanRead(sizeof(MMD2PlaySeq))) continue; MMD2PlaySeq playSeq; file.ReadStruct(playSeq); if(!order.empty()) order.push_back(order.GetIgnoreIndex()); size_t readOrders = playSeq.length; if(!file.CanRead(readOrders)) LimitMax(readOrders, file.BytesLeft()); LimitMax(readOrders, ORDERINDEX_MAX); size_t orderStart = order.size(); order.reserve(orderStart + readOrders); for(size_t ord = 0; ord < readOrders; ord++) { PATTERNINDEX pat = file.ReadUint16BE(); if(pat < 0x8000) { order.push_back(basePattern + pat); } } if(playSeq.name[0]) order.SetName(mpt::ToUnicode(mpt::Charset::ISO8859_1, mpt::String::ReadAutoBuf(playSeq.name))); // Play commands (jump / stop) if(playSeq.commandTableOffset > 0 && file.Seek(playSeq.commandTableOffset)) { MMDPlaySeqCommand command; while(file.ReadStruct(command)) { FileReader chunk = file.ReadChunk(command.extraSize); ORDERINDEX ord = mpt::saturate_cast(orderStart + command.offset); if(command.offset == 0xFFFF || ord >= order.size()) break; if(command.command == MMDPlaySeqCommand::kStop) { order[ord] = order.GetInvalidPatIndex(); } else if(command.command == MMDPlaySeqCommand::kJump) { jumpTargets[ord] = chunk.ReadUint16BE(); order[ord] = order.GetIgnoreIndex(); } } } } } const bool volHex = (songHeader.flags & MMDSong::FLAG_VOLHEX) != 0; const bool is8Ch = (songHeader.flags & MMDSong::FLAG_8CHANNEL) != 0; const bool bpmMode = (songHeader.flags2 & MMDSong::FLAG2_BPM) != 0; const uint8 rowsPerBeat = 1 + (songHeader.flags2 & MMDSong::FLAG2_BMASK); m_nDefaultTempo = MMDTempoToBPM(songHeader.defaultTempo, is8Ch, bpmMode, rowsPerBeat); m_nDefaultSpeed = Clamp(songHeader.tempo2, 1, 32); if(bpmMode) { m_nDefaultRowsPerBeat = rowsPerBeat; m_nDefaultRowsPerMeasure = m_nDefaultRowsPerBeat * 4u; } if(songHeader.masterVol) m_nDefaultGlobalVolume = std::min(songHeader.masterVol, 64) * 4; m_nSamplePreAmp = m_nVSTiVolume = preamp; // For MED, this affects both volume and pitch slides m_SongFlags.set(SONG_FASTVOLSLIDES, !(songHeader.flags & MMDSong::FLAG_STSLIDE)); if(expData.songNameOffset && file.Seek(expData.songNameOffset)) { file.ReadString(m_songName, expData.songNameLength); if(numSongs > 1) order.SetName(mpt::ToUnicode(mpt::Charset::ISO8859_1, m_songName)); } if(expData.annoLength > 1 && file.Seek(expData.annoText)) { m_songMessage.Read(file, expData.annoLength - 1, SongMessage::leAutodetect); } #ifdef MPT_WITH_VST // Read MIDI messages if(expData.midiDumpOffset && file.Seek(expData.midiDumpOffset) && file.CanRead(8)) { uint16 numDumps = std::min(file.ReadUint16BE(), static_cast(m_MidiCfg.Zxx.size())); file.Skip(6); if(file.CanRead(numDumps * 4)) { std::vector dumpPointers; file.ReadVector(dumpPointers, numDumps); for(uint16 dump = 0; dump < numDumps; dump++) { if(!file.Seek(dumpPointers[dump]) || !file.CanRead(sizeof(MMDDump))) continue; MMDDump dumpHeader; file.ReadStruct(dumpHeader); if(!file.Seek(dumpHeader.dataPointer) || !file.CanRead(dumpHeader.length)) continue; std::array macro{}; auto length = std::min(static_cast(dumpHeader.length), macro.size() / 2u); for(size_t i = 0; i < length; i++) { const uint8 byte = file.ReadUint8(), high = byte >> 4, low = byte & 0x0F; macro[i * 2] = high + (high < 0x0A ? '0' : 'A' - 0x0A); macro[i * 2 + 1] = low + (low < 0x0A ? '0' : 'A' - 0x0A); } m_MidiCfg.Zxx[dump] = std::string_view{macro.data(), length * 2}; } } } #endif // MPT_WITH_VST if(expData.mmdInfoOffset && file.Seek(expData.mmdInfoOffset) && file.CanRead(12)) { file.Skip(6); // Next info file (unused) + reserved if(file.ReadUint16BE() == 1) // ASCII text { uint32 length = file.ReadUint32BE(); if(length && file.CanRead(length)) { const auto oldMsg = std::move(m_songMessage); m_songMessage.Read(file, length, SongMessage::leAutodetect); if(!oldMsg.empty()) m_songMessage.SetRaw(oldMsg + std::string(2, SongMessage::InternalLineEnding) + m_songMessage); } } } // Track Names if(version >= 2 && expData.trackInfoOffset) { for(CHANNELINDEX chn = 0; chn < m_nChannels; chn++) { if(file.Seek(expData.trackInfoOffset + chn * 4) && file.Seek(file.ReadUint32BE())) { uint32 nameOffset = 0, nameLength = 0; while(file.CanRead(sizeof(MMDTag))) { MMDTag tag; file.ReadStruct(tag); if(tag.type == MMDTag::MMDTAG_END) break; switch(tag.type & MMDTag::MMDTAG_MASK) { case MMDTag::MMDTAG_TRK_NAME: nameOffset = tag.data; break; case MMDTag::MMDTAG_TRK_NAMELEN: nameLength = tag.data; break; } } if(nameOffset > 0 && nameLength > 0 && file.Seek(nameOffset)) { file.ReadString(ChnSettings[chn].szName, nameLength); } } } } PATTERNINDEX numPatterns = songHeader.numBlocks; Patterns.ResizeArray(basePattern + numPatterns); for(PATTERNINDEX pat = 0; pat < numPatterns; pat++) { if(!(loadFlags & loadPatternData) || !file.Seek(fileHeader.blockArrOffset + pat * 4u) || !file.Seek(file.ReadUint32BE())) { continue; } CHANNELINDEX numTracks; ROWINDEX numRows; std::string patName; int transpose; FileReader cmdExt; if(version < 1) { transpose = NOTE_MIN + 47; MMD0PatternHeader patHeader; file.ReadStruct(patHeader); numTracks = patHeader.numTracks; numRows = patHeader.numRows + 1; } else { transpose = NOTE_MIN + (version <= 2 ? 47 : 23) + songHeader.playTranspose; MMD1PatternHeader patHeader; file.ReadStruct(patHeader); numTracks = patHeader.numTracks; numRows = patHeader.numRows + 1; if(patHeader.blockInfoOffset) { auto offset = file.GetPosition(); file.Seek(patHeader.blockInfoOffset); MMDBlockInfo blockInfo; file.ReadStruct(blockInfo); if(file.Seek(blockInfo.nameOffset)) { // We have now chased four pointers to get this far... lovely format. file.ReadString(patName, blockInfo.nameLength); } if(blockInfo.cmdExtTableOffset && file.Seek(blockInfo.cmdExtTableOffset) && file.Seek(file.ReadUint32BE())) { cmdExt = file.ReadChunk(numTracks * numRows); } file.Seek(offset); } } if(!Patterns.Insert(basePattern + pat, numRows)) continue; CPattern &pattern = Patterns[basePattern + pat]; pattern.SetName(patName); LimitMax(numTracks, m_nChannels); for(ROWINDEX row = 0; row < numRows; row++) { ModCommand *m = pattern.GetpModCommand(row, 0); for(CHANNELINDEX chn = 0; chn < numTracks; chn++, m++) { int note = NOTE_NONE; if(version < 1) { const auto [noteInstr, instrCmd, param] = file.ReadArray(); if(noteInstr & 0x3F) note = (noteInstr & 0x3F) + transpose; m->instr = (instrCmd >> 4) | ((noteInstr & 0x80) >> 3) | ((noteInstr & 0x40) >> 1); m->command = instrCmd & 0x0F; m->param = param; } else { const auto [noteVal, instr, command, param1] = file.ReadArray(); m->vol = cmdExt.ReadUint8(); if(noteVal & 0x7F) note = (noteVal & 0x7F) + transpose; else if(noteVal == 0x80) m->note = NOTE_NOTECUT; m->instr = instr & 0x3F; m->command = command; m->param = param1; } // Octave wrapping for 4-channel modules (TODO: this should not be set because of synth instruments) if(hardwareMixSamples && note >= NOTE_MIDDLEC + 2 * 12) needInstruments = true; if(note >= NOTE_MIN && note <= NOTE_MAX) m->note = static_cast(note); ConvertMEDEffect(*m, is8Ch, bpmMode, rowsPerBeat, volHex); } } } // Fix jump order commands for(const auto & [from, to] : jumpTargets) { PATTERNINDEX pat; if(from > 0 && order.IsValidPat(from - 1)) { pat = order.EnsureUnique(from - 1); } else { if(to == from + 1) // No action required continue; pat = Patterns.InsertAny(1); if(pat == PATTERNINDEX_INVALID) continue; order[from] = pat; } Patterns[pat].WriteEffect(EffectWriter(CMD_POSITIONJUMP, mpt::saturate_cast(to)).Row(Patterns[pat].GetNumRows() - 1).RetryPreviousRow()); if(pat >= numPatterns) numPatterns = pat + 1; } basePattern += numPatterns; if(!expData.nextModOffset || !file.Seek(expData.nextModOffset)) break; } Order.SetSequence(0); if(!needInstruments) { for(INSTRUMENTINDEX ins = 1; ins <= m_nInstruments; ins++) { delete Instruments[ins]; Instruments[ins] = nullptr; } m_nInstruments = 0; } if(anySynthInstrs) AddToLog(LogWarning, U_("Synthesized MED instruments are not supported.")); const mpt::uchar *madeWithTracker = MPT_ULITERAL(""); switch(version) { case 0: madeWithTracker = m_nChannels > 4 ? MPT_ULITERAL("OctaMED v2.10 (MMD0)") : MPT_ULITERAL("MED v2 (MMD0)"); break; case 1: madeWithTracker = MPT_ULITERAL("OctaMED v4 (MMD1)"); break; case 2: madeWithTracker = MPT_ULITERAL("OctaMED v5 (MMD2)"); break; case 3: madeWithTracker = MPT_ULITERAL("OctaMED Soundstudio (MMD3)"); break; } m_modFormat.formatName = MPT_UFORMAT("OctaMED (MMD{})")(version); m_modFormat.type = MPT_USTRING("med"); m_modFormat.madeWithTracker = madeWithTracker; m_modFormat.charset = mpt::Charset::ISO8859_1; return true; } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Load_mid.cpp0000644000175000017500000011230014111260440020275 00000000000000/* * Load_mid.cpp * ------------ * Purpose: MIDI file loader * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Loaders.h" #include "Dlsbank.h" #include "MIDIEvents.h" #ifdef MODPLUG_TRACKER #include "../mptrack/TrackerSettings.h" #include "../mptrack/Moddoc.h" #include "../mptrack/Mptrack.h" #include "../common/mptFileIO.h" #endif // MODPLUG_TRACKER OPENMPT_NAMESPACE_BEGIN #if defined(MODPLUG_TRACKER) || defined(MPT_FUZZ_TRACKER) #ifdef LIBOPENMPT_BUILD struct CDLSBank { static int32 DLSMidiVolumeToLinear(uint32) { return 256; } }; #endif // LIBOPENMPT_BUILD #define MIDI_DRUMCHANNEL 10 const char *szMidiGroupNames[17] = { "Piano", "Chromatic Percussion", "Organ", "Guitar", "Bass", "Strings", "Ensemble", "Brass", "Reed", "Pipe", "Synth Lead", "Synth Pad", "Synth Effects", "Ethnic", "Percussive", "Sound Effects", "Percussions" }; const char *szMidiProgramNames[128] = { // 1-8: Piano "Acoustic Grand Piano", "Bright Acoustic Piano", "Electric Grand Piano", "Honky-tonk Piano", "Electric Piano 1", "Electric Piano 2", "Harpsichord", "Clavi", // 9-16: Chromatic Percussion "Celesta", "Glockenspiel", "Music Box", "Vibraphone", "Marimba", "Xylophone", "Tubular Bells", "Dulcimer", // 17-24: Organ "Drawbar Organ", "Percussive Organ", "Rock Organ", "Church Organ", "Reed Organ", "Accordion", "Harmonica", "Tango Accordion", // 25-32: Guitar "Acoustic Guitar (nylon)", "Acoustic Guitar (steel)", "Electric Guitar (jazz)", "Electric Guitar (clean)", "Electric Guitar (muted)", "Overdriven Guitar", "Distortion Guitar", "Guitar harmonics", // 33-40 Bass "Acoustic Bass", "Electric Bass (finger)", "Electric Bass (pick)", "Fretless Bass", "Slap Bass 1", "Slap Bass 2", "Synth Bass 1", "Synth Bass 2", // 41-48 Strings "Violin", "Viola", "Cello", "Contrabass", "Tremolo Strings", "Pizzicato Strings", "Orchestral Harp", "Timpani", // 49-56 Ensemble "String Ensemble 1", "String Ensemble 2", "SynthStrings 1", "SynthStrings 2", "Choir Aahs", "Voice Oohs", "Synth Voice", "Orchestra Hit", // 57-64 Brass "Trumpet", "Trombone", "Tuba", "Muted Trumpet", "French Horn", "Brass Section", "SynthBrass 1", "SynthBrass 2", // 65-72 Reed "Soprano Sax", "Alto Sax", "Tenor Sax", "Baritone Sax", "Oboe", "English Horn", "Bassoon", "Clarinet", // 73-80 Pipe "Piccolo", "Flute", "Recorder", "Pan Flute", "Blown Bottle", "Shakuhachi", "Whistle", "Ocarina", // 81-88 Synth Lead "Lead 1 (square)", "Lead 2 (sawtooth)", "Lead 3 (calliope)", "Lead 4 (chiff)", "Lead 5 (charang)", "Lead 6 (voice)", "Lead 7 (fifths)", "Lead 8 (bass + lead)", // 89-96 Synth Pad "Pad 1 (new age)", "Pad 2 (warm)", "Pad 3 (polysynth)", "Pad 4 (choir)", "Pad 5 (bowed)", "Pad 6 (metallic)", "Pad 7 (halo)", "Pad 8 (sweep)", // 97-104 Synth Effects "FX 1 (rain)", "FX 2 (soundtrack)", "FX 3 (crystal)", "FX 4 (atmosphere)", "FX 5 (brightness)", "FX 6 (goblins)", "FX 7 (echoes)", "FX 8 (sci-fi)", // 105-112 Ethnic "Sitar", "Banjo", "Shamisen", "Koto", "Kalimba", "Bag pipe", "Fiddle", "Shanai", // 113-120 Percussive "Tinkle Bell", "Agogo", "Steel Drums", "Woodblock", "Taiko Drum", "Melodic Tom", "Synth Drum", "Reverse Cymbal", // 121-128 Sound Effects "Guitar Fret Noise", "Breath Noise", "Seashore", "Bird Tweet", "Telephone Ring", "Helicopter", "Applause", "Gunshot" }; // Notes 25-85 const char *szMidiPercussionNames[61] = { "Seq Click", "Brush Tap", "Brush Swirl", "Brush Slap", "Brush Swirl W/Attack", "Snare Roll", "Castanet", "Snare Lo", "Sticks", "Bass Drum Lo", "Open Rim Shot", "Acoustic Bass Drum", "Bass Drum 1", "Side Stick", "Acoustic Snare", "Hand Clap", "Electric Snare", "Low Floor Tom", "Closed Hi-Hat", "High Floor Tom", "Pedal Hi-Hat", "Low Tom", "Open Hi-Hat", "Low-Mid Tom", "Hi Mid Tom", "Crash Cymbal 1", "High Tom", "Ride Cymbal 1", "Chinese Cymbal", "Ride Bell", "Tambourine", "Splash Cymbal", "Cowbell", "Crash Cymbal 2", "Vibraslap", "Ride Cymbal 2", "Hi Bongo", "Low Bongo", "Mute Hi Conga", "Open Hi Conga", "Low Conga", "High Timbale", "Low Timbale", "High Agogo", "Low Agogo", "Cabasa", "Maracas", "Short Whistle", "Long Whistle", "Short Guiro", "Long Guiro", "Claves", "Hi Wood Block", "Low Wood Block", "Mute Cuica", "Open Cuica", "Mute Triangle", "Open Triangle", "Shaker", "Jingle Bell", "Bell Tree", }; //////////////////////////////////////////////////////////////////////////////// // Maps a midi instrument - returns the instrument number in the file uint32 CSoundFile::MapMidiInstrument(uint8 program, uint16 bank, uint8 midiChannel, uint8 note, bool isXG, std::bitset<16> drumChns) { ModInstrument *pIns; program &= 0x7F; bank &= 0x3FFF; note &= 0x7F; // In XG mode, extra drums are on banks with MSB 7F const bool isDrum = drumChns[midiChannel - 1] || (bank >= 0x3F80 && isXG); for (uint32 i = 1; i <= m_nInstruments; i++) if (Instruments[i]) { ModInstrument *p = Instruments[i]; // Drum Kit? if (isDrum) { if (note == p->nMidiDrumKey && bank + 1 == p->wMidiBank) return i; } else // Melodic Instrument { if (program + 1 == p->nMidiProgram && bank + 1 == p->wMidiBank && p->nMidiDrumKey == 0) return i; } } if(!CanAddMoreInstruments() || !CanAddMoreSamples()) return 0; pIns = AllocateInstrument(m_nInstruments + 1); if(pIns == nullptr) { return 0; } m_nSamples++; pIns->wMidiBank = bank + 1; pIns->nMidiProgram = program + 1; pIns->nFadeOut = 1024; pIns->nNNA = NewNoteAction::NoteOff; pIns->nDCT = isDrum ? DuplicateCheckType::Sample : DuplicateCheckType::Note; pIns->nDNA = DuplicateNoteAction::NoteFade; if(isDrum) { pIns->nMidiChannel = MIDI_DRUMCHANNEL; pIns->nMidiDrumKey = note; for(auto &key : pIns->NoteMap) { key = NOTE_MIDDLEC; } } pIns->VolEnv.dwFlags.set(ENV_ENABLED); if (!isDrum) pIns->VolEnv.dwFlags.set(ENV_SUSTAIN); pIns->VolEnv.reserve(4); pIns->VolEnv.push_back(EnvelopeNode(0, ENVELOPE_MAX)); pIns->VolEnv.push_back(EnvelopeNode(10, ENVELOPE_MAX)); pIns->VolEnv.push_back(EnvelopeNode(15, (ENVELOPE_MAX + ENVELOPE_MID) / 2)); pIns->VolEnv.push_back(EnvelopeNode(20, ENVELOPE_MIN)); pIns->VolEnv.nSustainStart = pIns->VolEnv.nSustainEnd = 1; // Set GM program / drum name if (!isDrum) { pIns->name = szMidiProgramNames[program]; } else { if (note >= 24 && note <= 84) pIns->name = szMidiPercussionNames[note - 24]; else pIns->name = "Percussions"; } return m_nInstruments; } struct MThd { uint32be headerLength; uint16be format; // 0 = single-track, 1 = multi-track, 2 = multi-song uint16be numTracks; // Number of track chunks uint16be division; // Delta timing value: positive = units/beat; negative = smpte compatible units }; MPT_BINARY_STRUCT(MThd, 10) using tick_t = uint32; struct TrackState { FileReader track; tick_t nextEvent = 0; uint8 command = 0; bool finished = false; }; struct ModChannelState { static constexpr uint8 NOMIDI = 0xFF; // No MIDI channel assigned. tick_t age = 0; // At which MIDI tick the channel was triggered int32 porta = 0; // Current portamento position in extra-fine slide units (1/64th of a semitone) uint8 vol = 100; // MIDI note volume (0...127) uint8 pan = 128; // MIDI channel panning (0...256) uint8 midiCh = NOMIDI; // MIDI channel that was last played on this channel ModCommand::NOTE note = NOTE_NONE; // MIDI note that was last played on this channel bool sustained = false; // If true, the note was already released by a note-off event, but sustain pedal CC is still active }; struct MidiChannelState { int32 pitchbendMod = 0; // Pre-computed pitchbend in extra-fine slide units (1/64th of a semitone) int16 pitchbend = MIDIEvents::pitchBendCentre; // 0...16383 uint16 bank = 0; // 0...16383 uint8 program = 0; // 0...127 // -- Controllers ---------------- function ---------- CC# --- range ---- init (midi) --- uint8 pan = 128; // Channel Panning 10 [0-255] 128 (64) uint8 expression = 128; // Channel Expression 11 0-128 128 (127) uint8 volume = 80; // Channel Volume 7 0-128 80 (100) uint16 rpn = 0x3FFF; // Currently selected RPN 100/101 n/a uint8 pitchBendRange = 2; // Pitch Bend Range 2 int8 transpose = 0; // Channel transpose 0 bool monoMode = false; // Mono/Poly operation 126/127 n/a Poly bool sustain = false; // Sustain pedal 64 on/off off std::array noteOn; // Value != CHANNELINDEX_INVALID: Note is active and mapped to mod channel in value MidiChannelState() { noteOn.fill(CHANNELINDEX_INVALID); } void SetPitchbend(uint16 value) { pitchbend = value; // Convert from arbitrary MIDI pitchbend to 64th of semitone pitchbendMod = Util::muldiv(pitchbend - MIDIEvents::pitchBendCentre, pitchBendRange * 64, MIDIEvents::pitchBendCentre); } void ResetAllControllers() { expression = 128; pitchBendRange = 2; SetPitchbend(MIDIEvents::pitchBendCentre); transpose = 0; rpn = 0x3FFF; monoMode = false; sustain = false; // Should also reset modulation, pedals (40h-43h), aftertouch } void SetRPN(uint8 value) { switch(rpn) { case 0: // Pitch Bend Range pitchBendRange = std::max(value, uint8(1)); SetPitchbend(pitchbend); break; case 2: // Coarse Tune transpose = static_cast(value) - 64; break; } } void SetRPNRelative(int8 value) { switch(rpn) { case 0: // Pitch Bend Range pitchBendRange = static_cast(std::clamp(pitchBendRange + value, 1, 0x7F)); break; case 2: // Coarse Tune transpose = mpt::saturate_cast(transpose + value); break; } } }; static CHANNELINDEX FindUnusedChannel(uint8 midiCh, ModCommand::NOTE note, const std::vector &channels, bool monoMode, PatternRow patRow) { for(size_t i = 0; i < channels.size(); i++) { // Check if this note is already playing, or find any note of the same MIDI channel in case of mono mode if(channels[i].midiCh == midiCh && (channels[i].note == note || (monoMode && channels[i].note != NOTE_NONE))) { return static_cast(i); } } CHANNELINDEX anyUnusedChannel = CHANNELINDEX_INVALID; CHANNELINDEX anyFreeChannel = CHANNELINDEX_INVALID; CHANNELINDEX oldsetMidiCh = CHANNELINDEX_INVALID; tick_t oldestMidiChAge = std::numeric_limits::max(); CHANNELINDEX oldestAnyCh = 0; tick_t oldestAnyChAge = std::numeric_limits::max(); for(size_t i = 0; i < channels.size(); i++) { if(channels[i].note == NOTE_NONE && !patRow[i].IsNote()) { // Recycle channel previously used by the same MIDI channel if(channels[i].midiCh == midiCh) return static_cast(i); // If we cannot find a channel that was already used for the same MIDI channel, try a completely unused channel next else if(channels[i].midiCh == ModChannelState::NOMIDI && anyUnusedChannel == CHANNELINDEX_INVALID) anyUnusedChannel = static_cast(i); // And if that fails, try any channel that currently doesn't play a note. if(anyFreeChannel == CHANNELINDEX_INVALID) anyFreeChannel = static_cast(i); } // If we can't find any free channels, look for the oldest channels if(channels[i].midiCh == midiCh && channels[i].age < oldestMidiChAge) { // Oldest channel matching this MIDI channel oldestMidiChAge = channels[i].age; oldsetMidiCh = static_cast(i); } else if(channels[i].age < oldestAnyChAge) { // Any oldest channel oldestAnyChAge = channels[i].age; oldestAnyCh = static_cast(i); } } if(anyUnusedChannel != CHANNELINDEX_INVALID) return anyUnusedChannel; if(anyFreeChannel != CHANNELINDEX_INVALID) return anyFreeChannel; if(oldsetMidiCh != CHANNELINDEX_INVALID) return oldsetMidiCh; return oldestAnyCh; } static void MIDINoteOff(MidiChannelState &midiChn, std::vector &modChnStatus, uint8 note, uint8 delay, PatternRow patRow, std::bitset<16> drumChns) { CHANNELINDEX chn = midiChn.noteOn[note]; if(chn == CHANNELINDEX_INVALID) return; if(midiChn.sustain) { // Turn this off later modChnStatus[chn].sustained = true; return; } uint8 midiCh = modChnStatus[chn].midiCh; modChnStatus[chn].note = NOTE_NONE; modChnStatus[chn].sustained = false; midiChn.noteOn[note] = CHANNELINDEX_INVALID; ModCommand &m = patRow[chn]; if(m.note == NOTE_NONE) { m.note = NOTE_KEYOFF; if(delay != 0) { m.command = CMD_S3MCMDEX; m.param = 0xD0 | delay; } } else if(m.IsNote() && !drumChns[midiCh]) { // Only do note cuts for melodic instruments - they sound weird on drums which should fade out naturally. if(m.command == CMD_S3MCMDEX && (m.param & 0xF0) == 0xD0) { // Already have a note delay m.command = CMD_DELAYCUT; m.param = (m.param << 4) | (delay - (m.param & 0x0F)); } else if(m.command == CMD_NONE || m.command == CMD_PANNING8) { m.command = CMD_S3MCMDEX; m.param = 0xC0 | delay; } } } static void EnterMIDIVolume(ModCommand &m, ModChannelState &modChn, const MidiChannelState &midiChn) { m.volcmd = VOLCMD_VOLUME; int32 vol = CDLSBank::DLSMidiVolumeToLinear(modChn.vol) >> 8; vol = (vol * midiChn.volume * midiChn.expression) >> 13; Limit(vol, 4, 256); m.vol = static_cast(vol / 4); } CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderMID(MemoryFileReader file, const uint64 *pfilesize) { MPT_UNREFERENCED_PARAMETER(pfilesize); char magic[4]; file.ReadArray(magic); if(!memcmp(magic, "MThd", 4)) return ProbeSuccess; if(!memcmp(magic, "RIFF", 4) && file.Skip(4) && file.ReadMagic("RMID")) return ProbeSuccess; return ProbeFailure; } bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags) { file.Rewind(); // Microsoft MIDI files bool isRIFF = false; if(file.ReadMagic("RIFF")) { file.Skip(4); if(!file.ReadMagic("RMID")) { return false; } else if(loadFlags == onlyVerifyHeader) { return true; } do { char id[4]; file.ReadArray(id); uint32 length = file.ReadUint32LE(); if(memcmp(id, "data", 4)) { file.Skip(length); } else { isRIFF = true; break; } } while(file.CanRead(8)); } MThd fileHeader; if(!file.ReadMagic("MThd") || !file.ReadStruct(fileHeader) || fileHeader.numTracks == 0 || fileHeader.headerLength < 6 || !file.Skip(fileHeader.headerLength - 6)) { return false; } else if(loadFlags == onlyVerifyHeader) { return true; } InitializeGlobals(MOD_TYPE_MID); InitializeChannels(); #ifdef MODPLUG_TRACKER const uint32 quantize = Clamp(TrackerSettings::Instance().midiImportQuantize.Get(), 4u, 256u); const ROWINDEX patternLen = Clamp(TrackerSettings::Instance().midiImportPatternLen.Get(), ROWINDEX(1), MAX_PATTERN_ROWS); const uint8 ticksPerRow = Clamp(TrackerSettings::Instance().midiImportTicks.Get(), uint8(2), uint8(16)); #else const uint32 quantize = 32; // Must be 4 or higher const ROWINDEX patternLen = 128; const uint8 ticksPerRow = 16; // Must be in range 2...16 #endif #ifdef MPT_FUZZ_TRACKER // Avoid generating test cases that take overly long to evaluate const ORDERINDEX MPT_MIDI_IMPORT_MAX_ORDERS = 64; #else const ORDERINDEX MPT_MIDI_IMPORT_MAX_ORDERS = MAX_ORDERS; #endif m_songArtist = U_("MIDI Conversion"); m_modFormat.formatName = U_("Standard MIDI File"); m_modFormat.type = isRIFF ? UL_("rmi") : UL_("mid"); m_modFormat.madeWithTracker = U_("Standard MIDI File"); m_modFormat.charset = mpt::Charset::ISO8859_1; SetMixLevels(MixLevels::v1_17RC3); m_nTempoMode = TempoMode::Modern; m_SongFlags = SONG_LINEARSLIDES; m_nDefaultTempo.Set(120); m_nDefaultSpeed = ticksPerRow; m_nChannels = MAX_BASECHANNELS; m_nDefaultRowsPerBeat = quantize / 4; m_nDefaultRowsPerMeasure = 4 * m_nDefaultRowsPerBeat; m_nSamplePreAmp = m_nVSTiVolume = 32; TEMPO tempo = m_nDefaultTempo; uint16 ppqn = fileHeader.division; if(ppqn & 0x8000) { // SMPTE compatible units (approximation) int frames = 256 - (ppqn >> 8), subFrames = (ppqn & 0xFF); ppqn = static_cast(frames * subFrames / 2); } if(!ppqn) ppqn = 96; Order().clear(); MidiChannelState midiChnStatus[16]; const CHANNELINDEX tempoChannel = m_nChannels - 2, globalVolChannel = m_nChannels - 1; const uint16 numTracks = fileHeader.numTracks; std::vector tracks(numTracks); std::vector modChnStatus(m_nChannels); std::bitset<16> drumChns; drumChns.set(MIDI_DRUMCHANNEL - 1); tick_t timeShift = 0; for(auto &track : tracks) { if(!file.ReadMagic("MTrk")) return false; track.track = file.ReadChunk(file.ReadUint32BE()); tick_t delta = 0; track.track.ReadVarInt(delta); // Work-around for some MID files that assume that negative deltas exist (they don't according to the standard) if(delta > int32_max) timeShift = std::max(static_cast(~delta + 1), timeShift); track.nextEvent = delta; } if(timeShift != 0) { for(auto &track : tracks) { if(track.nextEvent > int32_max) track.nextEvent = timeShift - static_cast(~track.nextEvent + 1); else track.nextEvent += timeShift; } } uint16 finishedTracks = 0; PATTERNINDEX emptyPattern = PATTERNINDEX_INVALID; ORDERINDEX lastOrd = 0, loopEndOrd = ORDERINDEX_INVALID; ROWINDEX lastRow = 0, loopEndRow = ROWINDEX_INVALID; ROWINDEX restartRow = ROWINDEX_INVALID; int8 masterTranspose = 0; bool isXG = false; bool isEMIDI = false; bool isEMIDILoop = false; const bool isType2 = (fileHeader.format == 2); const auto ModPositionFromTick = [&](const tick_t tick, const tick_t offset = 0) { tick_t modTicks = Util::muldivr_unsigned(tick, quantize * ticksPerRow, ppqn * 4u) - offset; ORDERINDEX ord = static_cast((modTicks / ticksPerRow) / patternLen); ROWINDEX row = (modTicks / ticksPerRow) % patternLen; uint8 delay = static_cast(modTicks % ticksPerRow); return std::make_tuple(ord, row, delay); }; while(finishedTracks < numTracks) { uint16 t = 0; tick_t tick = std::numeric_limits::max(); for(uint16 track = 0; track < numTracks; track++) { if(!tracks[track].finished && tracks[track].nextEvent < tick) { tick = tracks[track].nextEvent; t = track; if(isType2) break; } } FileReader &track = tracks[t].track; const auto [ord, row, delay] = ModPositionFromTick(tick); if(ord >= Order().GetLength()) { if(ord > MPT_MIDI_IMPORT_MAX_ORDERS) break; ORDERINDEX curSize = Order().GetLength(); // If we need to extend the order list by more than one pattern, this means that we // will be filling in empty patterns. Just recycle one empty pattern for this job. // We read events in chronological order, so it is never possible for the loader to // "jump back" to one of those empty patterns and write into it. if(ord > curSize && emptyPattern == PATTERNINDEX_INVALID) { if((emptyPattern = Patterns.InsertAny(patternLen)) == PATTERNINDEX_INVALID) break; } Order().resize(ord + 1, emptyPattern); if((Order()[ord] = Patterns.InsertAny(patternLen)) == PATTERNINDEX_INVALID) break; } // Keep track of position of last event for resizing the last pattern if(ord > lastOrd) { lastOrd = ord; lastRow = row; } else if(ord == lastOrd) { lastRow = std::max(lastRow, row); } PATTERNINDEX pat = Order()[ord]; PatternRow patRow = Patterns[pat].GetRow(row); uint8 data1 = track.ReadUint8(); if(data1 == 0xFF) { // Meta events data1 = track.ReadUint8(); size_t len = 0; track.ReadVarInt(len); FileReader chunk = track.ReadChunk(len); switch(data1) { case 1: // Text case 2: // Copyright m_songMessage.Read(chunk, len, SongMessage::leAutodetect); break; case 3: // Track Name if(len > 0) { std::string s; chunk.ReadString(s, len); if(!m_songMessage.empty()) m_songMessage.append(1, SongMessage::InternalLineEnding); m_songMessage += s; if(m_songName.empty()) m_songName = s; } break; case 4: // Instrument case 5: // Lyric break; case 6: // Marker case 7: // Cue point { std::string s; chunk.ReadString(s, len); Patterns[pat].SetName(s); if(!mpt::CompareNoCaseAscii(s, "loopStart")) { Order().SetRestartPos(ord); restartRow = row; } else if(!mpt::CompareNoCaseAscii(s, "loopEnd")) { std::tie(loopEndOrd, loopEndRow, std::ignore) = ModPositionFromTick(tick, 1); } } break; case 8: // Patch name case 9: // Port name break; case 0x2F: // End Of Track tracks[t].finished = true; break; case 0x51: // Tempo { uint32 tempoInt = chunk.ReadUint24BE(); if(tempoInt == 0) break; TEMPO newTempo(60000000.0 / tempoInt); if(!tick) { m_nDefaultTempo = newTempo; } else if(newTempo != tempo) { patRow[tempoChannel].command = CMD_TEMPO; patRow[tempoChannel].param = mpt::saturate_round(std::max(32.0, newTempo.ToDouble())); } tempo = newTempo; } break; default: break; } } else { uint8 command = tracks[t].command; if(data1 & 0x80) { // Command byte (if not present, use running status for channel messages) command = data1; if(data1 < 0xF0) { tracks[t].command = data1; data1 = track.ReadUint8(); } } uint8 midiCh = command & 0x0F; switch(command & 0xF0) { case 0x80: // Note Off case 0x90: // Note On { data1 &= 0x7F; ModCommand::NOTE note = static_cast(Clamp(data1 + NOTE_MIN, NOTE_MIN, NOTE_MAX)); uint8 data2 = track.ReadUint8(); if(data2 > 0 && (command & 0xF0) == 0x90) { // Note On CHANNELINDEX chn = FindUnusedChannel(midiCh, note, modChnStatus, midiChnStatus[midiCh].monoMode, patRow); if(chn != CHANNELINDEX_INVALID) { modChnStatus[chn].age = tick; modChnStatus[chn].note = note; modChnStatus[chn].midiCh = midiCh; modChnStatus[chn].vol = data2; modChnStatus[chn].sustained = false; midiChnStatus[midiCh].noteOn[data1] = chn; int32 pitchOffset = 0; if(midiChnStatus[midiCh].pitchbendMod != 0) { pitchOffset = (midiChnStatus[midiCh].pitchbendMod + (midiChnStatus[midiCh].pitchbendMod > 0 ? 32 : -32)) / 64; modChnStatus[chn].porta = pitchOffset * 64; } else { modChnStatus[chn].porta = 0; } patRow[chn].note = static_cast(Clamp(note + pitchOffset + midiChnStatus[midiCh].transpose + masterTranspose, NOTE_MIN, NOTE_MAX)); patRow[chn].instr = mpt::saturate_cast(MapMidiInstrument(midiChnStatus[midiCh].program, midiChnStatus[midiCh].bank, midiCh + 1, data1, isXG, drumChns)); EnterMIDIVolume(patRow[chn], modChnStatus[chn], midiChnStatus[midiCh]); if(patRow[chn].command == CMD_PORTAMENTODOWN || patRow[chn].command == CMD_PORTAMENTOUP) { patRow[chn].command = CMD_NONE; } if(delay != 0) { patRow[chn].command = CMD_S3MCMDEX; patRow[chn].param = 0xD0 | delay; } if(modChnStatus[chn].pan != midiChnStatus[midiCh].pan && patRow[chn].command == CMD_NONE) { patRow[chn].command = CMD_PANNING8; patRow[chn].param = midiChnStatus[midiCh].pan; modChnStatus[chn].pan = midiChnStatus[midiCh].pan; } } } else { // Note Off MIDINoteOff(midiChnStatus[midiCh], modChnStatus, data1, delay, patRow, drumChns); } } break; case 0xA0: // Note Aftertouch { track.Skip(1); } break; case 0xB0: // Controller { uint8 data2 = track.ReadUint8(); switch(data1) { case MIDIEvents::MIDICC_Panposition_Coarse: midiChnStatus[midiCh].pan = data2 * 2u; for(auto chn : midiChnStatus[midiCh].noteOn) { if(chn != CHANNELINDEX_INVALID && modChnStatus[chn].pan != midiChnStatus[midiCh].pan) { if(Patterns[pat].WriteEffect(EffectWriter(CMD_PANNING8, midiChnStatus[midiCh].pan).Channel(chn).Row(row))) { modChnStatus[chn].pan = midiChnStatus[midiCh].pan; } } } break; case MIDIEvents::MIDICC_DataEntry_Coarse: midiChnStatus[midiCh].SetRPN(data2); break; case MIDIEvents::MIDICC_Volume_Coarse: midiChnStatus[midiCh].volume = (uint8)(CDLSBank::DLSMidiVolumeToLinear(data2) >> 9); for(auto chn : midiChnStatus[midiCh].noteOn) { if(chn != CHANNELINDEX_INVALID) { EnterMIDIVolume(patRow[chn], modChnStatus[chn], midiChnStatus[midiCh]); } } break; case MIDIEvents::MIDICC_Expression_Coarse: midiChnStatus[midiCh].expression = (uint8)(CDLSBank::DLSMidiVolumeToLinear(data2) >> 9); for(auto chn : midiChnStatus[midiCh].noteOn) { if(chn != CHANNELINDEX_INVALID) { EnterMIDIVolume(patRow[chn], modChnStatus[chn], midiChnStatus[midiCh]); } } break; case MIDIEvents::MIDICC_BankSelect_Coarse: midiChnStatus[midiCh].bank &= 0x7F; midiChnStatus[midiCh].bank |= (data2 << 7); break; case MIDIEvents::MIDICC_BankSelect_Fine: midiChnStatus[midiCh].bank &= (0x7F << 7); midiChnStatus[midiCh].bank |= data2; break; case MIDIEvents::MIDICC_HoldPedal_OnOff: midiChnStatus[midiCh].sustain = (data2 >= 0x40); if(data2 < 0x40) { // Release notes that are still being held after note-off for(const auto &chnState : modChnStatus) { if(chnState.midiCh == midiCh && chnState.sustained && chnState.note != NOTE_NONE) { MIDINoteOff(midiChnStatus[midiCh], modChnStatus, chnState.note - NOTE_MIN, delay, patRow, drumChns); } } } break; case MIDIEvents::MIDICC_DataButtonincrement: case MIDIEvents::MIDICC_DataButtondecrement: midiChnStatus[midiCh].SetRPNRelative((data1 == MIDIEvents::MIDICC_DataButtonincrement) ? 1 : -1); break; case MIDIEvents::MIDICC_NonRegisteredParameter_Fine: case MIDIEvents::MIDICC_NonRegisteredParameter_Coarse: midiChnStatus[midiCh].rpn = 0x3FFF; break; case MIDIEvents::MIDICC_RegisteredParameter_Fine: midiChnStatus[midiCh].rpn &= (0x7F << 7); midiChnStatus[midiCh].rpn |= data2; break; case MIDIEvents::MIDICC_RegisteredParameter_Coarse: midiChnStatus[midiCh].rpn &= 0x7F; midiChnStatus[midiCh].rpn |= (data2 << 7); break; case 110: isEMIDI = true; break; case 111: // Non-standard MIDI loop point. May conflict with Apogee EMIDI CCs (110/111), which is why we also check if CC 110 is ever used. if(data2 == 0 && !isEMIDI) { Order().SetRestartPos(ord); restartRow = row; } break; case 118: // EMIDI Global Loop Start isEMIDI = true; isEMIDILoop = false; Order().SetRestartPos(ord); restartRow = row; break; case 119: // EMIDI Global Loop End if(data2 == 0x7F) { isEMIDILoop = true; isEMIDI = true; std::tie(loopEndOrd, loopEndRow, std::ignore) = ModPositionFromTick(tick, 1); } break; case MIDIEvents::MIDICC_AllControllersOff: midiChnStatus[midiCh].ResetAllControllers(); break; // Bn.78.00: All Sound Off (GS) // Bn.7B.00: All Notes Off (GM) case MIDIEvents::MIDICC_AllSoundOff: case MIDIEvents::MIDICC_AllNotesOff: // All Notes Off midiChnStatus[midiCh].sustain = false; for(uint8 note = 0; note < 128; note++) { MIDINoteOff(midiChnStatus[midiCh], modChnStatus, note, delay, patRow, drumChns); } break; case MIDIEvents::MIDICC_MonoOperation: if(data2 == 0) { midiChnStatus[midiCh].monoMode = true; } break; case MIDIEvents::MIDICC_PolyOperation: if(data2 == 0) { midiChnStatus[midiCh].monoMode = false; } break; } } break; case 0xC0: // Program Change midiChnStatus[midiCh].program = data1 & 0x7F; break; case 0xD0: // Channel aftertouch break; case 0xE0: // Pitch bend midiChnStatus[midiCh].SetPitchbend(data1 | (track.ReadUint8() << 7)); break; case 0xF0: // General / Immediate switch(midiCh) { case MIDIEvents::sysExStart: // SysEx case MIDIEvents::sysExEnd: // SysEx (continued) { uint32 len; track.ReadVarInt(len); FileReader sysex = track.ReadChunk(len); if(midiCh == MIDIEvents::sysExEnd) break; if(sysex.ReadMagic("\x7F\x7F\x04\x01")) { // Master volume uint8 volumeRaw[2]; sysex.ReadArray(volumeRaw); uint16 globalVol = volumeRaw[0] | (volumeRaw[1] << 7); if(tick == 0) { m_nDefaultGlobalVolume = Util::muldivr_unsigned(globalVol, MAX_GLOBAL_VOLUME, 16383); } else { patRow[globalVolChannel].command = CMD_GLOBALVOLUME; patRow[globalVolChannel].param = static_cast(Util::muldivr_unsigned(globalVol, 128, 16383)); } } else { uint8 xg[7]; sysex.ReadArray(xg); if(!memcmp(xg, "\x43\x10\x4C\x00\x00\x7E\x00", 7)) { // XG System On isXG = true; } else if(!memcmp(xg, "\x43\x10\x4C\x00\x00\x06", 6)) { // XG Master Transpose masterTranspose = static_cast(xg[6]) - 64; } else if(!memcmp(xg, "\x41\x10\x42\x12\x40", 5) && (xg[5] & 0xF0) == 0x10 && xg[6] == 0x15) { // GS Drum Kit uint8 chn = xg[5] & 0x0F; if(chn == 0) chn = 9; else if(chn < 10) chn--; drumChns.set(chn, sysex.ReadUint8() != 0); } } } break; case MIDIEvents::sysQuarterFrame: track.Skip(1); break; case MIDIEvents::sysPositionPointer: track.Skip(2); break; case MIDIEvents::sysSongSelect: track.Skip(1); break; case MIDIEvents::sysTuneRequest: case MIDIEvents::sysMIDIClock: case MIDIEvents::sysMIDITick: case MIDIEvents::sysStart: case MIDIEvents::sysContinue: case MIDIEvents::sysStop: case MIDIEvents::sysActiveSense: case MIDIEvents::sysReset: break; default: break; } break; default: break; } } // Pitch bend any channels that haven't reached their target yet // TODO: This is currently not called on any rows without events! for(size_t chn = 0; chn < modChnStatus.size(); chn++) { ModChannelState &chnState = modChnStatus[chn]; ModCommand &m = patRow[chn]; uint8 midiCh = chnState.midiCh; if(chnState.note == NOTE_NONE || m.command == CMD_S3MCMDEX || m.command == CMD_DELAYCUT || midiCh == ModChannelState::NOMIDI) continue; int32 diff = midiChnStatus[midiCh].pitchbendMod - chnState.porta; if(diff == 0) continue; if(m.command == CMD_PORTAMENTODOWN || m.command == CMD_PORTAMENTOUP) { // First, undo the effect of an existing portamento command int32 porta = 0; if(m.param < 0xE0) porta = m.param * 4 * (ticksPerRow - 1); else if(m.param < 0xF0) porta = (m.param & 0x0F); else porta = (m.param & 0x0F) * 4; if(m.command == CMD_PORTAMENTODOWN) porta = -porta; diff += porta; chnState.porta -= porta; if(diff == 0) { m.command = CMD_NONE; continue; } } m.command = static_cast(diff < 0 ? CMD_PORTAMENTODOWN : CMD_PORTAMENTOUP); int32 absDiff = std::abs(diff); int32 realDiff = 0; if(absDiff < 16) { // Extra-fine slides can do this. m.param = 0xE0 | static_cast(absDiff); realDiff = absDiff; } else if(absDiff < 64) { // Fine slides can do this. absDiff = std::min((absDiff + 3) / 4, 0x0F); m.param = 0xF0 | static_cast(absDiff); realDiff = absDiff * 4; } else { // Need a normal slide. absDiff /= 4 * (ticksPerRow - 1); LimitMax(absDiff, 0xDF); m.param = static_cast(absDiff); realDiff = absDiff * 4 * (ticksPerRow - 1); } chnState.porta += realDiff * mpt::signum(diff); } tick_t delta = 0; if(track.ReadVarInt(delta) && track.CanRead(1)) { tracks[t].nextEvent += delta; } else { finishedTracks++; tracks[t].nextEvent = Util::MaxValueOfType(delta); tracks[t].finished = true; // Add another sub-song for type-2 files if(isType2 && finishedTracks < numTracks) { if(Order.AddSequence() == SEQUENCEINDEX_INVALID) break; Order().clear(); } } } if(isEMIDILoop) isEMIDI = false; if(isEMIDI) { Order().SetRestartPos(0); } if(loopEndOrd == ORDERINDEX_INVALID) loopEndOrd = lastOrd; if(loopEndRow == ROWINDEX_INVALID) loopEndRow = lastRow; if(Order().IsValidPat(loopEndOrd)) { PATTERNINDEX lastPat = Order()[loopEndOrd]; if(loopEndOrd == lastOrd) Patterns[lastPat].Resize(loopEndRow + 1); if(restartRow != ROWINDEX_INVALID && !isEMIDI) { Patterns[lastPat].WriteEffect(EffectWriter(CMD_PATTERNBREAK, mpt::saturate_cast(restartRow)).Row(loopEndRow)); if(ORDERINDEX restartPos = Order().GetRestartPos(); loopEndOrd != lastOrd || restartPos <= std::numeric_limits::max()) Patterns[lastPat].WriteEffect(EffectWriter(CMD_POSITIONJUMP, mpt::saturate_cast(restartPos)).Row(loopEndRow)); } } Order.SetSequence(0); std::vector channels; channels.reserve(m_nChannels); for(CHANNELINDEX i = 0; i < m_nChannels; i++) { if(modChnStatus[i].midiCh != ModChannelState::NOMIDI #ifdef MODPLUG_TRACKER || (GetpModDoc() != nullptr && !GetpModDoc()->IsChannelUnused(i)) #endif // MODPLUG_TRACKER ) { channels.push_back(i); if(modChnStatus[i].midiCh != ModChannelState::NOMIDI) ChnSettings[i].szName = MPT_AFORMAT("MIDI Ch {}")(1 + modChnStatus[i].midiCh); else if(i == tempoChannel) ChnSettings[i].szName = "Tempo"; else if(i == globalVolChannel) ChnSettings[i].szName = "Global Volume"; } } if(channels.empty()) return false; #ifdef MODPLUG_TRACKER if(GetpModDoc() != nullptr) { // Keep MIDI channels in patterns neatly grouped std::sort(channels.begin(), channels.end(), [&modChnStatus] (CHANNELINDEX c1, CHANNELINDEX c2) { if(modChnStatus[c1].midiCh == modChnStatus[c2].midiCh) return c1 < c2; return modChnStatus[c1].midiCh < modChnStatus[c2].midiCh; }); GetpModDoc()->ReArrangeChannels(channels, false); GetpModDoc()->m_ShowSavedialog = true; } std::unique_ptr cachedBank, embeddedBank; if(CDLSBank::IsDLSBank(file.GetOptionalFileName().value_or(P_("")))) { // Soundfont embedded in MIDI file embeddedBank = std::make_unique(); embeddedBank->Open(file.GetOptionalFileName().value_or(P_(""))); } else { // Soundfont with same name as MIDI file for(const auto &ext : { P_(".sf2"), P_(".sf3"), P_(".sf4"), P_(".sbk"), P_(".dls") }) { mpt::PathString filename = file.GetOptionalFileName().value_or(P_("")).ReplaceExt(ext); if(filename.IsFile()) { embeddedBank = std::make_unique(); if(embeddedBank->Open(filename)) break; } } } ChangeModTypeTo(MOD_TYPE_MPT); const MidiLibrary &midiLib = CTrackApp::GetMidiLibrary(); mpt::PathString cachedBankName; // Load Instruments for (INSTRUMENTINDEX ins = 1; ins <= m_nInstruments; ins++) if (Instruments[ins]) { ModInstrument *pIns = Instruments[ins]; uint32 midiCode = 0; if(pIns->nMidiChannel == MIDI_DRUMCHANNEL) midiCode = 0x80 | (pIns->nMidiDrumKey & 0x7F); else if(pIns->nMidiProgram) midiCode = (pIns->nMidiProgram - 1) & 0x7F; if(embeddedBank && embeddedBank->FindAndExtract(*this, ins, midiCode >= 0x80)) { continue; } const mpt::PathString &midiMapName = midiLib[midiCode]; if(!midiMapName.empty()) { // Load from DLS/SF2 Bank if(CDLSBank::IsDLSBank(midiMapName)) { CDLSBank *dlsBank = nullptr; if(cachedBank != nullptr && !mpt::PathString::CompareNoCase(cachedBankName, midiMapName)) { dlsBank = cachedBank.get(); } else { cachedBank = std::make_unique(); cachedBankName = midiMapName; if(cachedBank->Open(midiMapName)) dlsBank = cachedBank.get(); } if(dlsBank) { dlsBank->FindAndExtract(*this, ins, midiCode >= 0x80); } } else { // Load from Instrument or Sample file InputFile f(midiMapName, SettingCacheCompleteFileBeforeLoading()); if(f.IsValid()) { FileReader insFile = GetFileReader(f); if(ReadInstrumentFromFile(ins, insFile, false)) { mpt::PathString filename = midiMapName.GetFullFileName(); pIns = Instruments[ins]; if(!pIns->filename[0]) pIns->filename = filename.ToLocale(); if(!pIns->name[0]) { if(midiCode < 0x80) { pIns->name = szMidiProgramNames[midiCode]; } else { uint32 key = midiCode & 0x7F; if((key >= 24) && (key < 24 + std::size(szMidiPercussionNames))) pIns->name = szMidiPercussionNames[key - 24]; } } } } } } } #endif // MODPLUG_TRACKER return true; } #else // !MODPLUG_TRACKER && !MPT_FUZZ_TRACKER bool CSoundFile::ReadMID(FileReader &/*file*/, ModLoadingFlags /*loadFlags*/) { return false; } #endif OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Load_mo3.cpp0000644000175000017500000017160114175523206020247 00000000000000/* * Load_mo3.cpp * ------------ * Purpose: MO3 module loader. * Notes : (currently none) * Authors: Johannes Schultz / OpenMPT Devs * Based on documentation and the decompression routines from the * open-source UNMO3 project (https://github.com/lclevy/unmo3). * The modified decompression code has been relicensed to the BSD * license with permission from Laurent Clévy. * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Loaders.h" #include "../common/ComponentManager.h" #include "mpt/io/base.hpp" #include "mpt/io/io.hpp" #include "mpt/io/io_stdstream.hpp" #include "Tables.h" #include "../common/version.h" #include "mpt/audio/span.hpp" #include "MPEGFrame.h" #include "OggStream.h" #if defined(MPT_WITH_VORBIS) && defined(MPT_WITH_VORBISFILE) #include #endif #if defined(MPT_WITH_VORBIS) #if MPT_COMPILER_CLANG #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wreserved-id-macro" #endif // MPT_COMPILER_CLANG #include #if MPT_COMPILER_CLANG #pragma clang diagnostic pop #endif // MPT_COMPILER_CLANG #endif #if defined(MPT_WITH_VORBISFILE) #if MPT_COMPILER_CLANG #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wreserved-id-macro" #endif // MPT_COMPILER_CLANG #include #if MPT_COMPILER_CLANG #pragma clang diagnostic pop #endif // MPT_COMPILER_CLANG #include "openmpt/soundbase/Copy.hpp" #endif #ifdef MPT_WITH_STBVORBIS #include #include "openmpt/soundbase/Copy.hpp" #endif // MPT_WITH_STBVORBIS OPENMPT_NAMESPACE_BEGIN struct MO3FileHeader { enum MO3HeaderFlags { linearSlides = 0x0001, isS3M = 0x0002, s3mFastSlides = 0x0004, isMTM = 0x0008, // Actually this is simply "not XM". But if none of the S3M, MOD and IT flags are set, it's an MTM. s3mAmigaLimits = 0x0010, // 0x20 and 0x40 have been used in old versions for things that can be inferred from the file format anyway. // The official UNMO3 ignores them. isMOD = 0x0080, isIT = 0x0100, instrumentMode = 0x0200, itCompatGxx = 0x0400, itOldFX = 0x0800, modplugMode = 0x10000, unknown = 0x20000, // Always set (internal BASS flag to designate modules) modVBlank = 0x80000, hasPlugins = 0x100000, extFilterRange = 0x200000, }; uint8le numChannels; // 1...64 (limited by channel panning and volume) uint16le numOrders; uint16le restartPos; uint16le numPatterns; uint16le numTracks; uint16le numInstruments; uint16le numSamples; uint8le defaultSpeed; uint8le defaultTempo; uint32le flags; // See MO3HeaderFlags uint8le globalVol; // 0...128 in IT, 0...64 in S3M uint8le panSeparation; // 0...128 in IT int8le sampleVolume; // Only used in IT uint8le chnVolume[64]; // 0...64 uint8le chnPan[64]; // 0...256, 127 = surround uint8le sfxMacros[16]; uint8le fixedMacros[128][2]; }; MPT_BINARY_STRUCT(MO3FileHeader, 422) struct MO3Envelope { enum MO3EnvelopeFlags { envEnabled = 0x01, envSustain = 0x02, envLoop = 0x04, envFilter = 0x10, envCarry = 0x20, }; uint8le flags; // See MO3EnvelopeFlags uint8le numNodes; uint8le sustainStart; uint8le sustainEnd; uint8le loopStart; uint8le loopEnd; int16le points[25][2]; // Convert MO3 envelope data into OpenMPT's internal envelope format void ConvertToMPT(InstrumentEnvelope &mptEnv, uint8 envShift) const { if(flags & envEnabled) mptEnv.dwFlags.set(ENV_ENABLED); if(flags & envSustain) mptEnv.dwFlags.set(ENV_SUSTAIN); if(flags & envLoop) mptEnv.dwFlags.set(ENV_LOOP); if(flags & envFilter) mptEnv.dwFlags.set(ENV_FILTER); if(flags & envCarry) mptEnv.dwFlags.set(ENV_CARRY); mptEnv.resize(std::min(numNodes.get(), uint8(25))); mptEnv.nSustainStart = sustainStart; mptEnv.nSustainEnd = sustainEnd; mptEnv.nLoopStart = loopStart; mptEnv.nLoopEnd = loopEnd; for(uint32 ev = 0; ev < mptEnv.size(); ev++) { mptEnv[ev].tick = points[ev][0]; if(ev > 0 && mptEnv[ev].tick < mptEnv[ev - 1].tick) mptEnv[ev].tick = mptEnv[ev - 1].tick + 1; mptEnv[ev].value = static_cast(Clamp(points[ev][1] >> envShift, 0, 64)); } } }; MPT_BINARY_STRUCT(MO3Envelope, 106) struct MO3Instrument { enum MO3InstrumentFlags { playOnMIDI = 0x01, mute = 0x02, }; uint32le flags; // See MO3InstrumentFlags uint16le sampleMap[120][2]; MO3Envelope volEnv; MO3Envelope panEnv; MO3Envelope pitchEnv; struct XMVibratoSettings { uint8le type; uint8le sweep; uint8le depth; uint8le rate; } vibrato; // Applies to all samples of this instrument (XM) uint16le fadeOut; uint8le midiChannel; uint8le midiBank; uint8le midiPatch; uint8le midiBend; uint8le globalVol; // 0...128 uint16le panning; // 0...256 if enabled, 0xFFFF otherwise uint8le nna; uint8le pps; uint8le ppc; uint8le dct; uint8le dca; uint16le volSwing; // 0...100 uint16le panSwing; // 0...256 uint8le cutoff; // 0...127, + 128 if enabled uint8le resonance; // 0...127, + 128 if enabled // Convert MO3 instrument data into OpenMPT's internal instrument format void ConvertToMPT(ModInstrument &mptIns, MODTYPE type) const { if(type == MOD_TYPE_XM) { for(size_t i = 0; i < 96; i++) { mptIns.Keyboard[i + 12] = sampleMap[i][1] + 1; } } else { for(size_t i = 0; i < 120; i++) { mptIns.NoteMap[i] = static_cast(sampleMap[i][0] + NOTE_MIN); mptIns.Keyboard[i] = sampleMap[i][1] + 1; } } volEnv.ConvertToMPT(mptIns.VolEnv, 0); panEnv.ConvertToMPT(mptIns.PanEnv, 0); pitchEnv.ConvertToMPT(mptIns.PitchEnv, 5); mptIns.nFadeOut = fadeOut; if(midiChannel >= 128) { // Plugin mptIns.nMixPlug = midiChannel - 127; } else if(midiChannel < 17 && (flags & playOnMIDI)) { // XM, or IT with recent encoder mptIns.nMidiChannel = midiChannel + MidiFirstChannel; } else if(midiChannel > 0 && midiChannel < 17) { // IT encoded with MO3 version prior to 2.4.1 (yes, channel 0 is represented the same way as "no channel") mptIns.nMidiChannel = midiChannel + MidiFirstChannel; } if(mptIns.nMidiChannel != MidiNoChannel) { if(type == MOD_TYPE_XM) { mptIns.nMidiProgram = midiPatch + 1; } else { if(midiBank < 128) mptIns.wMidiBank = midiBank + 1; if(midiPatch < 128) mptIns.nMidiProgram = midiPatch + 1; } mptIns.midiPWD = midiBend; } if(type == MOD_TYPE_IT) mptIns.nGlobalVol = std::min(static_cast(globalVol), uint8(128)) / 2u; if(panning <= 256) { mptIns.nPan = panning; mptIns.dwFlags.set(INS_SETPANNING); } mptIns.nNNA = static_cast(nna.get()); mptIns.nPPS = pps; mptIns.nPPC = ppc; mptIns.nDCT = static_cast(dct.get()); mptIns.nDNA = static_cast(dca.get()); mptIns.nVolSwing = static_cast(std::min(volSwing.get(), uint16(100))); mptIns.nPanSwing = static_cast(std::min(panSwing.get(), uint16(256)) / 4u); mptIns.SetCutoff(cutoff & 0x7F, (cutoff & 0x80) != 0); mptIns.SetResonance(resonance & 0x7F, (resonance & 0x80) != 0); } }; MPT_BINARY_STRUCT(MO3Instrument, 826) struct MO3Sample { enum MO3SampleFlags { smp16Bit = 0x01, smpLoop = 0x10, smpPingPongLoop = 0x20, smpSustain = 0x100, smpSustainPingPong = 0x200, smpStereo = 0x400, smpCompressionMPEG = 0x1000, // MPEG 1.0 / 2.0 / 2.5 sample smpCompressionOgg = 0x1000 | 0x2000, // Ogg sample smpSharedOgg = 0x1000 | 0x2000 | 0x4000, // Ogg sample with shared vorbis header smpDeltaCompression = 0x2000, // Deltas + compression smpDeltaPrediction = 0x4000, // Delta prediction + compression smpOPLInstrument = 0x8000, // OPL patch data smpCompressionMask = 0x1000 | 0x2000 | 0x4000 | 0x8000 }; uint32le freqFinetune; // Frequency in S3M and IT, finetune (0...255) in MOD, MTM, XM int8le transpose; uint8le defaultVolume; // 0...64 uint16le panning; // 0...256 if enabled, 0xFFFF otherwise uint32le length; uint32le loopStart; uint32le loopEnd; uint16le flags; // See MO3SampleFlags uint8le vibType; uint8le vibSweep; uint8le vibDepth; uint8le vibRate; uint8le globalVol; // 0...64 in IT, in XM it represents the instrument number uint32le sustainStart; uint32le sustainEnd; int32le compressedSize; uint16le encoderDelay; // MP3: Ignore first n bytes of decoded output. Ogg: Shared Ogg header size // Convert MO3 sample data into OpenMPT's internal instrument format void ConvertToMPT(ModSample &mptSmp, MODTYPE type, bool frequencyIsHertz) const { mptSmp.Initialize(); mptSmp.SetDefaultCuePoints(); if(type & (MOD_TYPE_IT | MOD_TYPE_S3M)) { if(frequencyIsHertz) mptSmp.nC5Speed = freqFinetune; else mptSmp.nC5Speed = mpt::saturate_round(8363.0 * std::pow(2.0, static_cast(freqFinetune + 1408) / 1536.0)); } else { mptSmp.nFineTune = static_cast(freqFinetune); if(type != MOD_TYPE_MTM) mptSmp.nFineTune -= 128; mptSmp.RelativeTone = transpose; } mptSmp.nVolume = std::min(defaultVolume.get(), uint8(64)) * 4u; if(panning <= 256) { mptSmp.nPan = panning; mptSmp.uFlags.set(CHN_PANNING); } mptSmp.nLength = length; mptSmp.nLoopStart = loopStart; mptSmp.nLoopEnd = loopEnd; if(flags & smpLoop) mptSmp.uFlags.set(CHN_LOOP); if(flags & smpPingPongLoop) mptSmp.uFlags.set(CHN_PINGPONGLOOP); if(flags & smpSustain) mptSmp.uFlags.set(CHN_SUSTAINLOOP); if(flags & smpSustainPingPong) mptSmp.uFlags.set(CHN_PINGPONGSUSTAIN); mptSmp.nVibType = static_cast(AutoVibratoIT2XM[vibType & 7]); mptSmp.nVibSweep = vibSweep; mptSmp.nVibDepth = vibDepth; mptSmp.nVibRate = vibRate; if(type == MOD_TYPE_IT) mptSmp.nGlobalVol = std::min(static_cast(globalVol), uint8(64)); mptSmp.nSustainStart = sustainStart; mptSmp.nSustainEnd = sustainEnd; } }; MPT_BINARY_STRUCT(MO3Sample, 41) // We need all this information for Ogg-compressed samples with shared headers: // A shared header can be taken from a sample that has not been read yet, so // we first need to read all headers, and then load the Ogg samples afterwards. struct MO3SampleChunk { FileReader chunk; uint16 headerSize; int16 sharedHeader; MO3SampleChunk(const FileReader &chunk_ = FileReader(), uint16 headerSize_ = 0, int16 sharedHeader_ = 0) : chunk(chunk_), headerSize(headerSize_), sharedHeader(sharedHeader_) {} }; // Unpack macros // shift control bits until it is empty: // a 0 bit means literal : the next data byte is copied // a 1 means compressed data // then the next 2 bits determines what is the LZ ptr // ('00' same as previous, else stored in stream) #define READ_CTRL_BIT \ data <<= 1; \ carry = (data > 0xFF); \ data &= 0xFF; \ if(data == 0) \ { \ uint8 nextByte; \ if(!file.Read(nextByte)) \ break; \ data = nextByte; \ data = (data << 1) + 1; \ carry = (data > 0xFF); \ data &= 0xFF; \ } // length coded within control stream: // most significant bit is 1 // then the first bit of each bits pair (noted n1), // until second bit is 0 (noted n0) #define DECODE_CTRL_BITS \ { \ strLen++; \ do \ { \ READ_CTRL_BIT; \ strLen = mpt::lshift_signed(strLen, 1) + carry; \ READ_CTRL_BIT; \ } while(carry); \ } static bool UnpackMO3Data(FileReader &file, std::vector &uncompressed, const uint32 size) { if(!size) return false; uint16 data = 0; int8 carry = 0; // x86 carry (used to propagate the most significant bit from one byte to another) int32 strLen = 0; // length of previous string int32 strOffset; // string offset uint32 previousPtr = 0; // Read first uncompressed byte uncompressed.push_back(file.ReadUint8()); uint32 remain = size - 1; while(remain > 0) { READ_CTRL_BIT; if(!carry) { // a 0 ctrl bit means 'copy', not compressed byte if(uint8 b; file.Read(b)) uncompressed.push_back(b); else break; remain--; } else { // a 1 ctrl bit means compressed bytes are following uint8 lengthAdjust = 0; // length adjustment DECODE_CTRL_BITS; // read length, and if strLen > 3 (coded using more than 1 bits pair) also part of the offset value strLen -= 3; if(strLen < 0) { // means LZ ptr with same previous relative LZ ptr (saved one) strOffset = previousPtr; // restore previous Ptr strLen++; } else { // LZ ptr in ctrl stream if(uint8 b; file.Read(b)) strOffset = mpt::lshift_signed(strLen, 8) | b; // read less significant offset byte from stream else break; strLen = 0; strOffset = ~strOffset; if(strOffset < -1280) lengthAdjust++; lengthAdjust++; // length is always at least 1 if(strOffset < -32000) lengthAdjust++; previousPtr = strOffset; // save current Ptr } // read the next 2 bits as part of strLen READ_CTRL_BIT; strLen = mpt::lshift_signed(strLen, 1) + carry; READ_CTRL_BIT; strLen = mpt::lshift_signed(strLen, 1) + carry; if(strLen == 0) { // length does not fit in 2 bits DECODE_CTRL_BITS; // decode length: 1 is the most significant bit, strLen += 2; // then first bit of each bits pairs (noted n1), until n0. } strLen += lengthAdjust; // length adjustment if(remain < static_cast(strLen) || strLen <= 0) break; if(strOffset >= 0 || -static_cast(uncompressed.size()) > strOffset) break; // Copy previous string // Need to do this in two steps as source and destination may overlap (e.g. strOffset = -1, strLen = 2 repeats last character twice) uncompressed.insert(uncompressed.end(), strLen, 0); remain -= strLen; auto src = uncompressed.cend() - strLen + strOffset; auto dst = uncompressed.end() - strLen; do { strLen--; *dst++ = *src++; } while(strLen > 0); } } #ifdef MPT_BUILD_FUZZER // When using a fuzzer, we should not care if the decompressed buffer has the correct size. // This makes finding new interesting test cases much easier. return true; #else return remain == 0; #endif // MPT_BUILD_FUZZER } struct MO3Delta8BitParams { using sample_t = int8; using unsigned_t = uint8; static constexpr int shift = 7; static constexpr uint8 dhInit = 4; static inline void Decode(FileReader &file, int8 &carry, uint16 &data, uint8 & /*dh*/, unsigned_t &val) { do { READ_CTRL_BIT; val = (val << 1) + carry; READ_CTRL_BIT; } while(carry); } }; struct MO3Delta16BitParams { using sample_t = int16; using unsigned_t = uint16; static constexpr int shift = 15; static constexpr uint8 dhInit = 8; static inline void Decode(FileReader &file, int8 &carry, uint16 &data, uint8 &dh, unsigned_t &val) { if(dh < 5) { do { READ_CTRL_BIT; val = (val << 1) + carry; READ_CTRL_BIT; val = (val << 1) + carry; READ_CTRL_BIT; } while(carry); } else { do { READ_CTRL_BIT; val = (val << 1) + carry; READ_CTRL_BIT; } while(carry); } } }; template static void UnpackMO3DeltaSample(FileReader &file, typename Properties::sample_t *dst, uint32 length, uint8 numChannels) { uint8 dh = Properties::dhInit, cl = 0; int8 carry = 0; uint16 data = 0; typename Properties::unsigned_t val; typename Properties::sample_t previous = 0; for(uint8 chn = 0; chn < numChannels; chn++) { typename Properties::sample_t *p = dst + chn; const typename Properties::sample_t *const pEnd = p + length * numChannels; while(p < pEnd) { val = 0; Properties::Decode(file, carry, data, dh, val); cl = dh; while(cl > 0) { READ_CTRL_BIT; val = (val << 1) + carry; cl--; } cl = 1; if(val >= 4) { cl = Properties::shift; while(((1 << cl) & val) == 0 && cl > 1) cl--; } dh = dh + cl; dh >>= 1; // next length in bits of encoded delta second part carry = val & 1; // sign of delta 1=+, 0=not val >>= 1; if(carry == 0) val = ~val; // negative delta val += previous; // previous value + delta *p = val; p += numChannels; previous = val; } } } template static void UnpackMO3DeltaPredictionSample(FileReader &file, typename Properties::sample_t *dst, uint32 length, uint8 numChannels) { uint8 dh = Properties::dhInit, cl = 0; int8 carry; uint16 data = 0; int32 next = 0; typename Properties::unsigned_t val = 0; typename Properties::sample_t sval = 0, delta = 0, previous = 0; for(uint8 chn = 0; chn < numChannels; chn++) { typename Properties::sample_t *p = dst + chn; const typename Properties::sample_t *const pEnd = p + length * numChannels; while(p < pEnd) { val = 0; Properties::Decode(file, carry, data, dh, val); cl = dh; // length in bits of: delta second part (right most bits of delta) and sign bit while(cl > 0) { READ_CTRL_BIT; val = (val << 1) + carry; cl--; } cl = 1; if(val >= 4) { cl = Properties::shift; while(((1 << cl) & val) == 0 && cl > 1) cl--; } dh = dh + cl; dh >>= 1; // next length in bits of encoded delta second part carry = val & 1; // sign of delta 1=+, 0=not val >>= 1; if(carry == 0) val = ~val; // negative delta delta = static_cast(val); val = val + static_cast(next); // predicted value + delta *p = val; p += numChannels; sval = static_cast(val); next = (sval * (1 << 1)) + (delta >> 1) - previous; // corrected next value Limit(next, std::numeric_limits::min(), std::numeric_limits::max()); previous = sval; } } } #undef READ_CTRL_BIT #undef DECODE_CTRL_BITS #if defined(MPT_WITH_VORBIS) && defined(MPT_WITH_VORBISFILE) static size_t VorbisfileFilereaderRead(void *ptr, size_t size, size_t nmemb, void *datasource) { FileReader &file = *reinterpret_cast(datasource); return file.ReadRaw(mpt::span(mpt::void_cast(ptr), size * nmemb)).size() / size; } static int VorbisfileFilereaderSeek(void *datasource, ogg_int64_t offset, int whence) { FileReader &file = *reinterpret_cast(datasource); switch(whence) { case SEEK_SET: if(!mpt::in_range(offset)) { return -1; } return file.Seek(mpt::saturate_cast(offset)) ? 0 : -1; case SEEK_CUR: if(offset < 0) { if(offset == std::numeric_limits::min()) { return -1; } if(!mpt::in_range(0 - offset)) { return -1; } return file.SkipBack(mpt::saturate_cast(0 - offset)) ? 0 : -1; } else { if(!mpt::in_range(offset)) { return -1; } return file.Skip(mpt::saturate_cast(offset)) ? 0 : -1; } break; case SEEK_END: if(!mpt::in_range(offset)) { return -1; } if(!mpt::in_range(file.GetLength() + offset)) { return -1; } return file.Seek(mpt::saturate_cast(file.GetLength() + offset)) ? 0 : -1; default: return -1; } } static long VorbisfileFilereaderTell(void *datasource) { FileReader &file = *reinterpret_cast(datasource); FileReader::off_t result = file.GetPosition(); if(!mpt::in_range(result)) { return -1; } return static_cast(result); } #endif // MPT_WITH_VORBIS && MPT_WITH_VORBISFILE struct MO3ContainerHeader { char magic[3]; // MO3 uint8le version; uint32le musicSize; }; MPT_BINARY_STRUCT(MO3ContainerHeader, 8) static bool ValidateHeader(const MO3ContainerHeader &containerHeader) { if(std::memcmp(containerHeader.magic, "MO3", 3)) { return false; } if(containerHeader.musicSize <= sizeof(MO3FileHeader) || containerHeader.musicSize >= uint32_max / 2u) { return false; } if(containerHeader.version > 5) { return false; } return true; } CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderMO3(MemoryFileReader file, const uint64 *pfilesize) { MO3ContainerHeader containerHeader; if(!file.ReadStruct(containerHeader)) { return ProbeWantMoreData; } if(!ValidateHeader(containerHeader)) { return ProbeFailure; } MPT_UNREFERENCED_PARAMETER(pfilesize); return ProbeSuccess; } bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) { file.Rewind(); MO3ContainerHeader containerHeader; if(!file.ReadStruct(containerHeader)) { return false; } if(!ValidateHeader(containerHeader)) { return false; } if(loadFlags == onlyVerifyHeader) { return true; } const uint8 version = containerHeader.version; uint32 compressedSize = uint32_max, reserveSize = 1024 * 1024; // Generous estimate based on biggest pre-v5 MO3s found in the wild (~350K music data) if(version >= 5) { // Size of compressed music chunk compressedSize = file.ReadUint32LE(); if(!file.CanRead(compressedSize)) return false; // Generous estimate based on highest real-world compression ratio I found in a module (~20:1) reserveSize = std::min(Util::MaxValueOfType(reserveSize) / 32u, compressedSize) * 32u; } std::vector musicData; // We don't always reserve the whole uncompressed size as claimed by the module to guard against broken files // that e.g. claim that the uncompressed size is 1GB while the MO3 file itself is only 100 bytes. // As the LZ compression used in MO3 doesn't allow for establishing a clear upper bound for the maximum size, // this is probably the only sensible way we can prevent DoS due to huge allocations. musicData.reserve(std::min(reserveSize, containerHeader.musicSize.get())); if(!UnpackMO3Data(file, musicData, containerHeader.musicSize)) { return false; } if(version >= 5) { file.Seek(12 + compressedSize); } InitializeGlobals(); InitializeChannels(); FileReader musicChunk(mpt::as_span(musicData)); musicChunk.ReadNullString(m_songName); musicChunk.ReadNullString(m_songMessage); MO3FileHeader fileHeader; if(!musicChunk.ReadStruct(fileHeader) || fileHeader.numChannels == 0 || fileHeader.numChannels > MAX_BASECHANNELS || fileHeader.numInstruments >= MAX_INSTRUMENTS || fileHeader.numSamples >= MAX_SAMPLES) { return false; } m_nChannels = fileHeader.numChannels; Order().SetRestartPos(fileHeader.restartPos); m_nInstruments = fileHeader.numInstruments; m_nSamples = fileHeader.numSamples; m_nDefaultSpeed = fileHeader.defaultSpeed ? fileHeader.defaultSpeed : 6; m_nDefaultTempo.Set(fileHeader.defaultTempo ? fileHeader.defaultTempo : 125, 0); if(fileHeader.flags & MO3FileHeader::isIT) SetType(MOD_TYPE_IT); else if(fileHeader.flags & MO3FileHeader::isS3M) SetType(MOD_TYPE_S3M); else if(fileHeader.flags & MO3FileHeader::isMOD) SetType(MOD_TYPE_MOD); else if(fileHeader.flags & MO3FileHeader::isMTM) SetType(MOD_TYPE_MTM); else SetType(MOD_TYPE_XM); m_SongFlags.set(SONG_IMPORTED); if(fileHeader.flags & MO3FileHeader::linearSlides) m_SongFlags.set(SONG_LINEARSLIDES); if((fileHeader.flags & MO3FileHeader::s3mAmigaLimits) && m_nType == MOD_TYPE_S3M) m_SongFlags.set(SONG_AMIGALIMITS); if((fileHeader.flags & MO3FileHeader::s3mFastSlides) && m_nType == MOD_TYPE_S3M) m_SongFlags.set(SONG_FASTVOLSLIDES); if(!(fileHeader.flags & MO3FileHeader::itOldFX) && m_nType == MOD_TYPE_IT) m_SongFlags.set(SONG_ITOLDEFFECTS); if(!(fileHeader.flags & MO3FileHeader::itCompatGxx) && m_nType == MOD_TYPE_IT) m_SongFlags.set(SONG_ITCOMPATGXX); if(fileHeader.flags & MO3FileHeader::extFilterRange) m_SongFlags.set(SONG_EXFILTERRANGE); if(fileHeader.flags & MO3FileHeader::modVBlank) m_playBehaviour.set(kMODVBlankTiming); if(m_nType == MOD_TYPE_IT) m_nDefaultGlobalVolume = std::min(fileHeader.globalVol.get(), uint8(128)) * 2; else if(m_nType == MOD_TYPE_S3M) m_nDefaultGlobalVolume = std::min(fileHeader.globalVol.get(), uint8(64)) * 4; if(fileHeader.sampleVolume < 0) m_nSamplePreAmp = fileHeader.sampleVolume + 52; else m_nSamplePreAmp = static_cast(std::exp(fileHeader.sampleVolume * 3.1 / 20.0)) + 51; // Header only has room for 64 channels, like in IT const CHANNELINDEX headerChannels = std::min(m_nChannels, CHANNELINDEX(64)); for(CHANNELINDEX i = 0; i < headerChannels; i++) { if(m_nType == MOD_TYPE_IT) ChnSettings[i].nVolume = std::min(fileHeader.chnVolume[i].get(), uint8(64)); if(m_nType != MOD_TYPE_XM) { if(fileHeader.chnPan[i] == 127) ChnSettings[i].dwFlags = CHN_SURROUND; else if(fileHeader.chnPan[i] == 255) ChnSettings[i].nPan = 256; else ChnSettings[i].nPan = fileHeader.chnPan[i]; } } bool anyMacros = false; for(uint32 i = 0; i < 16; i++) { if(fileHeader.sfxMacros[i]) anyMacros = true; } for(uint32 i = 0; i < 128; i++) { if(fileHeader.fixedMacros[i][1]) anyMacros = true; } if(anyMacros) { for(uint32 i = 0; i < 16; i++) { if(fileHeader.sfxMacros[i]) m_MidiCfg.SFx[i] = MPT_AFORMAT("F0F0{}z")(mpt::afmt::HEX0<2>(fileHeader.sfxMacros[i] - 1)); else m_MidiCfg.SFx[i] = ""; } for(uint32 i = 0; i < 128; i++) { if(fileHeader.fixedMacros[i][1]) m_MidiCfg.Zxx[i] = MPT_AFORMAT("F0F0{}{}")(mpt::afmt::HEX0<2>(fileHeader.fixedMacros[i][1] - 1), mpt::afmt::HEX0<2>(fileHeader.fixedMacros[i][0].get())); else m_MidiCfg.Zxx[i] = ""; } } ReadOrderFromFile(Order(), musicChunk, fileHeader.numOrders, 0xFF, 0xFE); // Track assignments for all patterns FileReader trackChunk = musicChunk.ReadChunk(fileHeader.numPatterns * fileHeader.numChannels * sizeof(uint16)); FileReader patLengthChunk = musicChunk.ReadChunk(fileHeader.numPatterns * sizeof(uint16)); std::vector tracks(fileHeader.numTracks); for(auto &track : tracks) { uint32 len = musicChunk.ReadUint32LE(); track = musicChunk.ReadChunk(len); } /* MO3 pattern commands: 01 = Note 02 = Instrument 03 = CMD_ARPEGGIO (IT, XM, S3M, MOD, MTM) 04 = CMD_PORTAMENTOUP (XM, MOD, MTM) [for formats with separate fine slides] 05 = CMD_PORTAMENTODOWN (XM, MOD, MTM) [for formats with separate fine slides] 06 = CMD_TONEPORTAMENTO (IT, XM, S3M, MOD, MTM) / VOLCMD_TONEPORTA (IT, XM) 07 = CMD_VIBRATO (IT, XM, S3M, MOD, MTM) / VOLCMD_VIBRATODEPTH (IT) 08 = CMD_TONEPORTAVOL (XM, MOD, MTM) 09 = CMD_VIBRATOVOL (XM, MOD, MTM) 0A = CMD_TREMOLO (IT, XM, S3M, MOD, MTM) 0B = CMD_PANNING8 (IT, XM, S3M, MOD, MTM) / VOLCMD_PANNING (IT, XM) 0C = CMD_OFFSET (IT, XM, S3M, MOD, MTM) 0D = CMD_VOLUMESLIDE (XM, MOD, MTM) 0E = CMD_POSITIONJUMP (IT, XM, S3M, MOD, MTM) 0F = CMD_VOLUME (XM, MOD, MTM) / VOLCMD_VOLUME (IT, XM, S3M) 10 = CMD_PATTERNBREAK (IT, XM, MOD, MTM) - BCD-encoded in MOD/XM/S3M/MTM! 11 = CMD_MODCMDEX (XM, MOD, MTM) 12 = CMD_TEMPO (XM, MOD, MTM) / CMD_SPEED (XM, MOD, MTM) 13 = CMD_TREMOR (XM) 14 = VOLCMD_VOLSLIDEUP x=X0 (XM) / VOLCMD_VOLSLIDEDOWN x=0X (XM) 15 = VOLCMD_FINEVOLUP x=X0 (XM) / VOLCMD_FINEVOLDOWN x=0X (XM) 16 = CMD_GLOBALVOLUME (IT, XM, S3M) 17 = CMD_GLOBALVOLSLIDE (XM) 18 = CMD_KEYOFF (XM) 19 = CMD_SETENVPOSITION (XM) 1A = CMD_PANNINGSLIDE (XM) 1B = VOLCMD_PANSLIDELEFT x=0X (XM) / VOLCMD_PANSLIDERIGHT x=X0 (XM) 1C = CMD_RETRIG (XM) 1D = CMD_XFINEPORTAUPDOWN X1x (XM) 1E = CMD_XFINEPORTAUPDOWN X2x (XM) 1F = VOLCMD_VIBRATOSPEED (XM) 20 = VOLCMD_VIBRATODEPTH (XM) 21 = CMD_SPEED (IT, S3M) 22 = CMD_VOLUMESLIDE (IT, S3M) 23 = CMD_PORTAMENTODOWN (IT, S3M) [for formats without separate fine slides] 24 = CMD_PORTAMENTOUP (IT, S3M) [for formats without separate fine slides] 25 = CMD_TREMOR (IT, S3M) 26 = CMD_RETRIG (IT, S3M) 27 = CMD_FINEVIBRATO (IT, S3M) 28 = CMD_CHANNELVOLUME (IT, S3M) 29 = CMD_CHANNELVOLSLIDE (IT, S3M) 2A = CMD_PANNINGSLIDE (IT, S3M) 2B = CMD_S3MCMDEX (IT, S3M) 2C = CMD_TEMPO (IT, S3M) 2D = CMD_GLOBALVOLSLIDE (IT, S3M) 2E = CMD_PANBRELLO (IT, XM, S3M) 2F = CMD_MIDI (IT, XM, S3M) 30 = VOLCMD_FINEVOLUP x=0...9 (IT) / VOLCMD_FINEVOLDOWN x=10...19 (IT) / VOLCMD_VOLSLIDEUP x=20...29 (IT) / VOLCMD_VOLSLIDEDOWN x=30...39 (IT) 31 = VOLCMD_PORTADOWN (IT) 32 = VOLCMD_PORTAUP (IT) 33 = Unused XM command "W" (XM) 34 = Any other IT volume column command to support OpenMPT extensions (IT) 35 = CMD_XPARAM (IT) 36 = CMD_SMOOTHMIDI (IT) 37 = CMD_DELAYCUT (IT) 38 = CMD_FINETUNE (MPTM) 39 = CMD_FINETUNE_SMOOTH (MPTM) Note: S3M/IT CMD_TONEPORTAVOL / CMD_VIBRATOVOL are encoded as two commands: K= 07 00 22 x L= 06 00 22 x */ static constexpr ModCommand::COMMAND effTrans[] = { CMD_NONE, CMD_NONE, CMD_NONE, CMD_ARPEGGIO, CMD_PORTAMENTOUP, CMD_PORTAMENTODOWN, CMD_TONEPORTAMENTO, CMD_VIBRATO, CMD_TONEPORTAVOL, CMD_VIBRATOVOL, CMD_TREMOLO, CMD_PANNING8, CMD_OFFSET, CMD_VOLUMESLIDE, CMD_POSITIONJUMP, CMD_VOLUME, CMD_PATTERNBREAK, CMD_MODCMDEX, CMD_TEMPO, CMD_TREMOR, VOLCMD_VOLSLIDEUP, VOLCMD_FINEVOLUP, CMD_GLOBALVOLUME, CMD_GLOBALVOLSLIDE, CMD_KEYOFF, CMD_SETENVPOSITION, CMD_PANNINGSLIDE, VOLCMD_PANSLIDELEFT, CMD_RETRIG, CMD_XFINEPORTAUPDOWN, CMD_XFINEPORTAUPDOWN, VOLCMD_VIBRATOSPEED, VOLCMD_VIBRATODEPTH, CMD_SPEED, CMD_VOLUMESLIDE, CMD_PORTAMENTODOWN, CMD_PORTAMENTOUP, CMD_TREMOR, CMD_RETRIG, CMD_FINEVIBRATO, CMD_CHANNELVOLUME, CMD_CHANNELVOLSLIDE, CMD_PANNINGSLIDE, CMD_S3MCMDEX, CMD_TEMPO, CMD_GLOBALVOLSLIDE, CMD_PANBRELLO, CMD_MIDI, VOLCMD_FINEVOLUP, VOLCMD_PORTADOWN, VOLCMD_PORTAUP, CMD_NONE, VOLCMD_OFFSET, CMD_XPARAM, CMD_SMOOTHMIDI, CMD_DELAYCUT, CMD_FINETUNE, CMD_FINETUNE_SMOOTH, }; uint8 noteOffset = NOTE_MIN; if(m_nType == MOD_TYPE_MTM) noteOffset = 13 + NOTE_MIN; else if(m_nType != MOD_TYPE_IT) noteOffset = 12 + NOTE_MIN; bool onlyAmigaNotes = true; if(loadFlags & loadPatternData) Patterns.ResizeArray(fileHeader.numPatterns); for(PATTERNINDEX pat = 0; pat < fileHeader.numPatterns; pat++) { const ROWINDEX numRows = patLengthChunk.ReadUint16LE(); if(!(loadFlags & loadPatternData) || !Patterns.Insert(pat, numRows)) continue; for(CHANNELINDEX chn = 0; chn < fileHeader.numChannels; chn++) { uint16 trackIndex = trackChunk.ReadUint16LE(); if(trackIndex >= tracks.size()) continue; FileReader &track = tracks[trackIndex]; track.Rewind(); ROWINDEX row = 0; ModCommand *patData = Patterns[pat].GetpModCommand(0, chn); while(row < numRows) { const uint8 b = track.ReadUint8(); if(!b) break; const uint8 numCommands = (b & 0x0F), rep = (b >> 4); ModCommand m = ModCommand::Empty(); for(uint8 c = 0; c < numCommands; c++) { uint8 cmd[2]; track.ReadArray(cmd); // Import pattern commands switch(cmd[0]) { case 0x01: // Note m.note = cmd[1]; if(m.note < 120) m.note += noteOffset; else if(m.note == 0xFF) m.note = NOTE_KEYOFF; else if(m.note == 0xFE) m.note = NOTE_NOTECUT; else m.note = NOTE_FADE; if(!m.IsAmigaNote()) onlyAmigaNotes = false; break; case 0x02: // Instrument m.instr = cmd[1] + 1; break; case 0x06: // Tone portamento if(m.volcmd == VOLCMD_NONE && m_nType == MOD_TYPE_XM && !(cmd[1] & 0x0F)) { m.volcmd = VOLCMD_TONEPORTAMENTO; m.vol = cmd[1] >> 4; break; } else if(m.volcmd == VOLCMD_NONE && m_nType == MOD_TYPE_IT) { for(uint8 i = 0; i < 10; i++) { if(ImpulseTrackerPortaVolCmd[i] == cmd[1]) { m.volcmd = VOLCMD_TONEPORTAMENTO; m.vol = i; break; } } if(m.volcmd != VOLCMD_NONE) break; } m.command = CMD_TONEPORTAMENTO; m.param = cmd[1]; break; case 0x07: // Vibrato if(m.volcmd == VOLCMD_NONE && cmd[1] < 10 && m_nType == MOD_TYPE_IT) { m.volcmd = VOLCMD_VIBRATODEPTH; m.vol = cmd[1]; } else { m.command = CMD_VIBRATO; m.param = cmd[1]; } break; case 0x0B: // Panning if(m.volcmd == VOLCMD_NONE) { if(m_nType == MOD_TYPE_IT && cmd[1] == 0xFF) { m.volcmd = VOLCMD_PANNING; m.vol = 64; break; } if((m_nType == MOD_TYPE_IT && !(cmd[1] & 0x03)) || (m_nType == MOD_TYPE_XM && !(cmd[1] & 0x0F))) { m.volcmd = VOLCMD_PANNING; m.vol = cmd[1] / 4; break; } } m.command = CMD_PANNING8; m.param = cmd[1]; break; case 0x0F: // Volume if(m_nType != MOD_TYPE_MOD && m.volcmd == VOLCMD_NONE && cmd[1] <= 64) { m.volcmd = VOLCMD_VOLUME; m.vol = cmd[1]; } else { m.command = CMD_VOLUME; m.param = cmd[1]; } break; case 0x10: // Pattern break m.command = CMD_PATTERNBREAK; m.param = cmd[1]; if(m_nType != MOD_TYPE_IT) m.param = ((m.param >> 4) * 10) + (m.param & 0x0F); break; case 0x12: // Combined Tempo / Speed command m.param = cmd[1]; if(m.param < 0x20) m.command = CMD_SPEED; else m.command = CMD_TEMPO; break; case 0x14: case 0x15: // XM volume column volume slides if(cmd[1] & 0xF0) { m.volcmd = static_cast((cmd[0] == 0x14) ? VOLCMD_VOLSLIDEUP : VOLCMD_FINEVOLUP); m.vol = cmd[1] >> 4; } else { m.volcmd = static_cast((cmd[0] == 0x14) ? VOLCMD_VOLSLIDEDOWN : VOLCMD_FINEVOLDOWN); m.vol = cmd[1] & 0x0F; } break; case 0x1B: // XM volume column panning slides if(cmd[1] & 0xF0) { m.volcmd = VOLCMD_PANSLIDERIGHT; m.vol = cmd[1] >> 4; } else { m.volcmd = VOLCMD_PANSLIDELEFT; m.vol = cmd[1] & 0x0F; } break; case 0x1D: // XM extra fine porta up m.command = CMD_XFINEPORTAUPDOWN; m.param = 0x10 | cmd[1]; break; case 0x1E: // XM extra fine porta down m.command = CMD_XFINEPORTAUPDOWN; m.param = 0x20 | cmd[1]; break; case 0x1F: case 0x20: // XM volume column vibrato m.volcmd = effTrans[cmd[0]]; m.vol = cmd[1]; break; case 0x22: // IT / S3M volume slide if(m.command == CMD_TONEPORTAMENTO) m.command = CMD_TONEPORTAVOL; else if(m.command == CMD_VIBRATO) m.command = CMD_VIBRATOVOL; else m.command = CMD_VOLUMESLIDE; m.param = cmd[1]; break; case 0x30: // IT volume column volume slides m.vol = cmd[1] % 10; if(cmd[1] < 10) m.volcmd = VOLCMD_FINEVOLUP; else if(cmd[1] < 20) m.volcmd = VOLCMD_FINEVOLDOWN; else if(cmd[1] < 30) m.volcmd = VOLCMD_VOLSLIDEUP; else if(cmd[1] < 40) m.volcmd = VOLCMD_VOLSLIDEDOWN; break; case 0x31: case 0x32: // IT volume column portamento m.volcmd = effTrans[cmd[0]]; m.vol = cmd[1]; break; case 0x34: // Any unrecognized IT volume command if(cmd[1] >= 223 && cmd[1] <= 232) { m.volcmd = VOLCMD_OFFSET; m.vol = cmd[1] - 223; } break; default: if(cmd[0] < std::size(effTrans)) { m.command = effTrans[cmd[0]]; m.param = cmd[1]; } break; } } #ifdef MODPLUG_TRACKER if(m_nType == MOD_TYPE_MTM) m.Convert(MOD_TYPE_MTM, MOD_TYPE_S3M, *this); #endif ROWINDEX targetRow = std::min(row + rep, numRows); while(row < targetRow) { *patData = m; patData += fileHeader.numChannels; row++; } } } } if(GetType() == MOD_TYPE_MOD && GetNumChannels() == 4 && onlyAmigaNotes) { m_SongFlags.set(SONG_AMIGALIMITS | SONG_ISAMIGA); } const bool isSampleMode = (m_nType != MOD_TYPE_XM && !(fileHeader.flags & MO3FileHeader::instrumentMode)); std::vector instrVibrato(m_nType == MOD_TYPE_XM ? m_nInstruments : 0); for(INSTRUMENTINDEX ins = 1; ins <= m_nInstruments; ins++) { ModInstrument *pIns = nullptr; if(isSampleMode || (pIns = AllocateInstrument(ins)) == nullptr) { // Even in IT sample mode, instrument headers are still stored.... while(musicChunk.ReadUint8() != 0) ; if(version >= 5) { while(musicChunk.ReadUint8() != 0) ; } musicChunk.Skip(sizeof(MO3Instrument)); continue; } std::string name; musicChunk.ReadNullString(name); pIns->name = name; if(version >= 5) { musicChunk.ReadNullString(name); pIns->filename = name; } MO3Instrument insHeader; if(!musicChunk.ReadStruct(insHeader)) break; insHeader.ConvertToMPT(*pIns, m_nType); if(m_nType == MOD_TYPE_XM) instrVibrato[ins - 1] = insHeader.vibrato; } if(isSampleMode) m_nInstruments = 0; std::vector sampleChunks(m_nSamples); const bool frequencyIsHertz = (version >= 5 || !(fileHeader.flags & MO3FileHeader::linearSlides)); bool unsupportedSamples = false; for(SAMPLEINDEX smp = 1; smp <= m_nSamples; smp++) { ModSample &sample = Samples[smp]; std::string name; musicChunk.ReadNullString(name); m_szNames[smp] = name; if(version >= 5) { musicChunk.ReadNullString(name); sample.filename = name; } MO3Sample smpHeader; if(!musicChunk.ReadStruct(smpHeader)) break; smpHeader.ConvertToMPT(sample, m_nType, frequencyIsHertz); int16 sharedOggHeader = 0; if(version >= 5 && (smpHeader.flags & MO3Sample::smpCompressionMask) == MO3Sample::smpSharedOgg) { sharedOggHeader = musicChunk.ReadInt16LE(); } if(!(loadFlags & loadSampleData)) continue; const uint32 compression = (smpHeader.flags & MO3Sample::smpCompressionMask); if(!compression && smpHeader.compressedSize == 0) { // Uncompressed sample SampleIO( (smpHeader.flags & MO3Sample::smp16Bit) ? SampleIO::_16bit : SampleIO::_8bit, (smpHeader.flags & MO3Sample::smpStereo) ? SampleIO::stereoSplit : SampleIO::mono, SampleIO::littleEndian, SampleIO::signedPCM) .ReadSample(Samples[smp], file); } else if(smpHeader.compressedSize < 0 && (smp + smpHeader.compressedSize) > 0) { // Duplicate sample sample.CopyWaveform(Samples[smp + smpHeader.compressedSize]); } else if(smpHeader.compressedSize > 0) { if(smpHeader.flags & MO3Sample::smp16Bit) sample.uFlags.set(CHN_16BIT); if(smpHeader.flags & MO3Sample::smpStereo) sample.uFlags.set(CHN_STEREO); FileReader sampleData = file.ReadChunk(smpHeader.compressedSize); const uint8 numChannels = sample.GetNumChannels(); if(compression == MO3Sample::smpDeltaCompression || compression == MO3Sample::smpDeltaPrediction) { // In the best case, MO3 compression represents each sample point as two bits. // As a result, if we have a file length of n, we know that the sample can be at most n*4 sample points long. auto maxLength = sampleData.GetLength(); uint8 maxSamplesPerByte = 4 / numChannels; if(Util::MaxValueOfType(maxLength) / maxSamplesPerByte >= maxLength) maxLength *= maxSamplesPerByte; else maxLength = Util::MaxValueOfType(maxLength); LimitMax(sample.nLength, mpt::saturate_cast(maxLength)); } if(compression == MO3Sample::smpDeltaCompression) { if(sample.AllocateSample()) { if(smpHeader.flags & MO3Sample::smp16Bit) UnpackMO3DeltaSample(sampleData, sample.sample16(), sample.nLength, numChannels); else UnpackMO3DeltaSample(sampleData, sample.sample8(), sample.nLength, numChannels); } } else if(compression == MO3Sample::smpDeltaPrediction) { if(sample.AllocateSample()) { if(smpHeader.flags & MO3Sample::smp16Bit) UnpackMO3DeltaPredictionSample(sampleData, sample.sample16(), sample.nLength, numChannels); else UnpackMO3DeltaPredictionSample(sampleData, sample.sample8(), sample.nLength, numChannels); } } else if(compression == MO3Sample::smpCompressionOgg || compression == MO3Sample::smpSharedOgg) { // Since shared Ogg headers can stem from a sample that has not been read yet, postpone Ogg import. sampleChunks[smp - 1] = MO3SampleChunk(sampleData, smpHeader.encoderDelay, sharedOggHeader); } else if(compression == MO3Sample::smpCompressionMPEG) { // Old MO3 encoders didn't remove LAME info frames. This is unfortunate since the encoder delay // specified in the sample header does not take the gapless information from the LAME info frame // into account. We should not depend on the MP3 decoder's capabilities to read or ignore such frames: // - libmpg123 has MPG123_IGNORE_INFOFRAME but that requires API version 31 (mpg123 v1.14) or higher // - Media Foundation does (currently) not read LAME gapless information at all // So we just play safe and remove such frames. FileReader mpegData(sampleData); MPEGFrame frame(sampleData); uint16 frameDelay = frame.numSamples * 2; if(frame.isLAME && smpHeader.encoderDelay >= frameDelay) { // The info frame does not produce any output, but still counts towards the encoder delay. smpHeader.encoderDelay -= frameDelay; sampleData.Seek(frame.frameSize); mpegData = sampleData.ReadChunk(sampleData.BytesLeft()); } if(ReadMP3Sample(smp, mpegData, true, true) || ReadMediaFoundationSample(smp, mpegData, true)) { if(smpHeader.encoderDelay > 0 && smpHeader.encoderDelay < sample.GetSampleSizeInBytes()) { SmpLength delay = smpHeader.encoderDelay / sample.GetBytesPerSample(); memmove(sample.sampleb(), sample.sampleb() + smpHeader.encoderDelay, sample.GetSampleSizeInBytes() - smpHeader.encoderDelay); sample.nLength -= delay; } LimitMax(sample.nLength, smpHeader.length); } else { unsupportedSamples = true; } } else if(compression == MO3Sample::smpOPLInstrument) { OPLPatch patch; if(sampleData.ReadArray(patch)) { sample.SetAdlib(true, patch); } } else { unsupportedSamples = true; } } } // Now we can load Ogg samples with shared headers. if(loadFlags & loadSampleData) { for(SAMPLEINDEX smp = 1; smp <= m_nSamples; smp++) { MO3SampleChunk &sampleChunk = sampleChunks[smp - 1]; // Is this an Ogg sample? if(!sampleChunk.chunk.IsValid()) continue; SAMPLEINDEX sharedOggHeader = smp + sampleChunk.sharedHeader; // Which chunk are we going to read the header from? // Note: Every Ogg stream has a unique serial number. // stb_vorbis (currently) ignores this serial number so we can just stitch // together our sample without adjusting the shared header's serial number. const bool sharedHeader = sharedOggHeader != smp && sharedOggHeader > 0 && sharedOggHeader <= m_nSamples && sampleChunk.headerSize > 0; #if defined(MPT_WITH_VORBIS) && defined(MPT_WITH_VORBISFILE) std::vector mergedData; if(sharedHeader) { // Prepend the shared header to the actual sample data and adjust bitstream serial numbers. // We do not handle multiple muxed logical streams as they do not exist in practice in mo3. // We assume sequence numbers are consecutive at the end of the headers. // Corrupted pages get dropped as required by Ogg spec. We cannot do any further sane parsing on them anyway. // We do not match up multiple muxed stream properly as this would need parsing of actual packet data to determine or guess the codec. // Ogg Vorbis files may contain at least an additional Ogg Skeleton stream. It is not clear whether these actually exist in MO3. // We do not validate packet structure or logical bitstream structure (i.e. sequence numbers and granule positions). // TODO: At least handle Skeleton streams here, as they violate our stream ordering assumptions here. #if 0 // This block may still turn out to be useful as it does a more thourough validation of the stream than the optimized version below. // We copy the whole data into a single consecutive buffer in order to keep things simple when interfacing libvorbisfile. // We could in theory only adjust the header and pass 2 chunks to libvorbisfile. // Another option would be to demux both chunks on our own (or using libogg) and pass the raw packet data to libvorbis directly. std::ostringstream mergedStream(std::ios::binary); mergedStream.imbue(std::locale::classic()); sampleChunks[sharedOggHeader - 1].chunk.Rewind(); FileReader sharedChunk = sampleChunks[sharedOggHeader - 1].chunk.ReadChunk(sampleChunk.headerSize); sharedChunk.Rewind(); std::vector streamSerials; Ogg::PageInfo oggPageInfo; std::vector oggPageData; streamSerials.clear(); while(Ogg::ReadPageAndSkipJunk(sharedChunk, oggPageInfo, oggPageData)) { auto it = std::find(streamSerials.begin(), streamSerials.end(), oggPageInfo.header.bitstream_serial_number); if(it == streamSerials.end()) { streamSerials.push_back(oggPageInfo.header.bitstream_serial_number); it = streamSerials.begin() + (streamSerials.size() - 1); } uint32 newSerial = it - streamSerials.begin() + 1; oggPageInfo.header.bitstream_serial_number = newSerial; Ogg::UpdatePageCRC(oggPageInfo, oggPageData); Ogg::WritePage(mergedStream, oggPageInfo, oggPageData); } streamSerials.clear(); while(Ogg::ReadPageAndSkipJunk(sampleChunk.chunk, oggPageInfo, oggPageData)) { auto it = std::find(streamSerials.begin(), streamSerials.end(), oggPageInfo.header.bitstream_serial_number); if(it == streamSerials.end()) { streamSerials.push_back(oggPageInfo.header.bitstream_serial_number); it = streamSerials.begin() + (streamSerials.size() - 1); } uint32 newSerial = it - streamSerials.begin() + 1; oggPageInfo.header.bitstream_serial_number = newSerial; Ogg::UpdatePageCRC(oggPageInfo, oggPageData); Ogg::WritePage(mergedStream, oggPageInfo, oggPageData); } std::string mergedStreamData = mergedStream.str(); mergedData.insert(mergedData.end(), mergedStreamData.begin(), mergedStreamData.end()); #else // We assume same ordering of streams in both header and data if // multiple streams are present. std::ostringstream mergedStream(std::ios::binary); mergedStream.imbue(std::locale::classic()); sampleChunks[sharedOggHeader - 1].chunk.Rewind(); FileReader sharedChunk = sampleChunks[sharedOggHeader - 1].chunk.ReadChunk(sampleChunk.headerSize); sharedChunk.Rewind(); std::vector dataStreamSerials; std::vector headStreamSerials; Ogg::PageInfo oggPageInfo; std::vector oggPageData; // Gather bitstream serial numbers form sample data chunk dataStreamSerials.clear(); while(Ogg::ReadPageAndSkipJunk(sampleChunk.chunk, oggPageInfo, oggPageData)) { if(!mpt::contains(dataStreamSerials, oggPageInfo.header.bitstream_serial_number)) { dataStreamSerials.push_back(oggPageInfo.header.bitstream_serial_number); } } // Apply the data bitstream serial numbers to the header headStreamSerials.clear(); while(Ogg::ReadPageAndSkipJunk(sharedChunk, oggPageInfo, oggPageData)) { auto it = std::find(headStreamSerials.begin(), headStreamSerials.end(), oggPageInfo.header.bitstream_serial_number); if(it == headStreamSerials.end()) { headStreamSerials.push_back(oggPageInfo.header.bitstream_serial_number); it = headStreamSerials.begin() + (headStreamSerials.size() - 1); } uint32 newSerial = 0; if(dataStreamSerials.size() >= static_cast(it - headStreamSerials.begin())) { // Found corresponding stream in data chunk. newSerial = dataStreamSerials[it - headStreamSerials.begin()]; } else { // No corresponding stream in data chunk. Find a free serialno. std::size_t extraIndex = (it - headStreamSerials.begin()) - dataStreamSerials.size(); for(newSerial = 1; newSerial < 0xffffffffu; ++newSerial) { if(!mpt::contains(dataStreamSerials, newSerial)) { extraIndex -= 1; } if(extraIndex == 0) { break; } } } oggPageInfo.header.bitstream_serial_number = newSerial; Ogg::UpdatePageCRC(oggPageInfo, oggPageData); Ogg::WritePage(mergedStream, oggPageInfo, oggPageData); } if(headStreamSerials.size() > 1) { AddToLog(LogWarning, MPT_UFORMAT("Sample {}: Ogg Vorbis data with shared header and multiple logical bitstreams in header chunk found. This may be handled incorrectly.")(smp)); } else if(dataStreamSerials.size() > 1) { AddToLog(LogWarning, MPT_UFORMAT("Sample {}: Ogg Vorbis sample with shared header and multiple logical bitstreams found. This may be handled incorrectly.")(smp)); } else if((dataStreamSerials.size() == 1) && (headStreamSerials.size() == 1) && (dataStreamSerials[0] != headStreamSerials[0])) { AddToLog(LogInformation, MPT_UFORMAT("Sample {}: Ogg Vorbis data with shared header and different logical bitstream serials found.")(smp)); } std::string mergedStreamData = mergedStream.str(); mergedData.insert(mergedData.end(), mergedStreamData.begin(), mergedStreamData.end()); sampleChunk.chunk.Rewind(); FileReader::PinnedView sampleChunkView = sampleChunk.chunk.GetPinnedView(); mpt::span sampleChunkViewSpan = mpt::byte_cast>(sampleChunkView.span()); mergedData.insert(mergedData.end(), sampleChunkViewSpan.begin(), sampleChunkViewSpan.end()); #endif } FileReader mergedDataChunk(mpt::byte_cast(mpt::as_span(mergedData))); FileReader &sampleData = sharedHeader ? mergedDataChunk : sampleChunk.chunk; FileReader &headerChunk = sampleData; #else // !(MPT_WITH_VORBIS && MPT_WITH_VORBISFILE) FileReader &sampleData = sampleChunk.chunk; FileReader &headerChunk = sharedHeader ? sampleChunks[sharedOggHeader - 1].chunk : sampleData; #if defined(MPT_WITH_STBVORBIS) std::size_t initialRead = sharedHeader ? sampleChunk.headerSize : headerChunk.GetLength(); #endif // MPT_WITH_STBVORBIS #endif // MPT_WITH_VORBIS && MPT_WITH_VORBISFILE headerChunk.Rewind(); if(sharedHeader && !headerChunk.CanRead(sampleChunk.headerSize)) continue; #if defined(MPT_WITH_VORBIS) && defined(MPT_WITH_VORBISFILE) ov_callbacks callbacks = { &VorbisfileFilereaderRead, &VorbisfileFilereaderSeek, nullptr, &VorbisfileFilereaderTell}; OggVorbis_File vf; MemsetZero(vf); if(ov_open_callbacks(&sampleData, &vf, nullptr, 0, callbacks) == 0) { if(ov_streams(&vf) == 1) { // we do not support chained vorbis samples vorbis_info *vi = ov_info(&vf, -1); if(vi && vi->rate > 0 && vi->channels > 0) { ModSample &sample = Samples[smp]; sample.AllocateSample(); SmpLength offset = 0; int channels = vi->channels; int current_section = 0; long decodedSamples = 0; bool eof = false; while(!eof && offset < sample.nLength && sample.HasSampleData()) { float **output = nullptr; long ret = ov_read_float(&vf, &output, 1024, ¤t_section); if(ret == 0) { eof = true; } else if(ret < 0) { // stream error, just try to continue } else { decodedSamples = ret; LimitMax(decodedSamples, mpt::saturate_cast(sample.nLength - offset)); if(decodedSamples > 0 && channels == sample.GetNumChannels()) { if(sample.uFlags[CHN_16BIT]) { CopyAudio(mpt::audio_span_interleaved(sample.sample16() + (offset * sample.GetNumChannels()), sample.GetNumChannels(), decodedSamples), mpt::audio_span_planar(output, channels, decodedSamples)); } else { CopyAudio(mpt::audio_span_interleaved(sample.sample8() + (offset * sample.GetNumChannels()), sample.GetNumChannels(), decodedSamples), mpt::audio_span_planar(output, channels, decodedSamples)); } } offset += decodedSamples; } } } else { unsupportedSamples = true; } } else { AddToLog(LogWarning, MPT_UFORMAT("Sample {}: Unsupported Ogg Vorbis chained stream found.")(smp)); unsupportedSamples = true; } ov_clear(&vf); } else { unsupportedSamples = true; } #elif defined(MPT_WITH_STBVORBIS) // NOTE/TODO: stb_vorbis does not handle inferred negative PCM sample // position at stream start. (See // ). // This means that, for remuxed and re-aligned/cutted (at stream start) // Vorbis files, stb_vorbis will include superfluous samples at the // beginning. MO3 files with this property are yet to be spotted in the // wild, thus, this behaviour is currently not problematic. int consumed = 0, error = 0; stb_vorbis *vorb = nullptr; if(sharedHeader) { FileReader::PinnedView headChunkView = headerChunk.GetPinnedView(initialRead); vorb = stb_vorbis_open_pushdata(mpt::byte_cast(headChunkView.data()), mpt::saturate_cast(headChunkView.size()), &consumed, &error, nullptr); headerChunk.Skip(consumed); } FileReader::PinnedView sampleDataView = sampleData.GetPinnedView(); const std::byte *data = sampleDataView.data(); std::size_t dataLeft = sampleDataView.size(); if(!sharedHeader) { vorb = stb_vorbis_open_pushdata(mpt::byte_cast(data), mpt::saturate_cast(dataLeft), &consumed, &error, nullptr); sampleData.Skip(consumed); data += consumed; dataLeft -= consumed; } if(vorb) { // Header has been read, proceed to reading the sample data ModSample &sample = Samples[smp]; sample.AllocateSample(); SmpLength offset = 0; while((error == VORBIS__no_error || (error == VORBIS_need_more_data && dataLeft > 0)) && offset < sample.nLength && sample.HasSampleData()) { int channels = 0, decodedSamples = 0; float **output; consumed = stb_vorbis_decode_frame_pushdata(vorb, mpt::byte_cast(data), mpt::saturate_cast(dataLeft), &channels, &output, &decodedSamples); sampleData.Skip(consumed); data += consumed; dataLeft -= consumed; LimitMax(decodedSamples, mpt::saturate_cast(sample.nLength - offset)); if(decodedSamples > 0 && channels == sample.GetNumChannels()) { if(sample.uFlags[CHN_16BIT]) { CopyAudio(mpt::audio_span_interleaved(sample.sample16() + (offset * sample.GetNumChannels()), sample.GetNumChannels(), decodedSamples), mpt::audio_span_planar(output, channels, decodedSamples)); } else { CopyAudio(mpt::audio_span_interleaved(sample.sample8() + (offset * sample.GetNumChannels()), sample.GetNumChannels(), decodedSamples), mpt::audio_span_planar(output, channels, decodedSamples)); } } offset += decodedSamples; error = stb_vorbis_get_error(vorb); } stb_vorbis_close(vorb); } else { unsupportedSamples = true; } #else // !VORBIS unsupportedSamples = true; #endif // VORBIS } } if(m_nType == MOD_TYPE_XM) { // Transfer XM instrument vibrato to samples for(INSTRUMENTINDEX ins = 0; ins < m_nInstruments; ins++) { PropagateXMAutoVibrato(ins + 1, static_cast(instrVibrato[ins].type.get()), instrVibrato[ins].sweep, instrVibrato[ins].depth, instrVibrato[ins].rate); } } if((fileHeader.flags & MO3FileHeader::hasPlugins) && musicChunk.CanRead(1)) { // Plugin data uint8 pluginFlags = musicChunk.ReadUint8(); if(pluginFlags & 1) { // Channel plugins for(CHANNELINDEX chn = 0; chn < m_nChannels; chn++) { ChnSettings[chn].nMixPlugin = static_cast(musicChunk.ReadUint32LE()); } } while(musicChunk.CanRead(1)) { PLUGINDEX plug = musicChunk.ReadUint8(); if(!plug) break; FileReader pluginChunk = musicChunk.ReadChunk(musicChunk.ReadUint32LE()); #ifndef NO_PLUGINS if(plug <= MAX_MIXPLUGINS) { ReadMixPluginChunk(pluginChunk, m_MixPlugins[plug - 1]); } #endif // NO_PLUGINS } } mpt::ustring madeWithTracker; uint16 cwtv = 0; uint16 cmwt = 0; while(musicChunk.CanRead(8)) { uint32 id = musicChunk.ReadUint32LE(); uint32 len = musicChunk.ReadUint32LE(); FileReader chunk = musicChunk.ReadChunk(len); switch(id) { case MagicLE("VERS"): // Tracker magic bytes (depending on format) switch(m_nType) { case MOD_TYPE_IT: cwtv = chunk.ReadUint16LE(); cmwt = chunk.ReadUint16LE(); /*switch(cwtv >> 12) { }*/ break; case MOD_TYPE_S3M: cwtv = chunk.ReadUint16LE(); break; case MOD_TYPE_XM: chunk.ReadString(madeWithTracker, mpt::Charset::CP437, std::min(FileReader::off_t(32), chunk.GetLength())); break; case MOD_TYPE_MTM: { uint8 mtmVersion = chunk.ReadUint8(); madeWithTracker = MPT_UFORMAT("MultiTracker {}.{}")(mtmVersion >> 4, mtmVersion & 0x0F); } break; default: break; } break; case MagicLE("PRHI"): m_nDefaultRowsPerBeat = chunk.ReadUint8(); m_nDefaultRowsPerMeasure = chunk.ReadUint8(); break; case MagicLE("MIDI"): // Full MIDI config chunk.ReadStruct(m_MidiCfg); m_MidiCfg.Sanitize(); break; case MagicLE("OMPT"): // Read pattern names: "PNAM" if(chunk.ReadMagic("PNAM")) { FileReader patterns = chunk.ReadChunk(chunk.ReadUint32LE()); const PATTERNINDEX namedPats = std::min(static_cast(patterns.GetLength() / MAX_PATTERNNAME), Patterns.Size()); for(PATTERNINDEX pat = 0; pat < namedPats; pat++) { char patName[MAX_PATTERNNAME]; patterns.ReadString(patName, MAX_PATTERNNAME); Patterns[pat].SetName(patName); } } // Read channel names: "CNAM" if(chunk.ReadMagic("CNAM")) { FileReader channels = chunk.ReadChunk(chunk.ReadUint32LE()); const CHANNELINDEX namedChans = std::min(static_cast(channels.GetLength() / MAX_CHANNELNAME), GetNumChannels()); for(CHANNELINDEX chn = 0; chn < namedChans; chn++) { channels.ReadString(ChnSettings[chn].szName, MAX_CHANNELNAME); } } LoadExtendedInstrumentProperties(chunk); LoadExtendedSongProperties(chunk, true); if(cwtv > 0x0889 && cwtv <= 0x8FF) { m_nType = MOD_TYPE_MPT; LoadMPTMProperties(chunk, cwtv); } if(m_dwLastSavedWithVersion) { madeWithTracker = U_("OpenMPT ") + mpt::ufmt::val(m_dwLastSavedWithVersion); } break; } } if((GetType() == MOD_TYPE_IT && cwtv >= 0x0100 && cwtv < 0x0214) || (GetType() == MOD_TYPE_S3M && cwtv >= 0x3100 && cwtv < 0x3214) || (GetType() == MOD_TYPE_S3M && cwtv >= 0x1300 && cwtv < 0x1320)) { // Ignore MIDI data in files made with IT older than version 2.14 and old ST3 versions. m_MidiCfg.ClearZxxMacros(); } if(fileHeader.flags & MO3FileHeader::modplugMode) { // Apply some old ModPlug (mis-)behaviour if(!m_dwLastSavedWithVersion) { // These fixes are only applied when the OpenMPT version number is not known, as otherwise the song upgrade feature will take care of it. for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++) { if(ModInstrument *ins = Instruments[i]) { // Fix pitch / filter envelope being shortened by one tick (for files before v1.20) ins->GetEnvelope(ENV_PITCH).Convert(MOD_TYPE_XM, GetType()); // Fix excessive pan swing range (for files before v1.26) ins->nPanSwing = (ins->nPanSwing + 3) / 4u; } } } if(m_dwLastSavedWithVersion < MPT_V("1.18.00.00")) { m_playBehaviour.reset(kITOffset); m_playBehaviour.reset(kFT2ST3OffsetOutOfRange); } if(m_dwLastSavedWithVersion < MPT_V("1.23.00.00")) m_playBehaviour.reset(kFT2Periods); if(m_dwLastSavedWithVersion < MPT_V("1.26.00.00")) m_playBehaviour.reset(kITInstrWithNoteOff); } if(madeWithTracker.empty()) madeWithTracker = MPT_UFORMAT("MO3 v{}")(version); else madeWithTracker = MPT_UFORMAT("MO3 v{} ({})")(version, madeWithTracker); m_modFormat.formatName = MPT_UFORMAT("Un4seen MO3 v{}")(version); m_modFormat.type = U_("mo3"); switch(GetType()) { case MOD_TYPE_MTM: m_modFormat.originalType = U_("mtm"); m_modFormat.originalFormatName = U_("MultiTracker"); break; case MOD_TYPE_MOD: m_modFormat.originalType = U_("mod"); m_modFormat.originalFormatName = U_("Generic MOD"); break; case MOD_TYPE_XM: m_modFormat.originalType = U_("xm"); m_modFormat.originalFormatName = U_("FastTracker 2"); break; case MOD_TYPE_S3M: m_modFormat.originalType = U_("s3m"); m_modFormat.originalFormatName = U_("Scream Tracker 3"); break; case MOD_TYPE_IT: m_modFormat.originalType = U_("it"); if(cmwt) m_modFormat.originalFormatName = MPT_UFORMAT("Impulse Tracker {}.{}")(cmwt >> 8, mpt::ufmt::hex0<2>(cmwt & 0xFF)); else m_modFormat.originalFormatName = U_("Impulse Tracker"); break; case MOD_TYPE_MPT: m_modFormat.originalType = U_("mptm"); m_modFormat.originalFormatName = U_("OpenMPT MPTM"); break; default: MPT_ASSERT_NOTREACHED(); } m_modFormat.madeWithTracker = std::move(madeWithTracker); if(m_dwLastSavedWithVersion) m_modFormat.charset = mpt::Charset::Windows1252; else if(GetType() == MOD_TYPE_MOD) m_modFormat.charset = mpt::Charset::ISO8859_1; else m_modFormat.charset = mpt::Charset::CP437; if(unsupportedSamples) { AddToLog(LogWarning, U_("Some compressed samples could not be loaded because they use an unsupported codec.")); } return true; } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Load_mod.cpp0000644000175000017500000022461414173265322020333 00000000000000/* * Load_mod.cpp * ------------ * Purpose: MOD / NST (ProTracker / NoiseTracker), M15 / STK (Ultimate Soundtracker / Soundtracker) and ST26 (SoundTracker 2.6 / Ice Tracker) module loader / saver * Notes : "2000 LOC for processing MOD files?!" you say? Well, this file also contains loaders for some formats that are almost identical to MOD, and extensive * heuristics for more or less broken MOD files and files saved with tons of different trackers, to allow for the most optimal playback. * Authors: Olivier Lapicque * OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Loaders.h" #include "Tables.h" #ifndef MODPLUG_NO_FILESAVE #include "mpt/io/base.hpp" #include "mpt/io/io.hpp" #include "mpt/io/io_stdstream.hpp" #include "../common/mptFileIO.h" #endif #ifdef MPT_EXTERNAL_SAMPLES // For loading external data in Startrekker files #include "../common/mptPathString.h" #endif // MPT_EXTERNAL_SAMPLES OPENMPT_NAMESPACE_BEGIN void CSoundFile::ConvertModCommand(ModCommand &m) { switch(m.command) { case 0x00: if(m.param) m.command = CMD_ARPEGGIO; break; case 0x01: m.command = CMD_PORTAMENTOUP; break; case 0x02: m.command = CMD_PORTAMENTODOWN; break; case 0x03: m.command = CMD_TONEPORTAMENTO; break; case 0x04: m.command = CMD_VIBRATO; break; case 0x05: m.command = CMD_TONEPORTAVOL; break; case 0x06: m.command = CMD_VIBRATOVOL; break; case 0x07: m.command = CMD_TREMOLO; break; case 0x08: m.command = CMD_PANNING8; break; case 0x09: m.command = CMD_OFFSET; break; case 0x0A: m.command = CMD_VOLUMESLIDE; break; case 0x0B: m.command = CMD_POSITIONJUMP; break; case 0x0C: m.command = CMD_VOLUME; break; case 0x0D: m.command = CMD_PATTERNBREAK; m.param = ((m.param >> 4) * 10) + (m.param & 0x0F); break; case 0x0E: m.command = CMD_MODCMDEX; break; case 0x0F: // For a very long time, this code imported 0x20 as CMD_SPEED for MOD files, but this seems to contradict // pretty much the majority of other MOD player out there. // 0x20 is Speed: Impulse Tracker, Scream Tracker, old ModPlug // 0x20 is Tempo: ProTracker, XMPlay, Imago Orpheus, Cubic Player, ChibiTracker, BeRoTracker, DigiTrakker, DigiTrekker, Disorder Tracker 2, DMP, Extreme's Tracker, ... if(m.param < 0x20) m.command = CMD_SPEED; else m.command = CMD_TEMPO; break; // Extension for XM extended effects case 'G' - 55: m.command = CMD_GLOBALVOLUME; break; //16 case 'H' - 55: m.command = CMD_GLOBALVOLSLIDE; break; case 'K' - 55: m.command = CMD_KEYOFF; break; case 'L' - 55: m.command = CMD_SETENVPOSITION; break; case 'P' - 55: m.command = CMD_PANNINGSLIDE; break; case 'R' - 55: m.command = CMD_RETRIG; break; case 'T' - 55: m.command = CMD_TREMOR; break; case 'W' - 55: m.command = CMD_DUMMY; break; case 'X' - 55: m.command = CMD_XFINEPORTAUPDOWN; break; case 'Y' - 55: m.command = CMD_PANBRELLO; break; // 34 case 'Z' - 55: m.command = CMD_MIDI; break; // 35 case '\\' - 56: m.command = CMD_SMOOTHMIDI; break; // 36 - note: this is actually displayed as "-" in FT2, but seems to be doing nothing. case 37: m.command = CMD_SMOOTHMIDI; break; // BeRoTracker uses this for smooth MIDI macros for some reason; in old OpenMPT versions this was reserved for the unimplemented "velocity" command case '#' + 3: m.command = CMD_XPARAM; break; // 38 default: m.command = CMD_NONE; } } #ifndef MODPLUG_NO_FILESAVE void CSoundFile::ModSaveCommand(uint8 &command, uint8 ¶m, bool toXM, bool compatibilityExport) const { switch(command) { case CMD_NONE: command = param = 0; break; case CMD_ARPEGGIO: command = 0; break; case CMD_PORTAMENTOUP: if (GetType() & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_STM|MOD_TYPE_MPT)) { 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(GetType() & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_STM|MOD_TYPE_MPT)) { 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(GetType() & MOD_TYPE_S3M) { if(param <= 0x80) { param = mpt::saturate_cast(param * 2); } else if(param == 0xA4) // surround { if(compatibilityExport || !toXM) { command = param = 0; } else { command = 'X' - 55; param = 91; } } } 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; param = std::min(param, uint8(0x1F)); break; case CMD_TEMPO: command = 0x0F; param = std::max(param, uint8(0x20)); 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_PANNINGSLIDE: command = 'P' - 55; break; case CMD_RETRIG: command = 'R' - 55; break; case CMD_TREMOR: command = 'T' - 55; break; case CMD_DUMMY: command = 'W' - 55; break; case CMD_XFINEPORTAUPDOWN: command = 'X' - 55; if(compatibilityExport && param >= 0x30) // X1x and X2x are legit, everything above are MPT extensions, which don't belong here. param = 0; // Don't set command to 0 to indicate that there *was* some X command here... break; case CMD_PANBRELLO: if(compatibilityExport) command = param = 0; else command = 'Y' - 55; break; case CMD_MIDI: if(compatibilityExport) command = param = 0; else command = 'Z' - 55; break; case CMD_SMOOTHMIDI: //rewbs.smoothVST: 36 if(compatibilityExport) command = param = 0; else command = '\\' - 56; break; case CMD_XPARAM: //rewbs.XMfixes - XParam is 38 if(compatibilityExport) command = param = 0; else command = '#' + 3; 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: if(compatibilityExport) command = param = 0; else 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; } // Don't even think about saving XM effects in MODs... if(command > 0x0F && !toXM) { command = param = 0; } } #endif // MODPLUG_NO_FILESAVE // File Header struct MODFileHeader { uint8be numOrders; uint8be restartPos; // Tempo (early SoundTracker) or restart position (only PC trackers?) uint8be orderList[128]; }; MPT_BINARY_STRUCT(MODFileHeader, 130) // Sample Header struct MODSampleHeader { char name[22]; uint16be length; uint8be finetune; uint8be volume; uint16be loopStart; uint16be loopLength; // Convert an MOD sample header to OpenMPT's internal sample header. void ConvertToMPT(ModSample &mptSmp, bool is4Chn) const { mptSmp.Initialize(MOD_TYPE_MOD); mptSmp.nLength = length * 2; mptSmp.nFineTune = MOD2XMFineTune(finetune & 0x0F); mptSmp.nVolume = 4u * std::min(volume.get(), uint8(64)); SmpLength lStart = loopStart * 2; SmpLength lLength = loopLength * 2; // See if loop start is incorrect as words, but correct as bytes (like in Soundtracker modules) if(lLength > 2 && (lStart + lLength > mptSmp.nLength) && (lStart / 2 + lLength <= mptSmp.nLength)) { lStart /= 2; } if(mptSmp.nLength == 2) { mptSmp.nLength = 0; } if(mptSmp.nLength) { mptSmp.nLoopStart = lStart; mptSmp.nLoopEnd = lStart + lLength; if(mptSmp.nLoopStart >= mptSmp.nLength) { mptSmp.nLoopStart = mptSmp.nLength - 1; } if(mptSmp.nLoopStart > mptSmp.nLoopEnd || mptSmp.nLoopEnd < 4 || mptSmp.nLoopEnd - mptSmp.nLoopStart < 4) { mptSmp.nLoopStart = 0; mptSmp.nLoopEnd = 0; } // Fix for most likely broken sample loops. This fixes super_sufm_-_new_life.mod (M.K.) which has a long sample which is looped from 0 to 4. // This module also has notes outside of the Amiga frequency range, so we cannot say that it should be played using ProTracker one-shot loops. // On the other hand, "Crew Generation" by Necros (6CHN) has a sample with a similar loop, which is supposed to be played. // To be able to correctly play both modules, we will draw a somewhat arbitrary line here and trust the loop points in MODs with more than // 4 channels, even if they are tiny and at the very beginning of the sample. if(mptSmp.nLoopEnd <= 8 && mptSmp.nLoopStart == 0 && mptSmp.nLength > mptSmp.nLoopEnd && is4Chn) { mptSmp.nLoopEnd = 0; } if(mptSmp.nLoopEnd > mptSmp.nLoopStart) { mptSmp.uFlags.set(CHN_LOOP); } } } // Convert OpenMPT's internal sample header to a MOD sample header. SmpLength ConvertToMOD(const ModSample &mptSmp) { SmpLength writeLength = mptSmp.HasSampleData() ? mptSmp.nLength : 0; // If the sample size is odd, we have to add a padding byte, as all sample sizes in MODs are even. if((writeLength % 2u) != 0) { writeLength++; } LimitMax(writeLength, SmpLength(0x1FFFE)); length = static_cast(writeLength / 2u); if(mptSmp.RelativeTone < 0) { finetune = 0x08; } else if(mptSmp.RelativeTone > 0) { finetune = 0x07; } else { finetune = XM2MODFineTune(mptSmp.nFineTune); } volume = static_cast(mptSmp.nVolume / 4u); loopStart = 0; loopLength = 1; if(mptSmp.uFlags[CHN_LOOP] && (mptSmp.nLoopStart + 2u) < writeLength) { const SmpLength loopEnd = Clamp(mptSmp.nLoopEnd, (mptSmp.nLoopStart & ~1) + 2u, writeLength) & ~1; loopStart = static_cast(mptSmp.nLoopStart / 2u); loopLength = static_cast((loopEnd - (mptSmp.nLoopStart & ~1)) / 2u); } return writeLength; } // Compute a "rating" of this sample header by counting invalid header data to ultimately reject garbage files. uint32 GetInvalidByteScore() const { return ((volume > 64) ? 1 : 0) + ((finetune > 15) ? 1 : 0) + ((loopStart > length * 2) ? 1 : 0); } // Suggested threshold for rejecting invalid files based on cumulated score returned by GetInvalidByteScore static constexpr uint32 INVALID_BYTE_THRESHOLD = 40; // This threshold is used for files where the file magic only gives a // fragile result which alone would lead to too many false positives. // In particular, the files from Inconexia demo by Iguana // (https://www.pouet.net/prod.php?which=830) which have 3 \0 bytes in // the file magic tend to cause misdetection of random files. static constexpr uint32 INVALID_BYTE_FRAGILE_THRESHOLD = 1; // Retrieve the internal sample format flags for this sample. static SampleIO GetSampleFormat() { return SampleIO( SampleIO::_8bit, SampleIO::mono, SampleIO::bigEndian, SampleIO::signedPCM); } }; MPT_BINARY_STRUCT(MODSampleHeader, 30) // Pattern data of a 4-channel MOD file using MODPatternData = std::array, 4>, 64>; // Synthesized StarTrekker instruments struct AMInstrument { char am[2]; // "AM" char zero[4]; uint16be startLevel; // Start level uint16be attack1Level; // Attack 1 level uint16be attack1Speed; // Attack 1 speed uint16be attack2Level; // Attack 2 level uint16be attack2Speed; // Attack 2 speed uint16be sustainLevel; // Sustain level uint16be decaySpeed; // Decay speed uint16be sustainTime; // Sustain time uint16be nt; // ? uint16be releaseSpeed; // Release speed uint16be waveform; // Waveform int16be pitchFall; // Pitch fall uint16be vibAmp; // Vibrato amplitude uint16be vibSpeed; // Vibrato speed uint16be octave; // Base frequency void ConvertToMPT(ModSample &sample, ModInstrument &ins, mpt::fast_prng &rng) const { sample.nLength = waveform == 3 ? 1024 : 32; sample.nLoopStart = 0; sample.nLoopEnd = sample.nLength; sample.uFlags.set(CHN_LOOP); sample.nVolume = 256; // prelude.mod has volume 0 in sample header sample.nVibDepth = mpt::saturate_cast(vibAmp * 2); sample.nVibRate = static_cast(vibSpeed); sample.nVibType = VIB_SINE; sample.RelativeTone = static_cast(-12 * octave); if(sample.AllocateSample()) { int8 *p = sample.sample8(); for(SmpLength i = 0; i < sample.nLength; i++) { switch(waveform) { default: case 0: p[i] = ModSinusTable[i * 2]; break; // Sine case 1: p[i] = static_cast(-128 + i * 8); break; // Saw case 2: p[i] = i < 16 ? -128 : 127; break; // Square case 3: p[i] = mpt::random(rng); break; // Noise } } } InstrumentEnvelope &volEnv = ins.VolEnv; volEnv.dwFlags.set(ENV_ENABLED); volEnv.reserve(6); volEnv.push_back(0, static_cast(startLevel / 4)); const struct { uint16 level, speed; } points[] = {{startLevel, 0}, {attack1Level, attack1Speed}, {attack2Level, attack2Speed}, {sustainLevel, decaySpeed}, {sustainLevel, sustainTime}, {0, releaseSpeed}}; for(uint8 i = 1; i < std::size(points); i++) { int duration = std::min(points[i].speed, uint16(256)); // Sustain time is already in ticks, no need to compute the segment duration. if(i != 4) { if(duration == 0) { volEnv.dwFlags.set(ENV_LOOP); volEnv.nLoopStart = volEnv.nLoopEnd = static_cast(volEnv.size() - 1); break; } // Startrekker increments / decrements the envelope level by the stage speed // until it reaches the next stage level. int a, b; if(points[i].level > points[i - 1].level) { a = points[i].level - points[i - 1].level; b = 256 - points[i - 1].level; } else { a = points[i - 1].level - points[i].level; b = points[i - 1].level; } // Release time is again special. if(i == 5) b = 256; else if(b == 0) b = 1; duration = std::max((256 * a) / (duration * b), 1); } if(duration > 0) { volEnv.push_back(volEnv.back().tick + static_cast(duration), static_cast(points[i].level / 4)); } } if(pitchFall) { InstrumentEnvelope &pitchEnv = ins.PitchEnv; pitchEnv.dwFlags.set(ENV_ENABLED); pitchEnv.reserve(2); pitchEnv.push_back(0, ENVELOPE_MID); // cppcheck false-positive // cppcheck-suppress zerodiv pitchEnv.push_back(static_cast(1024 / abs(pitchFall)), pitchFall > 0 ? ENVELOPE_MIN : ENVELOPE_MAX); } } }; MPT_BINARY_STRUCT(AMInstrument, 36) struct PT36IffChunk { // IFF chunk names enum ChunkIdentifiers { idVERS = MagicBE("VERS"), idINFO = MagicBE("INFO"), idCMNT = MagicBE("CMNT"), idPTDT = MagicBE("PTDT"), }; uint32be signature; // IFF chunk name uint32be chunksize; // chunk size without header }; MPT_BINARY_STRUCT(PT36IffChunk, 8) struct PT36InfoChunk { char name[32]; uint16be numSamples; uint16be numOrders; uint16be numPatterns; uint16be volume; uint16be tempo; uint16be flags; uint16be dateDay; uint16be dateMonth; uint16be dateYear; uint16be dateHour; uint16be dateMinute; uint16be dateSecond; uint16be playtimeHour; uint16be playtimeMinute; uint16be playtimeSecond; uint16be playtimeMsecond; }; MPT_BINARY_STRUCT(PT36InfoChunk, 64) // Check if header magic equals a given string. static bool IsMagic(const char *magic1, const char (&magic2)[5]) { return std::memcmp(magic1, magic2, 4) == 0; } static uint32 ReadSample(FileReader &file, MODSampleHeader &sampleHeader, ModSample &sample, mpt::charbuf &sampleName, bool is4Chn) { file.ReadStruct(sampleHeader); sampleHeader.ConvertToMPT(sample, is4Chn); sampleName = mpt::String::ReadBuf(mpt::String::spacePadded, sampleHeader.name); // Get rid of weird characters in sample names. for(auto &c : sampleName.buf) { if(c > 0 && c < ' ') { c = ' '; } } // Check for invalid values return sampleHeader.GetInvalidByteScore(); } // Count malformed bytes in MOD pattern data static uint32 CountMalformedMODPatternData(const MODPatternData &patternData, const bool allow31Samples) { const uint8 mask = allow31Samples ? 0xE0 : 0xF0; uint32 malformedBytes = 0; for(const auto &row : patternData) { for(const auto &data : row) { if(data[0] & mask) malformedBytes++; } } return malformedBytes; } // Check if number of malformed bytes in MOD pattern data exceeds some threshold template static bool ValidateMODPatternData(TFileReader &file, const uint32 threshold, const bool allow31Samples) { MODPatternData patternData; if(!file.Read(patternData)) return false; return CountMalformedMODPatternData(patternData, allow31Samples) <= threshold; } // Parse the order list to determine how many patterns are used in the file. static PATTERNINDEX GetNumPatterns(FileReader &file, ModSequence &Order, ORDERINDEX numOrders, SmpLength totalSampleLen, CHANNELINDEX &numChannels, SmpLength wowSampleLen, bool validateHiddenPatterns) { PATTERNINDEX numPatterns = 0; // Total number of patterns in file (determined by going through the whole order list) with pattern number < 128 PATTERNINDEX officialPatterns = 0; // Number of patterns only found in the "official" part of the order list (i.e. order positions < claimed order length) PATTERNINDEX numPatternsIllegal = 0; // Total number of patterns in file, also counting in "invalid" pattern indexes >= 128 for(ORDERINDEX ord = 0; ord < 128; ord++) { PATTERNINDEX pat = Order[ord]; if(pat < 128 && numPatterns <= pat) { numPatterns = pat + 1; if(ord < numOrders) { officialPatterns = numPatterns; } } if(pat >= numPatternsIllegal) { numPatternsIllegal = pat + 1; } } // Remove the garbage patterns past the official order end now that we don't need them anymore. Order.resize(numOrders); const size_t patternStartOffset = file.GetPosition(); const size_t sizeWithoutPatterns = totalSampleLen + patternStartOffset; const size_t sizeWithOfficialPatterns = sizeWithoutPatterns + officialPatterns * numChannels * 256; if(wowSampleLen && (wowSampleLen + patternStartOffset) + numPatterns * 8 * 256 == (file.GetLength() & ~1)) { // Check if this is a Mod's Grave WOW file... WOW files use the M.K. magic but are actually 8CHN files. // We do a simple pattern validation as well for regular MOD files that have non-module data attached at the end // (e.g. ponylips.mod, MD5 c039af363b1d99a492dafc5b5f9dd949, SHA1 1bee1941c47bc6f913735ce0cf1880b248b8fc93) file.Seek(patternStartOffset + numPatterns * 4 * 256); if(ValidateMODPatternData(file, 16, true)) numChannels = 8; file.Seek(patternStartOffset); } else if(numPatterns != officialPatterns && (validateHiddenPatterns || sizeWithOfficialPatterns == file.GetLength())) { // 15-sample SoundTracker specifics: // Fix SoundTracker modules where "hidden" patterns should be ignored. // razor-1911.mod (MD5 b75f0f471b0ae400185585ca05bf7fe8, SHA1 4de31af234229faec00f1e85e1e8f78f405d454b) // and captain_fizz.mod (MD5 55bd89fe5a8e345df65438dbfc2df94e, SHA1 9e0e8b7dc67939885435ea8d3ff4be7704207a43) // seem to have the "correct" file size when only taking the "official" patterns into account, // but they only play correctly when also loading the inofficial patterns. // On the other hand, the SoundTracker module // wolf1.mod (MD5 a4983d7a432d324ce8261b019257f4ed, SHA1 aa6b399d02546bcb6baf9ec56a8081730dea3f44), // wolf3.mod (MD5 af60840815aa9eef43820a7a04417fa6, SHA1 24d6c2e38894f78f6c5c6a4b693a016af8fa037b) // and jean_baudlot_-_bad_dudes_vs_dragonninja-dragonf.mod (MD5 fa48e0f805b36bdc1833f6b82d22d936, SHA1 39f2f8319f4847fe928b9d88eee19d79310b9f91) // only play correctly if we ignore the hidden patterns. // Hence, we have a peek at the first hidden pattern and check if it contains a lot of illegal data. // If that is the case, we assume it's part of the sample data and only consider the "official" patterns. // 31-sample NoiseTracker / ProTracker specifics: // Interestingly, (broken) variants of the ProTracker modules // "killing butterfly" (MD5 bd676358b1dbb40d40f25435e845cf6b, SHA1 9df4ae21214ff753802756b616a0cafaeced8021), // "quartex" by Reflex (MD5 35526bef0fb21cb96394838d94c14bab, SHA1 116756c68c7b6598dcfbad75a043477fcc54c96c), // seem to have the "correct" file size when only taking the "official" patterns into account, but they only play // correctly when also loading the inofficial patterns. // On the other hand, "Shofixti Ditty.mod" from Star Control 2 (MD5 62b7b0819123400e4d5a7813eef7fc7d, SHA1 8330cd595c61f51c37a3b6f2a8559cf3fcaaa6e8) // doesn't sound correct when taking the second "inofficial" pattern into account. file.Seek(patternStartOffset + officialPatterns * numChannels * 256); if(!ValidateMODPatternData(file, 64, true)) numPatterns = officialPatterns; file.Seek(patternStartOffset); } if(numPatternsIllegal > numPatterns && sizeWithoutPatterns + numPatternsIllegal * numChannels * 256 == file.GetLength()) { // Even those illegal pattern indexes (> 128) appear to be valid... What a weird file! // e.g. NIETNU.MOD, where the end of the order list is filled with FF rather than 00, and the file actually contains 256 patterns. numPatterns = numPatternsIllegal; } else if(numPatternsIllegal >= 0xFF) { // Patterns FE and FF are used with S3M semantics (e.g. some MODs written with old OpenMPT versions) Order.Replace(0xFE, Order.GetIgnoreIndex()); Order.Replace(0xFF, Order.GetInvalidPatIndex()); } return numPatterns; } void CSoundFile::ReadMODPatternEntry(FileReader &file, ModCommand &m) { ReadMODPatternEntry(file.ReadArray(), m); } void CSoundFile::ReadMODPatternEntry(const std::array data, ModCommand &m) { // Read Period uint16 period = (((static_cast(data[0]) & 0x0F) << 8) | data[1]); size_t note = NOTE_NONE; if(period > 0 && period != 0xFFF) { note = std::size(ProTrackerPeriodTable) + 23 + NOTE_MIN; for(size_t i = 0; i < std::size(ProTrackerPeriodTable); i++) { if(period >= ProTrackerPeriodTable[i]) { if(period != ProTrackerPeriodTable[i] && i != 0) { uint16 p1 = ProTrackerPeriodTable[i - 1]; uint16 p2 = ProTrackerPeriodTable[i]; if(p1 - period < (period - p2)) { note = i + 23 + NOTE_MIN; break; } } note = i + 24 + NOTE_MIN; break; } } } m.note = static_cast(note); // Read Instrument m.instr = (data[2] >> 4) | (data[0] & 0x10); // Read Effect m.command = data[2] & 0x0F; m.param = data[3]; } struct MODMagicResult { const mpt::uchar *madeWithTracker = nullptr; uint32 invalidByteThreshold = MODSampleHeader::INVALID_BYTE_THRESHOLD; uint16 patternDataOffset = 1084; CHANNELINDEX numChannels = 0; bool isNoiseTracker = false; bool isStartrekker = false; bool isGenericMultiChannel = false; bool setMODVBlankTiming = false; }; static bool CheckMODMagic(const char magic[4], MODMagicResult &result) { if(IsMagic(magic, "M.K.") // ProTracker and compatible || IsMagic(magic, "M!K!") // ProTracker (>64 patterns) || IsMagic(magic, "PATT") // ProTracker 3.6 || IsMagic(magic, "NSMS") // kingdomofpleasure.mod by bee hunter || IsMagic(magic, "LARD")) // judgement_day_gvine.mod by 4-mat { result.madeWithTracker = UL_("Generic ProTracker or compatible"); result.numChannels = 4; } else if(IsMagic(magic, "M&K!") // "His Master's Noise" musicdisk || IsMagic(magic, "FEST") // "His Master's Noise" musicdisk || IsMagic(magic, "N.T.")) { result.madeWithTracker = UL_("NoiseTracker"); result.isNoiseTracker = true; result.numChannels = 4; } else if(IsMagic(magic, "OKTA") || IsMagic(magic, "OCTA")) { // Oktalyzer result.madeWithTracker = UL_("Oktalyzer"); result.numChannels = 8; } else if(IsMagic(magic, "CD81") || IsMagic(magic, "CD61")) { // Octalyser on Atari STe/Falcon result.madeWithTracker = UL_("Octalyser (Atari)"); result.numChannels = magic[2] - '0'; } else if(IsMagic(magic, "M\0\0\0") || IsMagic(magic, "8\0\0\0")) { // Inconexia demo by Iguana, delta samples (https://www.pouet.net/prod.php?which=830) result.madeWithTracker = UL_("Inconexia demo (delta samples)"); result.invalidByteThreshold = MODSampleHeader::INVALID_BYTE_FRAGILE_THRESHOLD; result.numChannels = (magic[0] == '8') ? 8 : 4; } else if(!memcmp(magic, "FA0", 3) && magic[3] >= '4' && magic[3] <= '8') { // Digital Tracker on Atari Falcon result.madeWithTracker = UL_("Digital Tracker"); result.numChannels = magic[3] - '0'; // Digital Tracker MODs contain four bytes (00 40 00 00) right after the magic bytes which don't seem to do anything special. result.patternDataOffset = 1088; } else if((!memcmp(magic, "FLT", 3) || !memcmp(magic, "EXO", 3)) && magic[3] >= '4' && magic[3] <= '9') { // FLTx / EXOx - Startrekker by Exolon / Fairlight result.madeWithTracker = UL_("Startrekker"); result.isStartrekker = true; result.setMODVBlankTiming = true; result.numChannels = magic[3] - '0'; } else if(magic[0] >= '1' && magic[0] <= '9' && !memcmp(magic + 1, "CHN", 3)) { // xCHN - Many trackers result.madeWithTracker = UL_("Generic MOD-compatible Tracker"); result.isGenericMultiChannel = true; result.numChannels = magic[0] - '0'; } else if(magic[0] >= '1' && magic[0] <= '9' && magic[1] >= '0' && magic[1] <= '9' && (!memcmp(magic + 2, "CH", 2) || !memcmp(magic + 2, "CN", 2))) { // xxCN / xxCH - Many trackers result.madeWithTracker = UL_("Generic MOD-compatible Tracker"); result.isGenericMultiChannel = true; result.numChannels = (magic[0] - '0') * 10 + magic[1] - '0'; } else if(!memcmp(magic, "TDZ", 3) && magic[3] >= '1' && magic[3] <= '9') { // TDZx - TakeTracker (only TDZ1-TDZ3 should exist, but historically this code only supported 4-9 channels, so we keep those for the unlikely case that they were actually used for something) result.madeWithTracker = UL_("TakeTracker"); result.numChannels = magic[3] - '0'; } else { return false; } return true; } CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderMOD(MemoryFileReader file, const uint64 *pfilesize) { if(!file.LengthIsAtLeast(1080 + 4)) { return ProbeWantMoreData; } file.Seek(1080); char magic[4]; file.ReadArray(magic); MODMagicResult modMagicResult; if(!CheckMODMagic(magic, modMagicResult)) { return ProbeFailure; } file.Seek(20); uint32 invalidBytes = 0; for(SAMPLEINDEX smp = 1; smp <= 31; smp++) { MODSampleHeader sampleHeader; file.ReadStruct(sampleHeader); invalidBytes += sampleHeader.GetInvalidByteScore(); } if(invalidBytes > modMagicResult.invalidByteThreshold) { return ProbeFailure; } MPT_UNREFERENCED_PARAMETER(pfilesize); return ProbeSuccess; } bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags) { char magic[4]; if(!file.Seek(1080) || !file.ReadArray(magic)) { return false; } InitializeGlobals(MOD_TYPE_MOD); MODMagicResult modMagicResult; if(!CheckMODMagic(magic, modMagicResult) || modMagicResult.numChannels < 1 || modMagicResult.numChannels > MAX_BASECHANNELS) { return false; } if(loadFlags == onlyVerifyHeader) { return true; } m_nChannels = modMagicResult.numChannels; bool isNoiseTracker = modMagicResult.isNoiseTracker; bool isStartrekker = modMagicResult.isStartrekker; bool isGenericMultiChannel = modMagicResult.isGenericMultiChannel; bool isInconexia = IsMagic(magic, "M\0\0\0") || IsMagic(magic, "8\0\0\0"); // A loop length of zero will freeze ProTracker, so assume that modules having such a value were not meant to be played on Amiga. Fixes LHS_MI.MOD bool hasRepLen0 = false; // Empty sample slots typically should have a default volume of 0 in ProTracker bool hasEmptySampleWithVolume = false; if(modMagicResult.setMODVBlankTiming) { m_playBehaviour.set(kMODVBlankTiming); } // Startrekker 8 channel mod (needs special treatment, see below) const bool isFLT8 = isStartrekker && m_nChannels == 8; // Only apply VBlank tests to M.K. (ProTracker) modules. const bool isMdKd = IsMagic(magic, "M.K."); // Adjust finetune values for modules saved with "His Master's Noisetracker" const bool isHMNT = IsMagic(magic, "M&K!") || IsMagic(magic, "FEST"); bool maybeWOW = isMdKd; // Reading song title file.Seek(0); file.ReadString(m_songName, 20); // Load Sample Headers SmpLength totalSampleLen = 0, wowSampleLen = 0; m_nSamples = 31; uint32 invalidBytes = 0; for(SAMPLEINDEX smp = 1; smp <= 31; smp++) { MODSampleHeader sampleHeader; invalidBytes += ReadSample(file, sampleHeader, Samples[smp], m_szNames[smp], m_nChannels == 4); totalSampleLen += Samples[smp].nLength; if(isHMNT) Samples[smp].nFineTune = -static_cast(sampleHeader.finetune << 3); else if(Samples[smp].nLength > 65535) isNoiseTracker = false; if(sampleHeader.length && !sampleHeader.loopLength) hasRepLen0 = true; else if(!sampleHeader.length && sampleHeader.volume == 64) hasEmptySampleWithVolume = true; if(maybeWOW) { // Some WOW files rely on sample length 1 being counted as well wowSampleLen += sampleHeader.length * 2; // WOW files are converted 669 files, which don't support finetune or default volume if(sampleHeader.finetune) maybeWOW = false; else if(sampleHeader.length > 0 && sampleHeader.volume != 64) maybeWOW = false; } } // If there is too much binary garbage in the sample headers, reject the file. if(invalidBytes > modMagicResult.invalidByteThreshold) { return false; } // Read order information MODFileHeader fileHeader; file.ReadStruct(fileHeader); file.Seek(modMagicResult.patternDataOffset); if(fileHeader.restartPos > 0) maybeWOW = false; if(!maybeWOW) wowSampleLen = 0; ReadOrderFromArray(Order(), fileHeader.orderList); ORDERINDEX realOrders = fileHeader.numOrders; if(realOrders > 128) { // beatwave.mod by Sidewinder claims to have 129 orders. (MD5: 8a029ac498d453beb929db9a73c3c6b4, SHA1: f7b76fb9f477b07a2e78eb10d8624f0df262cde7 - the version from ModArchive, not ModLand) realOrders = 128; } else if(realOrders == 0) { // Is this necessary? realOrders = 128; while(realOrders > 1 && Order()[realOrders - 1] == 0) { realOrders--; } } // Get number of patterns (including some order list sanity checks) PATTERNINDEX numPatterns = GetNumPatterns(file, Order(), realOrders, totalSampleLen, m_nChannels, wowSampleLen, false); if(maybeWOW && GetNumChannels() == 8) { // M.K. with 8 channels = Mod's Grave modMagicResult.madeWithTracker = UL_("Mod's Grave"); isGenericMultiChannel = true; } if(isFLT8) { // FLT8 has only even order items, so divide by two. for(auto &pat : Order()) { pat /= 2u; } } // Restart position sanity checks realOrders--; Order().SetRestartPos(fileHeader.restartPos); // (Ultimate) Soundtracker didn't have a restart position, but instead stored a default tempo in this value. // The default value for this is 0x78 (120 BPM). This is probably the reason why some M.K. modules // have this weird restart position. I think I've read somewhere that NoiseTracker actually writes 0x78 there. // M.K. files that have restart pos == 0x78: action's batman by DJ Uno, VALLEY.MOD, WormsTDC.MOD, ZWARTZ.MOD // Files that have an order list longer than 0x78 with restart pos = 0x78: my_shoe_is_barking.mod, papermix.mod // - in both cases it does not appear like the restart position should be used. MPT_ASSERT(fileHeader.restartPos != 0x78 || fileHeader.restartPos + 1u >= realOrders); if(fileHeader.restartPos > realOrders || (fileHeader.restartPos == 0x78 && m_nChannels == 4)) { Order().SetRestartPos(0); } m_nDefaultSpeed = 6; m_nDefaultTempo.Set(125); m_nMinPeriod = 14 * 4; m_nMaxPeriod = 3424 * 4; // Prevent clipping based on number of channels... If all channels are playing at full volume, "256 / #channels" // is the maximum possible sample pre-amp without getting distortion (Compatible mix levels given). // The more channels we have, the less likely it is that all of them are used at the same time, though, so cap at 32... m_nSamplePreAmp = Clamp(256 / m_nChannels, 32, 128); m_SongFlags.reset(); // SONG_ISAMIGA will be set conditionally // Setup channel pan positions and volume SetupMODPanning(); // Before loading patterns, apply some heuristics: // - Scan patterns to check if file could be a NoiseTracker file in disguise. // In this case, the parameter of Dxx commands needs to be ignored. // - Use the same code to find notes that would be out-of-range on Amiga. // - Detect 7-bit panning and whether 8xx / E8x commands should be interpreted as panning at all. bool onlyAmigaNotes = true; bool fix7BitPanning = false; uint8 maxPanning = 0; // For detecting 8xx-as-sync const uint8 ENABLE_MOD_PANNING_THRESHOLD = 0x30; if(!isNoiseTracker) { bool leftPanning = false, extendedPanning = false; // For detecting 800-880 panning isNoiseTracker = isMdKd; for(PATTERNINDEX pat = 0; pat < numPatterns; pat++) { uint16 patternBreaks = 0; for(uint32 i = 0; i < 256; i++) { ModCommand m; ReadMODPatternEntry(file, m); if(!m.IsAmigaNote()) { isNoiseTracker = onlyAmigaNotes = false; } if((m.command > 0x06 && m.command < 0x0A) || (m.command == 0x0E && m.param > 0x01) || (m.command == 0x0F && m.param > 0x1F) || (m.command == 0x0D && ++patternBreaks > 1)) { isNoiseTracker = false; } if(m.command == 0x08) { maxPanning = std::max(maxPanning, m.param); if(m.param < 0x80) leftPanning = true; else if(m.param > 0x8F && m.param != 0xA4) extendedPanning = true; } else if(m.command == 0x0E && (m.param & 0xF0) == 0x80) { maxPanning = std::max(maxPanning, static_cast((m.param & 0x0F) << 4)); } } } fix7BitPanning = leftPanning && !extendedPanning && maxPanning >= ENABLE_MOD_PANNING_THRESHOLD; } file.Seek(modMagicResult.patternDataOffset); const CHANNELINDEX readChannels = (isFLT8 ? 4 : m_nChannels); // 4 channels per pattern in FLT8 format. if(isFLT8) numPatterns++; // as one logical pattern consists of two real patterns in FLT8 format, the highest pattern number has to be increased by one. bool hasTempoCommands = false, definitelyCIA = false; // for detecting VBlank MODs // Heuristic for rejecting E0x commands that are most likely not intended to actually toggle the Amiga LED filter, like in naen_leijasi_ptk.mod by ilmarque bool filterState = false; int filterTransitions = 0; // Reading patterns Patterns.ResizeArray(numPatterns); for(PATTERNINDEX pat = 0; pat < numPatterns; pat++) { ModCommand *rowBase = nullptr; if(isFLT8) { // FLT8: Only create "even" patterns and either write to channel 1 to 4 (even patterns) or 5 to 8 (odd patterns). PATTERNINDEX actualPattern = pat / 2u; if((pat % 2u) == 0 && !Patterns.Insert(actualPattern, 64)) { break; } rowBase = Patterns[actualPattern].GetpModCommand(0, (pat % 2u) == 0 ? 0 : 4); } else { if(!Patterns.Insert(pat, 64)) { break; } rowBase = Patterns[pat].GetpModCommand(0, 0); } if(rowBase == nullptr || !(loadFlags & loadPatternData)) { break; } // For detecting PT1x mode std::vector lastInstrument(GetNumChannels(), 0); std::vector instrWithoutNoteCount(GetNumChannels(), 0); for(ROWINDEX row = 0; row < 64; row++, rowBase += m_nChannels) { // If we have more than one Fxx command on this row and one can be interpreted as speed // and the other as tempo, we can be rather sure that it is not a VBlank mod. bool hasSpeedOnRow = false, hasTempoOnRow = false; for(CHANNELINDEX chn = 0; chn < readChannels; chn++) { ModCommand &m = rowBase[chn]; ReadMODPatternEntry(file, m); if(m.command || m.param) { if(isStartrekker && m.command == 0x0E) { // No support for Startrekker assembly macros m.command = CMD_NONE; m.param = 0; } else if(isStartrekker && m.command == 0x0F && m.param > 0x1F) { // Startrekker caps speed at 31 ticks per row m.param = 0x1F; } ConvertModCommand(m); } // Perform some checks for our heuristics... if(m.command == CMD_TEMPO) { hasTempoOnRow = true; if(m.param < 100) hasTempoCommands = true; } else if(m.command == CMD_SPEED) { hasSpeedOnRow = true; } else if(m.command == CMD_PATTERNBREAK && isNoiseTracker) { m.param = 0; } else if(m.command == CMD_PANNING8 && fix7BitPanning) { // Fix MODs with 7-bit + surround panning if(m.param == 0xA4) { m.command = CMD_S3MCMDEX; m.param = 0x91; } else { m.param = mpt::saturate_cast(m.param * 2); } } else if(m.command == CMD_MODCMDEX && m.param < 0x10) { // Count LED filter transitions bool newState = !(m.param & 0x01); if(newState != filterState) { filterState = newState; filterTransitions++; } } if(m.note == NOTE_NONE && m.instr > 0 && !isFLT8) { if(lastInstrument[chn] > 0 && lastInstrument[chn] != m.instr) { // Arbitrary threshold for enabling sample swapping: 4 consecutive "sample swaps" in one pattern. if(++instrWithoutNoteCount[chn] >= 4) { m_playBehaviour.set(kMODSampleSwap); } } } else if(m.note != NOTE_NONE) { instrWithoutNoteCount[chn] = 0; } if(m.instr != 0) { lastInstrument[chn] = m.instr; } } if(hasSpeedOnRow && hasTempoOnRow) definitelyCIA = true; } } if(onlyAmigaNotes && !hasRepLen0 && (IsMagic(magic, "M.K.") || IsMagic(magic, "M!K!") || IsMagic(magic, "PATT"))) { // M.K. files that don't exceed the Amiga note limit (fixes mod.mothergoose) m_SongFlags.set(SONG_AMIGALIMITS); // Need this for professionaltracker.mod by h0ffman (SHA1: 9a7c52cbad73ed2a198ee3fa18d3704ea9f546ff) m_SongFlags.set(SONG_PT_MODE); m_playBehaviour.set(kMODSampleSwap); m_playBehaviour.set(kMODOutOfRangeNoteDelay); m_playBehaviour.set(kMODTempoOnSecondTick); // Arbitrary threshold for deciding that 8xx effects are only used as sync markers if(maxPanning < ENABLE_MOD_PANNING_THRESHOLD) { m_playBehaviour.set(kMODIgnorePanning); if(fileHeader.restartPos != 0x7F) { // Don't enable these hacks for ScreamTracker modules (restart position = 0x7F), to fix e.g. sample 10 in BASIC001.MOD (SHA1: 11298a5620e677beaa50bd4ed00c3710b75c81af) // Note: restart position = 0x7F can also be found in ProTracker modules, e.g. professionaltracker.mod by h0ffman m_playBehaviour.set(kMODOneShotLoops); } } } else if(!onlyAmigaNotes && fileHeader.restartPos == 0x7F && isMdKd && fileHeader.restartPos + 1u >= realOrders) { modMagicResult.madeWithTracker = UL_("Scream Tracker"); } if(onlyAmigaNotes && !isGenericMultiChannel && filterTransitions < 7) { m_SongFlags.set(SONG_ISAMIGA); } if(isGenericMultiChannel || isMdKd) { m_playBehaviour.set(kFT2MODTremoloRampWaveform); } if(isInconexia) { m_playBehaviour.set(kMODIgnorePanning); } // Reading samples if(loadFlags & loadSampleData) { file.Seek(modMagicResult.patternDataOffset + (readChannels * 64 * 4) * numPatterns); for(SAMPLEINDEX smp = 1; smp <= 31; smp++) { ModSample &sample = Samples[smp]; if(sample.nLength) { SampleIO::Encoding encoding = SampleIO::signedPCM; if(isInconexia) encoding = SampleIO::deltaPCM; else if(file.ReadMagic("ADPCM")) encoding = SampleIO::ADPCM; SampleIO sampleIO( SampleIO::_8bit, SampleIO::mono, SampleIO::littleEndian, encoding); // Fix sample 6 in MOD.shorttune2, which has a replen longer than the sample itself. // ProTracker reads beyond the end of the sample when playing. Normally samples are // adjacent in PT's memory, so we simply read into the next sample in the file. // On the other hand, the loop points in Purple Motions's SOUL-O-M.MOD are completely broken and shouldn't be treated like this. // As it was most likely written in Scream Tracker, it has empty sample slots with a default volume of 64, which we use for // rejecting this quirk for that file. FileReader::off_t nextSample = file.GetPosition() + sampleIO.CalculateEncodedSize(sample.nLength); if(isMdKd && onlyAmigaNotes && !hasEmptySampleWithVolume) sample.nLength = std::max(sample.nLength, sample.nLoopEnd); sampleIO.ReadSample(sample, file); file.Seek(nextSample); } } } #if defined(MPT_EXTERNAL_SAMPLES) || defined(MPT_BUILD_FUZZER) // Detect Startrekker files with external synth instruments. // Note: Synthesized AM samples may overwrite existing samples (e.g. sample 1 in fa.worse face.mod), // hence they are loaded here after all regular samples have been loaded. if((loadFlags & loadSampleData) && isStartrekker) { #ifdef MPT_EXTERNAL_SAMPLES std::optional amFile; FileReader amData; if(file.GetOptionalFileName()) { mpt::PathString filename = file.GetOptionalFileName().value(); // Find instrument definition file const mpt::PathString exts[] = {P_(".nt"), P_(".NT"), P_(".as"), P_(".AS")}; for(const auto &ext : exts) { mpt::PathString infoName = filename + ext; char stMagic[16]; if(infoName.IsFile()) { amFile.emplace(infoName, SettingCacheCompleteFileBeforeLoading()); if(amFile->IsValid() && (amData = GetFileReader(*amFile)).IsValid() && amData.ReadArray(stMagic)) { if(!memcmp(stMagic, "ST1.2 ModuleINFO", 16)) modMagicResult.madeWithTracker = UL_("Startrekker 1.2"); else if(!memcmp(stMagic, "ST1.3 ModuleINFO", 16)) modMagicResult.madeWithTracker = UL_("Startrekker 1.3"); else if(!memcmp(stMagic, "AudioSculpture10", 16)) modMagicResult.madeWithTracker = UL_("AudioSculpture 1.0"); else continue; if(amData.Seek(144)) { // Looks like a valid instrument definition file! m_nInstruments = 31; break; } } } } } #elif defined(MPT_BUILD_FUZZER) // For fuzzing this part of the code, just take random data from patterns FileReader amData = file.GetChunkAt(1084, 31 * 120); m_nInstruments = 31; #endif for(SAMPLEINDEX smp = 1; smp <= m_nInstruments; smp++) { // For Startrekker AM synthesis, we need instrument envelopes. ModInstrument *ins = AllocateInstrument(smp, smp); if(ins == nullptr) { break; } ins->name = m_szNames[smp]; AMInstrument am; // Allow partial reads for fa.worse face.mod if(amData.ReadStructPartial(am) && !memcmp(am.am, "AM", 2) && am.waveform < 4) { am.ConvertToMPT(Samples[smp], *ins, AccessPRNG()); } // This extra padding is probably present to have identical block sizes for AM and FM instruments. amData.Skip(120 - sizeof(AMInstrument)); } } #endif // MPT_EXTERNAL_SAMPLES || MPT_BUILD_FUZZER // Fix VBlank MODs. Arbitrary threshold: 8 minutes (enough for "frame of mind" by Dascon...). // Basically, this just converts all tempo commands into speed commands // for MODs which are supposed to have VBlank timing (instead of CIA timing). // There is no perfect way to do this, since both MOD types look the same, // but the most reliable way is to simply check for extremely long songs // (as this would indicate that e.g. a F30 command was really meant to set // the ticks per row to 48, and not the tempo to 48 BPM). // In the pattern loader above, a second condition is used: Only tempo commands // below 100 BPM are taken into account. Furthermore, only M.K. (ProTracker) // modules are checked. if(isMdKd && hasTempoCommands && !definitelyCIA) { const double songTime = GetLength(eNoAdjust).front().duration; if(songTime >= 480.0) { m_playBehaviour.set(kMODVBlankTiming); if(GetLength(eNoAdjust, GetLengthTarget(songTime)).front().targetReached) { // This just makes things worse, song is at least as long as in CIA mode // Obviously we should keep using CIA timing then... m_playBehaviour.reset(kMODVBlankTiming); } else { modMagicResult.madeWithTracker = UL_("ProTracker (VBlank)"); } } } std::transform(std::begin(magic), std::end(magic), std::begin(magic), [](unsigned char c) -> unsigned char { return (c < ' ') ? ' ' : c; }); m_modFormat.formatName = MPT_UFORMAT("ProTracker MOD ({})")(mpt::ToUnicode(mpt::Charset::ASCII, std::string(std::begin(magic), std::end(magic)))); m_modFormat.type = U_("mod"); if(modMagicResult.madeWithTracker) m_modFormat.madeWithTracker = modMagicResult.madeWithTracker; m_modFormat.charset = mpt::Charset::ISO8859_1; return true; } // Check if a name string is valid (i.e. doesn't contain binary garbage data) template static uint32 CountInvalidChars(const char (&name)[N]) { uint32 invalidChars = 0; for(int8 c : name) // char can be signed or unsigned { // Check for any Extended ASCII and control characters if(c != 0 && c < ' ') invalidChars++; } return invalidChars; } // We'll have to do some heuristic checks to find out whether this is an old Ultimate Soundtracker module // or if it was made with the newer Soundtracker versions. // Thanks for Fraggie for this information! (https://www.un4seen.com/forum/?topic=14471.msg100829#msg100829) enum STVersions { UST1_00, // Ultimate Soundtracker 1.0-1.21 (K. Obarski) UST1_80, // Ultimate Soundtracker 1.8-2.0 (K. Obarski) ST2_00_Exterminator, // SoundTracker 2.0 (The Exterminator), D.O.C. Sountracker II (Unknown/D.O.C.) ST_III, // Defjam Soundtracker III (Il Scuro/Defjam), Alpha Flight SoundTracker IV (Alpha Flight), D.O.C. SoundTracker IV (Unknown/D.O.C.), D.O.C. SoundTracker VI (Unknown/D.O.C.) ST_IX, // D.O.C. SoundTracker IX (Unknown/D.O.C.) MST1_00, // Master Soundtracker 1.0 (Tip/The New Masters) ST2_00, // SoundTracker 2.0, 2.1, 2.2 (Unknown/D.O.C.) }; struct M15FileHeaders { char songname[20]; MODSampleHeader sampleHeaders[15]; MODFileHeader fileHeader; }; MPT_BINARY_STRUCT(M15FileHeaders, 20 + 15 * 30 + 130) static bool ValidateHeader(const M15FileHeaders &fileHeaders) { // In theory, sample and song names should only ever contain printable ASCII chars and null. // However, there are quite a few SoundTracker modules in the wild with random // characters. To still be able to distguish them from other formats, we just reject // files with *too* many bogus characters. Arbitrary threshold: 48 bogus characters in total // or more than 5 invalid characters just in the title alone. uint32 invalidChars = CountInvalidChars(fileHeaders.songname); if(invalidChars > 5) { return false; } SmpLength totalSampleLen = 0; uint8 allVolumes = 0; for(SAMPLEINDEX smp = 0; smp < 15; smp++) { const MODSampleHeader &sampleHeader = fileHeaders.sampleHeaders[smp]; invalidChars += CountInvalidChars(sampleHeader.name); // Sanity checks - invalid character count adjusted for ata.mod (MD5 937b79b54026fa73a1a4d3597c26eace, SHA1 3322ca62258adb9e0ae8e9afe6e0c29d39add874) if(invalidChars > 48 || sampleHeader.volume > 64 || sampleHeader.finetune != 0 || sampleHeader.length > 32768) { return false; } totalSampleLen += sampleHeader.length; allVolumes |= sampleHeader.volume; } // Reject any files with no (or only silent) samples at all, as this might just be a random binary file (e.g. ID3 tags with tons of padding) if(totalSampleLen == 0 || allVolumes == 0) { return false; } // Sanity check: No more than 128 positions. ST's GUI limits tempo to [1, 220]. // There are some mods with a tempo of 0 (explora3-death.mod) though, so ignore the lower limit. if(fileHeaders.fileHeader.numOrders > 128 || fileHeaders.fileHeader.restartPos > 220) { return false; } uint8 maxPattern = *std::max_element(std::begin(fileHeaders.fileHeader.orderList), std::end(fileHeaders.fileHeader.orderList)); // Sanity check: 64 patterns max. if(maxPattern > 63) { return false; } // No playable song, and lots of null values => most likely a sparse binary file but not a module if(fileHeaders.fileHeader.restartPos == 0 && fileHeaders.fileHeader.numOrders == 0 && maxPattern == 0) { return false; } return true; } template static bool ValidateFirstM15Pattern(TFileReader &file) { // threshold is chosen as: [threshold for all patterns combined] / [max patterns] * [margin, do not reject too much] return ValidateMODPatternData(file, 512 / 64 * 2, false); } CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderM15(MemoryFileReader file, const uint64 *pfilesize) { M15FileHeaders fileHeaders; if(!file.ReadStruct(fileHeaders)) { return ProbeWantMoreData; } if(!ValidateHeader(fileHeaders)) { return ProbeFailure; } if(!file.CanRead(sizeof(MODPatternData))) { return ProbeWantMoreData; } if(!ValidateFirstM15Pattern(file)) { return ProbeFailure; } MPT_UNREFERENCED_PARAMETER(pfilesize); return ProbeSuccess; } bool CSoundFile::ReadM15(FileReader &file, ModLoadingFlags loadFlags) { file.Rewind(); M15FileHeaders fileHeaders; if(!file.ReadStruct(fileHeaders)) { return false; } if(!ValidateHeader(fileHeaders)) { return false; } if(!ValidateFirstM15Pattern(file)) { return false; } char songname[20]; std::memcpy(songname, fileHeaders.songname, 20); InitializeGlobals(MOD_TYPE_MOD); m_playBehaviour.reset(kMODOneShotLoops); m_playBehaviour.set(kMODIgnorePanning); m_playBehaviour.set(kMODSampleSwap); // untested m_nChannels = 4; STVersions minVersion = UST1_00; bool hasDiskNames = true; SmpLength totalSampleLen = 0; m_nSamples = 15; file.Seek(20); for(SAMPLEINDEX smp = 1; smp <= 15; smp++) { MODSampleHeader sampleHeader; ReadSample(file, sampleHeader, Samples[smp], m_szNames[smp], true); totalSampleLen += Samples[smp].nLength; if(m_szNames[smp][0] && ((memcmp(m_szNames[smp].buf, "st-", 3) && memcmp(m_szNames[smp].buf, "ST-", 3)) || m_szNames[smp][5] != ':')) { // Ultimate Soundtracker 1.8 and D.O.C. SoundTracker IX always have sample names containing disk names. hasDiskNames = false; } // Loop start is always in bytes, not words, so don't trust the auto-fix magic in the sample header conversion (fixes loop of "st-01:asia" in mod.drag 10) if(sampleHeader.loopLength > 1) { Samples[smp].nLoopStart = sampleHeader.loopStart; Samples[smp].nLoopEnd = sampleHeader.loopStart + sampleHeader.loopLength * 2; Samples[smp].SanitizeLoops(); } // UST only handles samples up to 9999 bytes. Master Soundtracker 1.0 and SoundTracker 2.0 introduce 32KB samples. if(sampleHeader.length > 4999 || sampleHeader.loopStart > 9999) minVersion = std::max(minVersion, MST1_00); } MODFileHeader fileHeader; file.ReadStruct(fileHeader); ReadOrderFromArray(Order(), fileHeader.orderList); PATTERNINDEX numPatterns = GetNumPatterns(file, Order(), fileHeader.numOrders, totalSampleLen, m_nChannels, 0, true); // Most likely just a file with lots of NULs at the start if(fileHeader.restartPos == 0 && fileHeader.numOrders == 0 && numPatterns <= 1) { return false; } // Let's see if the file is too small (including some overhead for broken files like sll7.mod or ghostbus.mod) if(file.BytesLeft() + 65536 < numPatterns * 64u * 4u * 4u + totalSampleLen) return false; if(loadFlags == onlyVerifyHeader) return true; // Now we can be pretty sure that this is a valid Soundtracker file. Set up default song settings. // explora3-death.mod has a tempo of 0 if(!fileHeader.restartPos) fileHeader.restartPos = 0x78; // jjk55 by Jesper Kyd has a weird tempo set, but it needs to be ignored. if(!memcmp(songname, "jjk55", 6)) fileHeader.restartPos = 0x78; // Sample 7 in echoing.mod won't "loop" correctly if we don't convert the VBlank tempo. m_nDefaultTempo.Set(125); if(fileHeader.restartPos != 0x78) { // Convert to CIA timing m_nDefaultTempo = TEMPO((709379.0 * 125.0 / 50.0) / ((240 - fileHeader.restartPos) * 122.0)); if(minVersion > UST1_80) { // D.O.C. SoundTracker IX re-introduced the variable tempo after some other versions dropped it. minVersion = std::max(minVersion, hasDiskNames ? ST_IX : MST1_00); } else { // Ultimate Soundtracker 1.8 adds variable tempo minVersion = std::max(minVersion, hasDiskNames ? UST1_80 : ST2_00_Exterminator); } } m_nMinPeriod = 113 * 4; m_nMaxPeriod = 856 * 4; m_nSamplePreAmp = 64; m_SongFlags.set(SONG_PT_MODE); m_songName = mpt::String::ReadBuf(mpt::String::spacePadded, songname); // Setup channel pan positions and volume SetupMODPanning(); FileReader::off_t patOffset = file.GetPosition(); // Scan patterns to identify Ultimate Soundtracker modules. uint32 illegalBytes = 0, totalNumDxx = 0; for(PATTERNINDEX pat = 0; pat < numPatterns; pat++) { const bool patternInUse = mpt::contains(Order(), pat); uint8 numDxx = 0; uint8 emptyCmds = 0; MODPatternData patternData; file.ReadArray(patternData); if(patternInUse) { illegalBytes += CountMalformedMODPatternData(patternData, false); // Reject files that contain a lot of illegal pattern data. // STK.the final remix (MD5 5ff13cdbd77211d1103be7051a7d89c9, SHA1 e94dba82a5da00a4758ba0c207eb17e3a89c3aa3) // has one illegal byte, so we only reject after an arbitrary threshold has been passed. // This also allows to play some rather damaged files like // crockets.mod (MD5 995ed9f44cab995a0eeb19deb52e2a8b, SHA1 6c79983c3b7d55c9bc110b625eaa07ce9d75f369) // but naturally we cannot recover the broken data. // We only check patterns that are actually being used in the order list, because some bad rips of the // "operation wolf" soundtrack have 15 patterns for several songs, but the last few patterns are just garbage. // Apart from those hidden patterns, the files play fine. // Example: operation wolf - wolf1.mod (MD5 739acdbdacd247fbefcac7bc2d8abe6b, SHA1 e6b4813daacbf95f41ce9ec3b22520a2ae07eed8) if(illegalBytes > 512) return false; } for(ROWINDEX row = 0; row < 64; row++) { for(CHANNELINDEX chn = 0; chn < 4; chn++) { const auto &data = patternData[row][chn]; const uint8 eff = data[2] & 0x0F, param = data[3]; // Check for empty space between the last Dxx command and the beginning of another pattern if(emptyCmds != 0 && !memcmp(data.data(), "\0\0\0\0", 4)) { emptyCmds++; if(emptyCmds > 32) { // Since there is a lot of empty space after the last Dxx command, // we assume it's supposed to be a pattern break effect. minVersion = ST2_00; } } else { emptyCmds = 0; } switch(eff) { case 1: case 2: if(param > 0x1F && minVersion == UST1_80) { // If a 1xx / 2xx effect has a parameter greater than 0x20, it is assumed to be UST. minVersion = hasDiskNames ? UST1_80 : UST1_00; } else if(eff == 1 && param > 0 && param < 0x03) { // This doesn't look like an arpeggio. minVersion = std::max(minVersion, ST2_00_Exterminator); } else if(eff == 1 && (param == 0x37 || param == 0x47) && minVersion <= ST2_00_Exterminator) { // This suspiciously looks like an arpeggio. // Catch sleepwalk.mod by Karsten Obarski, which has a default tempo of 125 rather than 120 in the header, so gets mis-identified as a later tracker version. minVersion = hasDiskNames ? UST1_80 : UST1_00; } break; case 0x0B: minVersion = ST2_00; break; case 0x0C: case 0x0D: case 0x0E: minVersion = std::max(minVersion, ST2_00_Exterminator); if(eff == 0x0D) { emptyCmds = 1; if(param == 0 && row == 0) { // Fix a possible tracking mistake in Blood Money title - who wants to do a pattern break on the first row anyway? break; } numDxx++; } break; case 0x0F: minVersion = std::max(minVersion, ST_III); break; } } } if(numDxx > 0 && numDxx < 3) { // Not many Dxx commands in one pattern means they were probably pattern breaks minVersion = ST2_00; } totalNumDxx += numDxx; } // If there is a huge number of Dxx commands, this is extremely unlikely to be a SoundTracker 2.0 module if(totalNumDxx > numPatterns + 32u && minVersion == ST2_00) minVersion = MST1_00; file.Seek(patOffset); // Reading patterns if(loadFlags & loadPatternData) Patterns.ResizeArray(numPatterns); for(PATTERNINDEX pat = 0; pat < numPatterns; pat++) { MODPatternData patternData; file.ReadArray(patternData); if(!(loadFlags & loadPatternData) || !Patterns.Insert(pat, 64)) { continue; } uint8 autoSlide[4] = {0, 0, 0, 0}; for(ROWINDEX row = 0; row < 64; row++) { PatternRow rowBase = Patterns[pat].GetpModCommand(row, 0); for(CHANNELINDEX chn = 0; chn < 4; chn++) { ModCommand &m = rowBase[chn]; ReadMODPatternEntry(patternData[row][chn], m); if(!m.param || m.command == 0x0E) { autoSlide[chn] = 0; } if(m.command || m.param) { if(autoSlide[chn] != 0) { if(autoSlide[chn] & 0xF0) { m.volcmd = VOLCMD_VOLSLIDEUP; m.vol = autoSlide[chn] >> 4; } else { m.volcmd = VOLCMD_VOLSLIDEDOWN; m.vol = autoSlide[chn] & 0x0F; } } if(m.command == 0x0D) { if(minVersion != ST2_00) { // Dxy is volume slide in some Soundtracker versions, D00 is a pattern break in the latest versions. m.command = 0x0A; } else { m.param = 0; } } else if(m.command == 0x0C) { // Volume is sent as-is to the chip, which ignores the highest bit. m.param &= 0x7F; } else if(m.command == 0x0E && (m.param > 0x01 || minVersion < ST_IX)) { // Import auto-slides as normal slides and fake them using volume column slides. m.command = 0x0A; autoSlide[chn] = m.param; } else if(m.command == 0x0F) { // Only the low nibble is evaluated in Soundtracker. m.param &= 0x0F; } if(minVersion <= UST1_80) { // UST effects switch(m.command) { case 0: // jackdance.mod by Karsten Obarski has 0xy arpeggios... if(m.param < 0x03) { m.command = CMD_NONE; } else { m.command = CMD_ARPEGGIO; } break; case 1: m.command = CMD_ARPEGGIO; break; case 2: if(m.param & 0x0F) { m.command = CMD_PORTAMENTOUP; m.param &= 0x0F; } else if(m.param >> 4) { m.command = CMD_PORTAMENTODOWN; m.param >>= 4; } break; default: m.command = CMD_NONE; break; } } else { ConvertModCommand(m); } } else { autoSlide[chn] = 0; } } } } const mpt::uchar *madeWithTracker = UL_(""); switch(minVersion) { case UST1_00: madeWithTracker = UL_("Ultimate Soundtracker 1.0-1.21"); break; case UST1_80: madeWithTracker = UL_("Ultimate Soundtracker 1.8-2.0"); break; case ST2_00_Exterminator: madeWithTracker = UL_("SoundTracker 2.0 / D.O.C. SoundTracker II"); break; case ST_III: madeWithTracker = UL_("Defjam Soundtracker III / Alpha Flight SoundTracker IV / D.O.C. SoundTracker IV / VI"); break; case ST_IX: madeWithTracker = UL_("D.O.C. SoundTracker IX"); break; case MST1_00: madeWithTracker = UL_("Master Soundtracker 1.0"); break; case ST2_00: madeWithTracker = UL_("SoundTracker 2.0 / 2.1 / 2.2"); break; } m_modFormat.formatName = U_("Soundtracker"); m_modFormat.type = U_("stk"); m_modFormat.madeWithTracker = madeWithTracker; m_modFormat.charset = mpt::Charset::ISO8859_1; // Reading samples if(loadFlags & loadSampleData) { for(SAMPLEINDEX smp = 1; smp <= 15; smp++) { // Looped samples in (Ultimate) Soundtracker seem to ignore all sample data before the actual loop start. // This avoids the clicks in the first sample of pretend.mod by Karsten Obarski. file.Skip(Samples[smp].nLoopStart); Samples[smp].nLength -= Samples[smp].nLoopStart; Samples[smp].nLoopEnd -= Samples[smp].nLoopStart; Samples[smp].nLoopStart = 0; MODSampleHeader::GetSampleFormat().ReadSample(Samples[smp], file); } } return true; } CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderICE(MemoryFileReader file, const uint64 *pfilesize) { if(!file.CanRead(1464 + 4)) { return ProbeWantMoreData; } file.Seek(1464); char magic[4]; file.ReadArray(magic); if(!IsMagic(magic, "MTN\0") && !IsMagic(magic, "IT10")) { return ProbeFailure; } file.Seek(20); uint32 invalidBytes = 0; for(SAMPLEINDEX smp = 1; smp <= 31; smp++) { MODSampleHeader sampleHeader; if(!file.ReadStruct(sampleHeader)) { return ProbeWantMoreData; } invalidBytes += sampleHeader.GetInvalidByteScore(); } if(invalidBytes > MODSampleHeader::INVALID_BYTE_THRESHOLD) { return ProbeFailure; } const auto [numOrders, numTracks] = file.ReadArray(); if(numOrders > 128) { return ProbeFailure; } uint8 tracks[128 * 4]; file.ReadArray(tracks); for(auto track : tracks) { if(track > numTracks) { return ProbeFailure; } } MPT_UNREFERENCED_PARAMETER(pfilesize); return ProbeSuccess; } // SoundTracker 2.6 / Ice Tracker variation of the MOD format // The only real difference to other SoundTracker formats is the way patterns are stored: // Every pattern consists of four independent, re-usable tracks. bool CSoundFile::ReadICE(FileReader &file, ModLoadingFlags loadFlags) { char magic[4]; if(!file.Seek(1464) || !file.ReadArray(magic)) { return false; } InitializeGlobals(MOD_TYPE_MOD); m_playBehaviour.reset(kMODOneShotLoops); m_playBehaviour.set(kMODIgnorePanning); m_playBehaviour.set(kMODSampleSwap); // untested if(IsMagic(magic, "MTN\0")) { m_modFormat.formatName = U_("MnemoTroN SoundTracker"); m_modFormat.type = U_("st26"); m_modFormat.madeWithTracker = U_("SoundTracker 2.6"); m_modFormat.charset = mpt::Charset::ISO8859_1; } else if(IsMagic(magic, "IT10")) { m_modFormat.formatName = U_("Ice Tracker"); m_modFormat.type = U_("ice"); m_modFormat.madeWithTracker = U_("Ice Tracker 1.0 / 1.1"); m_modFormat.charset = mpt::Charset::ISO8859_1; } else { return false; } // Reading song title file.Seek(0); file.ReadString(m_songName, 20); // Load Samples m_nSamples = 31; uint32 invalidBytes = 0; for(SAMPLEINDEX smp = 1; smp <= 31; smp++) { MODSampleHeader sampleHeader; invalidBytes += ReadSample(file, sampleHeader, Samples[smp], m_szNames[smp], true); } if(invalidBytes > MODSampleHeader::INVALID_BYTE_THRESHOLD) { return false; } const auto [numOrders, numTracks] = file.ReadArray(); if(numOrders > 128) { return false; } uint8 tracks[128 * 4]; file.ReadArray(tracks); for(auto track : tracks) { if(track > numTracks) { return false; } } if(loadFlags == onlyVerifyHeader) { return true; } // Now we can be pretty sure that this is a valid MOD file. Set up default song settings. m_nChannels = 4; m_nInstruments = 0; m_nDefaultSpeed = 6; m_nDefaultTempo.Set(125); m_nMinPeriod = 14 * 4; m_nMaxPeriod = 3424 * 4; m_nSamplePreAmp = 64; m_SongFlags.set(SONG_PT_MODE | SONG_IMPORTED); // Setup channel pan positions and volume SetupMODPanning(); // Reading patterns Order().resize(numOrders); uint8 speed[2] = {0, 0}, speedPos = 0; Patterns.ResizeArray(numOrders); for(PATTERNINDEX pat = 0; pat < numOrders; pat++) { Order()[pat] = pat; if(!Patterns.Insert(pat, 64)) continue; for(CHANNELINDEX chn = 0; chn < 4; chn++) { file.Seek(1468 + tracks[pat * 4 + chn] * 64u * 4u); ModCommand *m = Patterns[pat].GetpModCommand(0, chn); for(ROWINDEX row = 0; row < 64; row++, m += 4) { ReadMODPatternEntry(file, *m); if((m->command || m->param) && !(m->command == 0x0E && m->param >= 0x10) // Exx only sets filter && !(m->command >= 0x05 && m->command <= 0x09)) // These don't exist in ST2.6 { ConvertModCommand(*m); } else { m->command = CMD_NONE; } } } // Handle speed command with both nibbles set - this enables auto-swing (alternates between the two nibbles) auto m = Patterns[pat].begin(); for(ROWINDEX row = 0; row < 64; row++) { for(CHANNELINDEX chn = 0; chn < 4; chn++, m++) { if(m->command == CMD_SPEED || m->command == CMD_TEMPO) { m->command = CMD_SPEED; speedPos = 0; if(m->param & 0xF0) { if((m->param >> 4) != (m->param & 0x0F) && (m->param & 0x0F) != 0) { // Both nibbles set speed[0] = m->param >> 4; speed[1] = m->param & 0x0F; speedPos = 1; } m->param >>= 4; } } } if(speedPos) { Patterns[pat].WriteEffect(EffectWriter(CMD_SPEED, speed[speedPos - 1]).Row(row)); speedPos++; if(speedPos == 3) speedPos = 1; } } } // Reading samples if(loadFlags & loadSampleData) { file.Seek(1468 + numTracks * 64u * 4u); for(SAMPLEINDEX smp = 1; smp <= 31; smp++) if(Samples[smp].nLength) { SampleIO( SampleIO::_8bit, SampleIO::mono, SampleIO::littleEndian, SampleIO::signedPCM) .ReadSample(Samples[smp], file); } } return true; } struct PT36Header { char magicFORM[4]; // "FORM" uint32be size; char magicMODL[4]; // "MODL" }; MPT_BINARY_STRUCT(PT36Header, 12) static bool ValidateHeader(const PT36Header &fileHeader) { if(std::memcmp(fileHeader.magicFORM, "FORM", 4)) { return false; } if(std::memcmp(fileHeader.magicMODL, "MODL", 4)) { return false; } return true; } CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderPT36(MemoryFileReader file, const uint64 *pfilesize) { PT36Header fileHeader; if(!file.ReadStruct(fileHeader)) { return ProbeWantMoreData; } if(!ValidateHeader(fileHeader)) { return ProbeFailure; } MPT_UNREFERENCED_PARAMETER(pfilesize); return ProbeSuccess; } // ProTracker 3.6 version of the MOD format // Basically just a normal ProTracker mod with different magic, wrapped in an IFF file. // The "PTDT" chunk is passed to the normal MOD loader. bool CSoundFile::ReadPT36(FileReader &file, ModLoadingFlags loadFlags) { file.Rewind(); PT36Header fileHeader; if(!file.ReadStruct(fileHeader)) { return false; } if(!ValidateHeader(fileHeader)) { return false; } bool ok = false, infoOk = false; FileReader commentChunk; mpt::ustring version; PT36InfoChunk info; MemsetZero(info); // Go through IFF chunks... PT36IffChunk iffHead; if(!file.ReadStruct(iffHead)) { return false; } // First chunk includes "MODL" magic in size iffHead.chunksize -= 4; do { // All chunk sizes include chunk header iffHead.chunksize -= 8; if(loadFlags == onlyVerifyHeader && iffHead.signature == PT36IffChunk::idPTDT) { return true; } FileReader chunk = file.ReadChunk(iffHead.chunksize); if(!chunk.IsValid()) { break; } switch(iffHead.signature) { case PT36IffChunk::idVERS: chunk.Skip(4); if(chunk.ReadMagic("PT") && iffHead.chunksize > 6) { chunk.ReadString(version, mpt::Charset::ISO8859_1, iffHead.chunksize - 6); } break; case PT36IffChunk::idINFO: infoOk = chunk.ReadStruct(info); break; case PT36IffChunk::idCMNT: commentChunk = chunk; break; case PT36IffChunk::idPTDT: ok = ReadMOD(chunk, loadFlags); break; } } while(file.ReadStruct(iffHead)); if(version.empty()) { version = U_("3.6"); } // both an info chunk and a module are required if(ok && infoOk) { bool vblank = (info.flags & 0x100) == 0; m_playBehaviour.set(kMODVBlankTiming, vblank); if(info.volume != 0) m_nSamplePreAmp = std::min(uint16(64), static_cast(info.volume)); if(info.tempo != 0 && !vblank) m_nDefaultTempo.Set(info.tempo); if(info.name[0]) m_songName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, info.name); if(mpt::is_in_range(info.dateMonth, 1, 12) && mpt::is_in_range(info.dateDay, 1, 31) && mpt::is_in_range(info.dateHour, 0, 23) && mpt::is_in_range(info.dateMinute, 0, 59) && mpt::is_in_range(info.dateSecond, 0, 59)) { FileHistory mptHistory; mptHistory.loadDate.tm_year = info.dateYear; mptHistory.loadDate.tm_mon = info.dateMonth - 1; mptHistory.loadDate.tm_mday = info.dateDay; mptHistory.loadDate.tm_hour = info.dateHour; mptHistory.loadDate.tm_min = info.dateMinute; mptHistory.loadDate.tm_sec = info.dateSecond; m_FileHistory.push_back(mptHistory); } } if(ok) { if(commentChunk.IsValid()) { std::string author; commentChunk.ReadString(author, 32); if(author != "UNNAMED AUTHOR") m_songArtist = mpt::ToUnicode(mpt::Charset::ISO8859_1, author); if(!commentChunk.NoBytesLeft()) { m_songMessage.ReadFixedLineLength(commentChunk, commentChunk.BytesLeft(), 40, 0); } } m_modFormat.madeWithTracker = U_("ProTracker ") + version; } m_SongFlags.set(SONG_PT_MODE); m_playBehaviour.set(kMODIgnorePanning); m_playBehaviour.set(kMODOneShotLoops); m_playBehaviour.reset(kMODSampleSwap); return ok; } #ifndef MODPLUG_NO_FILESAVE bool CSoundFile::SaveMod(std::ostream &f) const { if(m_nChannels == 0) { return false; } // Write song title { char name[20]; mpt::String::WriteBuf(mpt::String::maybeNullTerminated, name) = m_songName; mpt::IO::Write(f, name); } std::vector sampleLength(32, 0); std::vector sampleSource(32, 0); if(GetNumInstruments()) { INSTRUMENTINDEX lastIns = std::min(INSTRUMENTINDEX(31), GetNumInstruments()); for(INSTRUMENTINDEX ins = 1; ins <= lastIns; ins++) if (Instruments[ins]) { // Find some valid sample associated with this instrument. for(auto smp : Instruments[ins]->Keyboard) { if(smp > 0 && smp <= GetNumSamples()) { sampleSource[ins] = smp; break; } } } } else { for(SAMPLEINDEX i = 1; i <= 31; i++) { sampleSource[i] = i; } } // Write sample headers for(SAMPLEINDEX smp = 1; smp <= 31; smp++) { MODSampleHeader sampleHeader; mpt::String::WriteBuf(mpt::String::maybeNullTerminated, sampleHeader.name) = m_szNames[sampleSource[smp]]; sampleLength[smp] = sampleHeader.ConvertToMOD(sampleSource[smp] <= GetNumSamples() ? GetSample(sampleSource[smp]) : ModSample(MOD_TYPE_MOD)); mpt::IO::Write(f, sampleHeader); } // Write order list MODFileHeader fileHeader; MemsetZero(fileHeader); PATTERNINDEX writePatterns = 0; uint8 writtenOrders = 0; for(ORDERINDEX ord = 0; ord < Order().GetLength() && writtenOrders < 128; ord++) { // Ignore +++ and --- patterns in order list, as well as high patterns (MOD officially only supports up to 128 patterns) if(ord == Order().GetRestartPos()) { fileHeader.restartPos = writtenOrders; } if(Order()[ord] < 128) { fileHeader.orderList[writtenOrders++] = static_cast(Order()[ord]); if(writePatterns <= Order()[ord]) { writePatterns = Order()[ord] + 1; } } } fileHeader.numOrders = writtenOrders; mpt::IO::Write(f, fileHeader); // Write magic bytes char modMagic[4]; CHANNELINDEX writeChannels = std::min(CHANNELINDEX(99), GetNumChannels()); if(writeChannels == 4) { // ProTracker may not load files with more than 64 patterns correctly if we do not specify the M!K! magic. if(writePatterns <= 64) memcpy(modMagic, "M.K.", 4); else memcpy(modMagic, "M!K!", 4); } else if(writeChannels < 10) { memcpy(modMagic, "0CHN", 4); modMagic[0] += static_cast(writeChannels); } else { memcpy(modMagic, "00CH", 4); modMagic[0] += static_cast(writeChannels / 10u); modMagic[1] += static_cast(writeChannels % 10u); } mpt::IO::Write(f, modMagic); // Write patterns bool invalidInstruments = false; std::vector events; for(PATTERNINDEX pat = 0; pat < writePatterns; pat++) { if(!Patterns.IsValidPat(pat)) { // Invent empty pattern events.assign(writeChannels * 64 * 4, 0); mpt::IO::Write(f, events); continue; } for(ROWINDEX row = 0; row < 64; row++) { if(row >= Patterns[pat].GetNumRows()) { // Invent empty row events.assign(writeChannels * 4, 0); mpt::IO::Write(f, events); continue; } PatternRow rowBase = Patterns[pat].GetRow(row); events.resize(writeChannels * 4); size_t eventByte = 0; for(CHANNELINDEX chn = 0; chn < writeChannels; chn++, eventByte += 4) { const ModCommand &m = rowBase[chn]; uint8 command = m.command, param = m.param; ModSaveCommand(command, param, false, true); if(m.volcmd == VOLCMD_VOLUME && !command && !param) { // Maybe we can save some volume commands... command = 0x0C; param = std::min(m.vol, uint8(64)); } uint16 period = 0; // Convert note to period if(m.note >= 24 + NOTE_MIN && m.note < std::size(ProTrackerPeriodTable) + 24 + NOTE_MIN) { period = ProTrackerPeriodTable[m.note - 24 - NOTE_MIN]; } const uint8 instr = (m.instr > 31) ? 0 : m.instr; if(m.instr > 31) invalidInstruments = true; events[eventByte + 0] = ((period >> 8) & 0x0F) | (instr & 0x10); events[eventByte + 1] = period & 0xFF; events[eventByte + 2] = ((instr & 0x0F) << 4) | (command & 0x0F); events[eventByte + 3] = param; } mpt::IO::WriteRaw(f, mpt::as_span(events)); } } if(invalidInstruments) { AddToLog(LogWarning, U_("Warning: This track references sample slots higher than 31. Such samples cannot be saved in the MOD format, and thus the notes will not sound correct. Use the Cleanup tool to rearrange and remove unused samples.")); } //Check for unsaved patterns for(PATTERNINDEX pat = writePatterns; pat < Patterns.Size(); pat++) { if(Patterns.IsValidPat(pat)) { AddToLog(LogWarning, U_("Warning: This track contains at least one pattern after the highest pattern number referred to in the sequence. Such patterns are not saved in the MOD format.")); break; } } // Writing samples for(SAMPLEINDEX smp = 1; smp <= 31; smp++) { if(sampleLength[smp] == 0) { continue; } const ModSample &sample = Samples[sampleSource[smp]]; const mpt::IO::Offset sampleStart = mpt::IO::TellWrite(f); const size_t writtenBytes = MODSampleHeader::GetSampleFormat().WriteSample(f, sample, sampleLength[smp]); const int8 silence = 0; // Write padding byte if the sample size is odd. if((writtenBytes % 2u) != 0) { mpt::IO::Write(f, silence); } if(!sample.uFlags[CHN_LOOP] && writtenBytes >= 2) { // First two bytes of oneshot samples have to be 0 due to PT's one-shot loop const mpt::IO::Offset sampleEnd = mpt::IO::TellWrite(f); mpt::IO::SeekAbsolute(f, sampleStart); mpt::IO::Write(f, silence); mpt::IO::Write(f, silence); mpt::IO::SeekAbsolute(f, sampleEnd); } } return true; } #endif // MODPLUG_NO_FILESAVE OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Load_mt2.cpp0000644000175000017500000010022014175523206020240 00000000000000/* * Load_mt2.cpp * ------------ * Purpose: MT2 (MadTracker 2) module loader * Notes : A couple of things are not handled properly or not at all, such as internal effects and automation envelopes * Authors: Olivier Lapicque * OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Loaders.h" #ifdef MPT_EXTERNAL_SAMPLES // For loading external samples #include "../common/mptPathString.h" #endif // MPT_EXTERNAL_SAMPLES #ifdef MPT_WITH_VST #include "../mptrack/Vstplug.h" #endif // MPT_WITH_VST OPENMPT_NAMESPACE_BEGIN struct MT2FileHeader { enum MT2HeaderFlags { packedPatterns = 0x01, automation = 0x02, drumsAutomation = 0x08, masterAutomation = 0x10, }; char signature[4]; // "MT20" uint32le userID; uint16le version; char trackerName[32]; // "MadTracker 2.0" char songName[64]; uint16le numOrders; uint16le restartPos; uint16le numPatterns; uint16le numChannels; uint16le samplesPerTick; uint8le ticksPerLine; uint8le linesPerBeat; uint32le flags; // See HeaderFlags uint16le numInstruments; uint16le numSamples; }; MPT_BINARY_STRUCT(MT2FileHeader, 126) struct MT2DrumsData { uint16le numDrumPatterns; uint16le DrumSamples[8]; uint8le DrumPatternOrder[256]; }; MPT_BINARY_STRUCT(MT2DrumsData, 274) struct MT2TrackSettings { uint16le volume; uint8le trackfx; // Built-in effect type is used uint8le output; uint16le fxID; uint16le trackEffectParam[64][8]; }; MPT_BINARY_STRUCT(MT2TrackSettings, 1030) struct MT2Command { uint8 note; // 0=nothing, 97=note off uint8 instr; uint8 vol; uint8 pan; uint8 fxcmd; uint8 fxparam1; uint8 fxparam2; }; MPT_BINARY_STRUCT(MT2Command, 7) struct MT2EnvPoint { uint16le x; uint16le y; }; MPT_BINARY_STRUCT(MT2EnvPoint, 4) struct MT2Instrument { enum EnvTypes { VolumeEnv = 1, PanningEnv = 2, PitchEnv = 4, FilterEnv = 8, }; uint16le numSamples; uint8le groupMap[96]; uint8le vibtype, vibsweep, vibdepth, vibrate; uint16le fadeout; uint16le nna; }; MPT_BINARY_STRUCT(MT2Instrument, 106) struct MT2IEnvelope { uint8le flags; uint8le numPoints; uint8le sustainPos; uint8le loopStart; uint8le loopEnd; uint8le reserved[3]; MT2EnvPoint points[16]; }; MPT_BINARY_STRUCT(MT2IEnvelope, 72) // Note: The order of these fields differs a bit in MTIOModule_MT2.cpp - maybe just typos, I'm not sure. // This struct follows the save format of MadTracker 2.6.1. struct MT2InstrSynth { uint8le synthID; uint8le effectID; // 0 = Lowpass filter, 1 = Highpass filter uint16le cutoff; // 100...11000 Hz uint8le resonance; // 0...128 uint8le attack; // 0...128 uint8le decay; // 0...128 uint8le midiChannel; // 0...15 int8le device; // VST slot (positive) or MIDI device (negative) int8le unknown1; // Missing in MTIOModule_MT2.cpp uint8le volume; // 0...255 int8le finetune; // -96...96 int8le transpose; // -48...48 uint8le unknown2; // Seems to be equal to instrument number. uint8le unknown3; uint8le midiProgram; uint8le reserved[16]; }; MPT_BINARY_STRUCT(MT2InstrSynth, 32) struct MT2Sample { uint32le length; uint32le frequency; uint8le depth; uint8le channels; uint8le flags; uint8le loopType; uint32le loopStart; uint32le loopEnd; uint16le volume; int8le panning; int8le note; int16le spb; }; MPT_BINARY_STRUCT(MT2Sample, 26) struct MT2Group { uint8le sample; uint8le vol; // 0...128 int8le pitch; // -128...127 uint8le reserved[5]; }; MPT_BINARY_STRUCT(MT2Group, 8) struct MT2VST { char dll[64]; char programName[28]; uint32le fxID; uint32le fxVersion; uint32le programNr; uint8le useChunks; uint8le track; int8le pan; // Not imported - could use pan mix mode for D/W ratio, but this is not implemented for instrument plugins! char reserved[17]; uint32le n; }; MPT_BINARY_STRUCT(MT2VST, 128) static bool ConvertMT2Command(CSoundFile *that, ModCommand &m, MT2Command &p) { bool hasLegacyTempo = false; // Note m.note = NOTE_NONE; if(p.note) m.note = (p.note > 96) ? NOTE_KEYOFF : (p.note + NOTE_MIN + 11); // Instrument m.instr = p.instr; // Volume Column if(p.vol >= 0x10 && p.vol <= 0x90) { m.volcmd = VOLCMD_VOLUME; m.vol = (p.vol - 0x10) / 2; } 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); } // Effects if(p.fxcmd || p.fxparam1 || p.fxparam2) { switch(p.fxcmd) { case 0x00: // FastTracker effect m.command = p.fxparam2; m.param = p.fxparam1; CSoundFile::ConvertModCommand(m); #ifdef MODPLUG_TRACKER m.Convert(MOD_TYPE_XM, MOD_TYPE_IT, *that); #else MPT_UNREFERENCED_PARAMETER(that); #endif // MODPLUG_TRACKER if(p.fxparam2 == 0x0F) hasLegacyTempo = true; break; case 0x01: // Portamento up (on every tick) m.command = CMD_PORTAMENTOUP; m.param = mpt::saturate_cast((p.fxparam2 << 4) | (p.fxparam1 >> 4)); break; case 0x02: // Portamento down (on every tick) m.command = CMD_PORTAMENTODOWN; m.param = mpt::saturate_cast((p.fxparam2 << 4) | (p.fxparam1 >> 4)); break; case 0x03: // Tone Portamento (on every tick) m.command = CMD_TONEPORTAMENTO; m.param = mpt::saturate_cast((p.fxparam2 << 4) | (p.fxparam1 >> 4)); break; case 0x04: // Vibrato m.command = CMD_VIBRATO; m.param = (p.fxparam2 & 0xF0) | (p.fxparam1 >> 4); break; case 0x08: // Panning + Polarity (we can only import panning for now) if(p.fxparam1) { m.command = CMD_PANNING8; m.param = p.fxparam1; } else if(p.fxparam2 == 1 || p.fxparam2 == 2) { // Invert left or right channel m.command = CMD_S3MCMDEX; m.param = 0x91; } break; case 0x0C: // Set volume (0x80 = 100%) m.command = CMD_VOLUME; m.param = p.fxparam2 / 2; break; case 0x0F: // Set tempo, LPB and ticks (we can only import tempo for now) if(p.fxparam2 != 0) { m.command = CMD_TEMPO; m.param = p.fxparam2; } else { m.command = CMD_SPEED; m.param = (p.fxparam1 & 0x0F); } break; case 0x10: // Impulse Tracker effect m.command = p.fxparam2; m.param = p.fxparam1; CSoundFile::S3MConvert(m, true); if(m.command == CMD_TEMPO || m.command == CMD_SPEED) hasLegacyTempo = true; break; case 0x1D: // Gapper (like IT Tremor with old FX, i.e. 1D 00 XY = ontime X + 1 ticks, offtime Y + 1 ticks) m.command = CMD_TREMOR; m.param = p.fxparam1; break; case 0x20: // Cutoff + Resonance (we can only import cutoff for now) m.command = CMD_MIDI; m.param = p.fxparam2 >> 1; break; case 0x22: // Cutoff + Resonance + Attack + Decay (we can only import cutoff for now) m.command = CMD_MIDI; m.param = (p.fxparam2 & 0xF0) >> 1; break; case 0x24: // Reverse m.command = CMD_S3MCMDEX; m.param = 0x9F; break; case 0x80: // Track volume m.command = CMD_CHANNELVOLUME; m.param = p.fxparam2 / 4u; break; case 0x9D: // Offset + delay m.volcmd = VOLCMD_OFFSET; m.vol = p.fxparam2 >> 3; m.command = CMD_S3MCMDEX; m.param = 0xD0 | std::min(p.fxparam1, uint8(0x0F)); break; case 0xCC: // MIDI CC //m.command = CMD_MIDI; break; // TODO: More MT2 Effects } } if(p.pan) { if(m.command == CMD_NONE) { m.command = CMD_PANNING8; m.param = p.pan; } else if(m.volcmd == VOLCMD_NONE) { m.volcmd = VOLCMD_PANNING; m.vol = p.pan / 4; } } return hasLegacyTempo; } // This doesn't really do anything but skipping the envelope chunk at the moment. static void ReadMT2Automation(uint16 version, FileReader &file) { uint32 flags; uint32 trkfxid; if(version >= 0x203) { flags = file.ReadUint32LE(); trkfxid = file.ReadUint32LE(); } else { flags = file.ReadUint16LE(); trkfxid = file.ReadUint16LE(); } MPT_UNREFERENCED_PARAMETER(trkfxid); while(flags != 0) { if(flags & 1) { file.Skip(4 + sizeof(MT2EnvPoint) * 64); } flags >>= 1; } } static bool ValidateHeader(const MT2FileHeader &fileHeader) { if(std::memcmp(fileHeader.signature, "MT20", 4) || fileHeader.version < 0x200 || fileHeader.version >= 0x300 || fileHeader.numChannels < 1 || fileHeader.numChannels > 64 || fileHeader.numOrders > 256 || fileHeader.numInstruments >= MAX_INSTRUMENTS || fileHeader.numSamples >= MAX_SAMPLES ) { return false; } return true; } static uint64 GetHeaderMinimumAdditionalSize(const MT2FileHeader &fileHeader) { MPT_UNREFERENCED_PARAMETER(fileHeader); return 256; } CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderMT2(MemoryFileReader file, const uint64 *pfilesize) { MT2FileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return ProbeWantMoreData; } if(!ValidateHeader(fileHeader)) { return ProbeFailure; } return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); } bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags) { file.Rewind(); MT2FileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return false; } if(!ValidateHeader(fileHeader)) { return false; } if(!file.CanRead(mpt::saturate_cast(GetHeaderMinimumAdditionalSize(fileHeader)))) { return false; } if(loadFlags == onlyVerifyHeader) { return true; } InitializeGlobals(MOD_TYPE_MT2); InitializeChannels(); m_modFormat.formatName = MPT_UFORMAT("MadTracker {}.{}")(fileHeader.version >> 8, mpt::ufmt::hex0<2>(fileHeader.version & 0xFF)); m_modFormat.type = U_("mt2"); m_modFormat.madeWithTracker = mpt::ToUnicode(mpt::Charset::Windows1252, mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileHeader.trackerName)); m_modFormat.charset = mpt::Charset::Windows1252; m_songName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileHeader.songName); m_nChannels = fileHeader.numChannels; m_nDefaultSpeed = Clamp(fileHeader.ticksPerLine, 1, 31); m_nDefaultTempo.Set(125); m_SongFlags = SONG_LINEARSLIDES | SONG_ITCOMPATGXX | SONG_EXFILTERRANGE; m_nInstruments = fileHeader.numInstruments; m_nSamples = fileHeader.numSamples; m_nDefaultRowsPerBeat = Clamp(fileHeader.linesPerBeat, 1, 32); m_nDefaultRowsPerMeasure = m_nDefaultRowsPerBeat * 4; m_nVSTiVolume = 48; m_nSamplePreAmp = 48 * 2; // Double pre-amp because we will halve the volume of all non-drum instruments, because the volume of drum samples can exceed that of normal samples uint8 orders[256]; file.ReadArray(orders); ReadOrderFromArray(Order(), orders, fileHeader.numOrders); Order().SetRestartPos(fileHeader.restartPos); // This value is supposed to be the size of the drums data, but in old MT2.0 files it's 8 bytes too small. // MadTracker itself unconditionally reads 274 bytes here if the value is != 0, so we do the same. const bool hasDrumChannels = file.ReadUint16LE() != 0; FileReader drumData = file.ReadChunk(hasDrumChannels ? sizeof(MT2DrumsData) : 0); FileReader extraData = file.ReadChunk(file.ReadUint32LE()); const CHANNELINDEX channelsWithoutDrums = m_nChannels; static_assert(MAX_BASECHANNELS >= 64 + 8); if(hasDrumChannels) { m_nChannels += 8; } bool hasLegacyTempo = false; // Read patterns if(loadFlags & loadPatternData) Patterns.ResizeArray(fileHeader.numPatterns); for(PATTERNINDEX pat = 0; pat < fileHeader.numPatterns; pat++) { ROWINDEX numRows = file.ReadUint16LE(); FileReader chunk = file.ReadChunk((file.ReadUint32LE() + 1) & ~1); LimitMax(numRows, MAX_PATTERN_ROWS); if(!numRows || !(loadFlags & loadPatternData) || !Patterns.Insert(pat, numRows)) { continue; } if(fileHeader.flags & MT2FileHeader::packedPatterns) { ROWINDEX row = 0; CHANNELINDEX chn = 0; while(chunk.CanRead(1)) { MT2Command cmd; uint8 infobyte = chunk.ReadUint8(); uint8 repeatCount = 0; if(infobyte == 0xFF) { repeatCount = chunk.ReadUint8(); infobyte = chunk.ReadUint8(); } if(infobyte & 0x7F) { ModCommand *m = Patterns[pat].GetpModCommand(row, chn); MemsetZero(cmd); if(infobyte & 0x01) cmd.note = chunk.ReadUint8(); if(infobyte & 0x02) cmd.instr = chunk.ReadUint8(); if(infobyte & 0x04) cmd.vol = chunk.ReadUint8(); if(infobyte & 0x08) cmd.pan = chunk.ReadUint8(); if(infobyte & 0x10) cmd.fxcmd = chunk.ReadUint8(); if(infobyte & 0x20) cmd.fxparam1 = chunk.ReadUint8(); if(infobyte & 0x40) cmd.fxparam2 = chunk.ReadUint8(); hasLegacyTempo |= ConvertMT2Command(this, *m, cmd); const ModCommand &orig = *m; const ROWINDEX fillRows = std::min((uint32)repeatCount, (uint32)numRows - (row + 1)); for(ROWINDEX r = 0; r < fillRows; r++) { m += GetNumChannels(); // cppcheck false-positive // cppcheck-suppress selfAssignment *m = orig; } } row += repeatCount + 1; while(row >= numRows) { row -= numRows; chn++; } if(chn >= channelsWithoutDrums) break; } } else { for(ROWINDEX row = 0; row < numRows; row++) { auto rowData = Patterns[pat].GetRow(row); for(CHANNELINDEX chn = 0; chn < channelsWithoutDrums; chn++) { MT2Command cmd; chunk.ReadStruct(cmd); hasLegacyTempo |= ConvertMT2Command(this, rowData[chn], cmd); } } } } if(fileHeader.samplesPerTick > 1 && fileHeader.samplesPerTick < 5000) { if(hasLegacyTempo) { m_nDefaultTempo.SetRaw(Util::muldivr(110250, TEMPO::fractFact, fileHeader.samplesPerTick)); m_nTempoMode = TempoMode::Classic; } else { m_nDefaultTempo = TEMPO(44100.0 * 60.0 / (m_nDefaultSpeed * m_nDefaultRowsPerBeat * fileHeader.samplesPerTick)); m_nTempoMode = TempoMode::Modern; } } // Read extra data uint32 numVST = 0; std::vector trackRouting(GetNumChannels(), 0); while(extraData.CanRead(8)) { uint32 id = extraData.ReadUint32LE(); FileReader chunk = extraData.ReadChunk(extraData.ReadUint32LE()); switch(id) { case MagicLE("BPM+"): if(!hasLegacyTempo) { m_nTempoMode = TempoMode::Modern; double d = chunk.ReadDoubleLE(); if(d > 0.00000001) { m_nDefaultTempo = TEMPO(44100.0 * 60.0 / (m_nDefaultSpeed * m_nDefaultRowsPerBeat * d)); } } break; case MagicLE("TFXM"): break; case MagicLE("TRKO"): break; case MagicLE("TRKS"): m_nSamplePreAmp = chunk.ReadUint16LE() / 256u; // 131072 is 0dB... I think (that's how MTIOModule_MT2.cpp reads) // Dirty workaround for modules that use track automation for a fade-in at the song start (e.g. Rock.mt2) if(!m_nSamplePreAmp) m_nSamplePreAmp = 48; m_nVSTiVolume = m_nSamplePreAmp / 2u; for(CHANNELINDEX c = 0; c < GetNumChannels(); c++) { MT2TrackSettings trackSettings; if(chunk.ReadStruct(trackSettings)) { ChnSettings[c].nVolume = static_cast(trackSettings.volume >> 10); // 32768 is 0dB trackRouting[c] = trackSettings.output; } } break; case MagicLE("TRKL"): for(CHANNELINDEX i = 0; i < m_nChannels && chunk.CanRead(1); i++) { std::string name; chunk.ReadNullString(name); ChnSettings[i].szName = mpt::String::ReadBuf(mpt::String::spacePadded, name.c_str(), name.length()); } break; case MagicLE("PATN"): for(PATTERNINDEX i = 0; i < fileHeader.numPatterns && chunk.CanRead(1) && Patterns.IsValidIndex(i); i++) { std::string name; chunk.ReadNullString(name); Patterns[i].SetName(name); } break; case MagicLE("MSG\0"): chunk.Skip(1); // Show message on startup m_songMessage.Read(chunk, chunk.BytesLeft(), SongMessage::leCRLF); break; case MagicLE("PICT"): break; case MagicLE("SUM\0"): { uint8 summaryMask[6]; chunk.ReadArray(summaryMask); std::string artist; chunk.ReadNullString(artist); if(artist != "Unregistered") { m_songArtist = mpt::ToUnicode(mpt::Charset::Windows1252, artist); } } break; case MagicLE("TMAP"): break; case MagicLE("MIDI"): break; case MagicLE("TREQ"): break; case MagicLE("VST2"): numVST = chunk.ReadUint32LE(); #ifdef MPT_WITH_VST if(!(loadFlags & loadPluginData)) { break; } for(uint32 i = 0; i < std::min(numVST, uint32(MAX_MIXPLUGINS)); i++) { MT2VST vstHeader; if(chunk.ReadStruct(vstHeader)) { if(fileHeader.version >= 0x0250) chunk.Skip(16 * 4); // Parameter automation map for 16 parameters SNDMIXPLUGIN &mixPlug = m_MixPlugins[i]; mixPlug.Destroy(); std::string libraryName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, vstHeader.dll); mixPlug.Info.szName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, vstHeader.programName); if(libraryName.length() > 4 && libraryName[libraryName.length() - 4] == '.') { // Remove ".dll" from library name libraryName.resize(libraryName.length() - 4 ); } mixPlug.Info.szLibraryName = libraryName; mixPlug.Info.dwPluginId1 = Vst::kEffectMagic; mixPlug.Info.dwPluginId2 = vstHeader.fxID; if(vstHeader.track >= m_nChannels) { mixPlug.SetMasterEffect(true); } else { if(!ChnSettings[vstHeader.track].nMixPlugin) { ChnSettings[vstHeader.track].nMixPlugin = static_cast(i + 1); } else { // Channel already has plugin assignment - chain the plugins PLUGINDEX outPlug = ChnSettings[vstHeader.track].nMixPlugin - 1; while(true) { if(m_MixPlugins[outPlug].GetOutputPlugin() == PLUGINDEX_INVALID) { m_MixPlugins[outPlug].SetOutputPlugin(static_cast(i)); break; } outPlug = m_MixPlugins[outPlug].GetOutputPlugin(); } } } // Read plugin settings uint32 dataSize; if(vstHeader.useChunks) { // MT2 only ever calls effGetChunk for programs, and OpenMPT uses the defaultProgram value to determine // whether it should use effSetChunk for programs or banks... mixPlug.defaultProgram = -1; LimitMax(vstHeader.n, std::numeric_limits::max() - 4); dataSize = vstHeader.n + 4; } else { mixPlug.defaultProgram = vstHeader.programNr; LimitMax(vstHeader.n, (std::numeric_limits::max() / 4u) - 1); dataSize = vstHeader.n * 4 + 4; } mixPlug.pluginData.resize(dataSize); if(vstHeader.useChunks) { std::memcpy(mixPlug.pluginData.data(), "fEvN", 4); // 'NvEf' plugin data type chunk.ReadRaw(mpt::span(mixPlug.pluginData.data() + 4, vstHeader.n)); } else { auto memFile = std::make_pair(mpt::as_span(mixPlug.pluginData), mpt::IO::Offset(0)); mpt::IO::WriteIntLE(memFile, 0); // Plugin data type for(uint32 param = 0; param < vstHeader.n; param++) { mpt::IO::Write(memFile, IEEE754binary32LE{chunk.ReadFloatLE()}); } } } else { break; } } #endif // MPT_WITH_VST break; } } #ifndef NO_PLUGINS // Now that we have both the track settings and plugins, establish the track routing by applying the same plugins to the source track as to the target track: for(CHANNELINDEX c = 0; c < GetNumChannels(); c++) { int8 outTrack = trackRouting[c]; if(outTrack > c && outTrack < GetNumChannels() && ChnSettings[outTrack].nMixPlugin != 0) { if(ChnSettings[c].nMixPlugin == 0) { ChnSettings[c].nMixPlugin = ChnSettings[outTrack].nMixPlugin; } else { PLUGINDEX outPlug = ChnSettings[c].nMixPlugin - 1; for(;;) { if(m_MixPlugins[outPlug].GetOutputPlugin() == PLUGINDEX_INVALID) { m_MixPlugins[outPlug].SetOutputPlugin(ChnSettings[outTrack].nMixPlugin - 1); break; } outPlug = m_MixPlugins[outPlug].GetOutputPlugin(); } } } } #endif // NO_PLUGINS // Read drum channels INSTRUMENTINDEX drumMap[8] = { 0 }; uint16 drumSample[8] = { 0 }; if(hasDrumChannels) { MT2DrumsData drumHeader; drumData.ReadStruct(drumHeader); // Allocate some instruments to handle the drum samples for(INSTRUMENTINDEX i = 0; i < 8; i++) { drumMap[i] = GetNextFreeInstrument(m_nInstruments + 1); drumSample[i] = drumHeader.DrumSamples[i]; if(drumMap[i] != INSTRUMENTINDEX_INVALID) { ModInstrument *mptIns = AllocateInstrument(drumMap[i], drumHeader.DrumSamples[i] + 1); if(mptIns != nullptr) { mptIns->name = MPT_AFORMAT("Drum #{}")(i+1); } } else { drumMap[i] = 0; } } // Get all the drum pattern chunks std::vector patternChunks(drumHeader.numDrumPatterns); for(uint32 pat = 0; pat < drumHeader.numDrumPatterns; pat++) { uint16 numRows = file.ReadUint16LE(); patternChunks[pat] = file.ReadChunk(numRows * 32); } std::vector patMapping(fileHeader.numPatterns, PATTERNINDEX_INVALID); for(uint32 ord = 0; ord < fileHeader.numOrders; ord++) { if(drumHeader.DrumPatternOrder[ord] >= drumHeader.numDrumPatterns || Order()[ord] >= fileHeader.numPatterns) continue; // Figure out where to write this drum pattern PATTERNINDEX writePat = Order()[ord]; if(patMapping[writePat] == PATTERNINDEX_INVALID) { patMapping[writePat] = drumHeader.DrumPatternOrder[ord]; } else if(patMapping[writePat] != drumHeader.DrumPatternOrder[ord]) { // Damn, this pattern has previously used a different drum pattern. Duplicate it... PATTERNINDEX newPat = Patterns.Duplicate(writePat); if(newPat != PATTERNINDEX_INVALID) { writePat = newPat; Order()[ord] = writePat; } } if(!Patterns.IsValidPat(writePat)) continue; FileReader &chunk = patternChunks[drumHeader.DrumPatternOrder[ord]]; chunk.Rewind(); const ROWINDEX numRows = static_cast(chunk.GetLength() / 32u); for(ROWINDEX row = 0; row < Patterns[writePat].GetNumRows(); row++) { ModCommand *m = Patterns[writePat].GetpModCommand(row, m_nChannels - 8); for(CHANNELINDEX chn = 0; chn < 8; chn++, m++) { *m = ModCommand::Empty(); if(row >= numRows) continue; uint8 drums[4]; chunk.ReadArray(drums); if(drums[0] & 0x80) { m->note = NOTE_MIDDLEC; m->instr = static_cast(drumMap[chn]); uint8 delay = drums[0] & 0x1F; if(delay) { LimitMax(delay, uint8(0x0F)); m->command = CMD_S3MCMDEX; m->param = 0xD0 | delay; } m->volcmd = VOLCMD_VOLUME; // Volume is 0...255, but 128 is equivalent to v64 - we compensate this by halving the global volume of all non-drum instruments m->vol = static_cast((static_cast(drums[1]) + 3) / 4u); } } } } } // Read automation envelopes if(fileHeader.flags & MT2FileHeader::automation) { const uint32 numEnvelopes = ((fileHeader.flags & MT2FileHeader::drumsAutomation) ? m_nChannels : channelsWithoutDrums) + ((fileHeader.version >= 0x0250) ? numVST : 0) + ((fileHeader.flags & MT2FileHeader::masterAutomation) ? 1 : 0); for(uint32 pat = 0; pat < fileHeader.numPatterns; pat++) { for(uint32 env = 0; env < numEnvelopes && file.CanRead(4); env++) { // TODO ReadMT2Automation(fileHeader.version, file); } } } // Read instruments std::vector instrChunks(255); for(INSTRUMENTINDEX i = 0; i < 255; i++) { char instrName[32]; file.ReadArray(instrName); uint32 dataLength = file.ReadUint32LE(); if(dataLength == 32) dataLength += 108 + sizeof(MT2IEnvelope) * 4; if(fileHeader.version > 0x0201 && dataLength) dataLength += 4; FileReader instrChunk = instrChunks[i] = file.ReadChunk(dataLength); ModInstrument *mptIns = nullptr; if(i < fileHeader.numInstruments) { // Default sample assignment if there is no data chunk? Fixes e.g. instrument 33 in Destiny - Dream Alone.mt2 mptIns = AllocateInstrument(i + 1, i + 1); } if(mptIns == nullptr) continue; mptIns->name = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, instrName); if(!dataLength) continue; MT2Instrument insHeader; instrChunk.ReadStruct(insHeader); uint16 flags = 0; if(fileHeader.version >= 0x0201) flags = instrChunk.ReadUint16LE(); uint32 envMask = MT2Instrument::VolumeEnv | MT2Instrument::PanningEnv; if(fileHeader.version >= 0x0202) envMask = instrChunk.ReadUint32LE(); mptIns->nFadeOut = insHeader.fadeout; const NewNoteAction NNA[4] = { NewNoteAction::NoteCut, NewNoteAction::Continue, NewNoteAction::NoteOff, NewNoteAction::NoteFade }; const DuplicateCheckType DCT[4] = { DuplicateCheckType::None, DuplicateCheckType::Note, DuplicateCheckType::Sample, DuplicateCheckType::Instrument }; const DuplicateNoteAction DNA[4] = { DuplicateNoteAction::NoteCut, DuplicateNoteAction::NoteFade /* actually continue, but IT doesn't have that */, DuplicateNoteAction::NoteOff, DuplicateNoteAction::NoteFade }; mptIns->nNNA = NNA[insHeader.nna & 3]; mptIns->nDCT = DCT[(insHeader.nna >> 8) & 3]; mptIns->nDNA = DNA[(insHeader.nna >> 12) & 3]; // Load envelopes for(uint32 env = 0; env < 4; env++) { if(envMask & 1) { MT2IEnvelope mt2Env; instrChunk.ReadStruct(mt2Env); const EnvelopeType envType[4] = { ENV_VOLUME, ENV_PANNING, ENV_PITCH, ENV_PITCH }; InstrumentEnvelope &mptEnv = mptIns->GetEnvelope(envType[env]); mptEnv.dwFlags.set(ENV_FILTER, (env == 3) && (mt2Env.flags & 1) != 0); mptEnv.dwFlags.set(ENV_ENABLED, (mt2Env.flags & 1) != 0); mptEnv.dwFlags.set(ENV_SUSTAIN, (mt2Env.flags & 2) != 0); mptEnv.dwFlags.set(ENV_LOOP, (mt2Env.flags & 4) != 0); mptEnv.resize(std::min(mt2Env.numPoints.get(), uint8(16))); mptEnv.nSustainStart = mptEnv.nSustainEnd = mt2Env.sustainPos; mptEnv.nLoopStart = mt2Env.loopStart; mptEnv.nLoopEnd = mt2Env.loopEnd; for(uint32 p = 0; p < mptEnv.size(); p++) { mptEnv[p].tick = mt2Env.points[p].x; mptEnv[p].value = static_cast(Clamp(mt2Env.points[p].y, 0, 64)); } } envMask >>= 1; } if(!mptIns->VolEnv.dwFlags[ENV_ENABLED] && mptIns->nNNA != NewNoteAction::NoteFade) { mptIns->nFadeOut = int16_max; } mptIns->SetCutoff(0x7F, true); mptIns->SetResonance(0, true); if(flags) { MT2InstrSynth synthData; instrChunk.ReadStruct(synthData); if(flags & 2) { mptIns->SetCutoff(FrequencyToCutOff(synthData.cutoff), true); mptIns->SetResonance(synthData.resonance, true); } mptIns->filterMode = synthData.effectID == 1 ? FilterMode::HighPass : FilterMode::LowPass; if(flags & 4) { // VSTi / MIDI synth enabled mptIns->nMidiChannel = synthData.midiChannel + 1; mptIns->nMixPlug = static_cast(synthData.device + 1); if(synthData.device < 0) { // TODO: This is a MIDI device - maybe use MIDI I/O plugin to emulate those? mptIns->nMidiProgram = synthData.midiProgram + 1; // MT2 only seems to use this for MIDI devices, not VSTis! } if(synthData.transpose) { for(uint32 n = 0; n < std::size(mptIns->NoteMap); n++) { int note = NOTE_MIN + n + synthData.transpose; Limit(note, NOTE_MIN, NOTE_MAX); mptIns->NoteMap[n] = static_cast(note); } } // Instruments with plugin assignments never play samples at the same time! mptIns->AssignSample(0); } } } // Read sample headers std::bitset<256> sampleNoInterpolation; std::bitset<256> sampleSynchronized; for(SAMPLEINDEX i = 0; i < 256; i++) { char sampleName[32]; file.ReadArray(sampleName); uint32 dataLength = file.ReadUint32LE(); FileReader sampleChunk = file.ReadChunk(dataLength); if(i < fileHeader.numSamples) { m_szNames[i + 1] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, sampleName); } if(dataLength && i < fileHeader.numSamples) { ModSample &mptSmp = Samples[i + 1]; mptSmp.Initialize(MOD_TYPE_IT); mptSmp.SetDefaultCuePoints(); MT2Sample sampleHeader; sampleChunk.ReadStruct(sampleHeader); mptSmp.nLength = sampleHeader.length; mptSmp.nC5Speed = sampleHeader.frequency; if(sampleHeader.depth > 1) { mptSmp.uFlags.set(CHN_16BIT); mptSmp.nLength /= 2u; } if(sampleHeader.channels > 1) { mptSmp.uFlags.set(CHN_STEREO); mptSmp.nLength /= 2u; } if(sampleHeader.loopType == 1) mptSmp.uFlags.set(CHN_LOOP); else if(sampleHeader.loopType == 2) mptSmp.uFlags.set(CHN_LOOP | CHN_PINGPONGLOOP); mptSmp.nLoopStart = sampleHeader.loopStart; mptSmp.nLoopEnd = sampleHeader.loopEnd; mptSmp.nVolume = sampleHeader.volume >> 7; if(sampleHeader.panning == -128) mptSmp.uFlags.set(CHN_SURROUND); else mptSmp.nPan = sampleHeader.panning + 128; mptSmp.uFlags.set(CHN_PANNING); mptSmp.RelativeTone = sampleHeader.note; if(sampleHeader.flags & 2) { // Sample is synchronized to beat // The synchronization part is not supported in OpenMPT, but synchronized samples also always play at the same pitch as C-5, which we CAN do! sampleSynchronized[i] = true; //mptSmp.nC5Speed = Util::muldiv(mptSmp.nC5Speed, sampleHeader.spb, 22050); } if(sampleHeader.flags & 5) { // External sample mptSmp.uFlags.set(SMP_KEEPONDISK); } if(sampleHeader.flags & 8) { sampleNoInterpolation[i] = true; for(INSTRUMENTINDEX drum = 0; drum < 8; drum++) { if(drumSample[drum] == i && Instruments[drumMap[drum]] != nullptr) { Instruments[drumMap[drum]]->resampling = SRCMODE_NEAREST; } } } } } // Read sample groups for(INSTRUMENTINDEX ins = 0; ins < fileHeader.numInstruments; ins++) { if(instrChunks[ins].GetLength()) { FileReader &chunk = instrChunks[ins]; MT2Instrument insHeader; chunk.Rewind(); chunk.ReadStruct(insHeader); std::vector groups; file.ReadVector(groups, insHeader.numSamples); ModInstrument *mptIns = Instruments[ins + 1]; // Instruments with plugin assignments never play samples at the same time! if(mptIns == nullptr || mptIns->nMixPlug != 0) continue; mptIns->nGlobalVol = 32; // Compensate for extended dynamic range of drum instruments mptIns->AssignSample(0); for(uint32 note = 0; note < 96; note++) { if(insHeader.groupMap[note] < insHeader.numSamples) { const MT2Group &group = groups[insHeader.groupMap[note]]; SAMPLEINDEX sample = group.sample + 1; mptIns->Keyboard[note + 11 + NOTE_MIN] = sample; if(sample > 0 && sample <= m_nSamples) { ModSample &mptSmp = Samples[sample]; mptSmp.nVibType = static_cast(insHeader.vibtype & 3); // In fact, MT2 only implements sine vibrato mptSmp.nVibSweep = insHeader.vibsweep; mptSmp.nVibDepth = insHeader.vibdepth; mptSmp.nVibRate = insHeader.vibrate; mptSmp.nGlobalVol = uint16(group.vol) * 2; mptSmp.nFineTune = group.pitch; if(sampleNoInterpolation[sample - 1]) { mptIns->resampling = SRCMODE_NEAREST; } if(sampleSynchronized[sample - 1]) { mptIns->NoteMap[note + 11 + NOTE_MIN] = NOTE_MIDDLEC; } } // TODO: volume, finetune for duplicated samples } } } } if(!(loadFlags & loadSampleData)) return true; // Read sample data for(SAMPLEINDEX i = 0; i < m_nSamples; i++) { ModSample &mptSmp = Samples[i + 1]; mptSmp.Transpose(-(mptSmp.RelativeTone - 49 - (mptSmp.nFineTune / 128.0)) / 12.0); mptSmp.nFineTune = 0; mptSmp.RelativeTone = 0; if(!mptSmp.uFlags[SMP_KEEPONDISK]) { SampleIO( mptSmp.uFlags[CHN_16BIT] ? SampleIO::_16bit : SampleIO::_8bit, mptSmp.uFlags[CHN_STEREO] ? SampleIO::stereoSplit : SampleIO::mono, SampleIO::littleEndian, SampleIO::MT2) .ReadSample(mptSmp, file); } else { // External sample const uint32 filenameSize = file.ReadUint32LE(); file.Skip(12); // Reserved std::string filename; file.ReadString(filename, filenameSize); mptSmp.filename = filename; #if defined(MPT_EXTERNAL_SAMPLES) if(filename.length() >= 2 && filename[0] != '\\' // Relative path on same drive && filename[1] != ':') // Absolute path { // Relative path in same folder or sub folder filename = ".\\" + filename; } SetSamplePath(i + 1, mpt::PathString::FromLocaleSilent(filename)); #elif !defined(LIBOPENMPT_BUILD_TEST) AddToLog(LogWarning, MPT_UFORMAT("Loading external sample {} ('{}') failed: External samples are not supported.")(i + 1, mpt::ToUnicode(GetCharsetFile(), filename))); #endif // MPT_EXTERNAL_SAMPLES } } return true; } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Load_mtm.cpp0000644000175000017500000001553614147535507020360 00000000000000/* * Load_mtm.cpp * ------------ * Purpose: MTM (MultiTracker) module loader * Notes : (currently none) * Authors: Olivier Lapicque * OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Loaders.h" OPENMPT_NAMESPACE_BEGIN // File Header struct MTMFileHeader { char id[3]; // MTM file marker uint8le version; // Tracker version char songName[20]; // ASCIIZ songname uint16le numTracks; // Number of tracks saved uint8le lastPattern; // Last pattern number saved uint8le lastOrder; // Last order number to play (songlength-1) uint16le commentSize; // Length of comment field uint8le numSamples; // Number of samples saved uint8le attribute; // Attribute byte (unused) uint8le beatsPerTrack; // Numbers of rows in every pattern (MultiTracker itself does not seem to support values != 64) uint8le numChannels; // Number of channels used uint8le panPos[32]; // Channel pan positions }; MPT_BINARY_STRUCT(MTMFileHeader, 66) // Sample Header struct MTMSampleHeader { char samplename[22]; uint32le length; uint32le loopStart; uint32le loopEnd; int8le finetune; uint8le volume; uint8le attribute; // Convert an MTM sample header to OpenMPT's internal sample header. void ConvertToMPT(ModSample &mptSmp) const { mptSmp.Initialize(); mptSmp.nVolume = std::min(uint16(volume * 4), uint16(256)); if(length > 2) { mptSmp.nLength = length; mptSmp.nLoopStart = loopStart; mptSmp.nLoopEnd = std::max(loopEnd.get(), uint32(1)) - 1; LimitMax(mptSmp.nLoopEnd, mptSmp.nLength); if(mptSmp.nLoopStart + 4 >= mptSmp.nLoopEnd) mptSmp.nLoopStart = mptSmp.nLoopEnd = 0; if(mptSmp.nLoopEnd) mptSmp.uFlags.set(CHN_LOOP); mptSmp.nFineTune = finetune; // Uses MOD units but allows the full int8 range rather than just -8...+7 so we keep the value as-is and convert it during playback mptSmp.nC5Speed = ModSample::TransposeToFrequency(0, finetune * 16); if(attribute & 0x01) { mptSmp.uFlags.set(CHN_16BIT); mptSmp.nLength /= 2; mptSmp.nLoopStart /= 2; mptSmp.nLoopEnd /= 2; } } } }; MPT_BINARY_STRUCT(MTMSampleHeader, 37) static bool ValidateHeader(const MTMFileHeader &fileHeader) { if(std::memcmp(fileHeader.id, "MTM", 3) || fileHeader.version >= 0x20 || fileHeader.lastOrder > 127 || fileHeader.beatsPerTrack > 64 || fileHeader.numChannels > 32 || fileHeader.numChannels == 0 ) { return false; } return true; } static uint64 GetHeaderMinimumAdditionalSize(const MTMFileHeader &fileHeader) { return sizeof(MTMSampleHeader) * fileHeader.numSamples + 128 + 192 * fileHeader.numTracks + 64 * (fileHeader.lastPattern + 1) + fileHeader.commentSize; } CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderMTM(MemoryFileReader file, const uint64 *pfilesize) { MTMFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return ProbeWantMoreData; } if(!ValidateHeader(fileHeader)) { return ProbeFailure; } return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); } bool CSoundFile::ReadMTM(FileReader &file, ModLoadingFlags loadFlags) { file.Rewind(); MTMFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return false; } if(!ValidateHeader(fileHeader)) { return false; } if(!file.CanRead(mpt::saturate_cast(GetHeaderMinimumAdditionalSize(fileHeader)))) { return false; } if(loadFlags == onlyVerifyHeader) { return true; } InitializeGlobals(MOD_TYPE_MTM); m_songName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileHeader.songName); m_nSamples = fileHeader.numSamples; m_nChannels = fileHeader.numChannels; m_modFormat.formatName = U_("MultiTracker"); m_modFormat.type = U_("mtm"); m_modFormat.madeWithTracker = MPT_UFORMAT("MultiTracker {}.{}")(fileHeader.version >> 4, fileHeader.version & 0x0F); m_modFormat.charset = mpt::Charset::CP437; // Reading instruments for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++) { MTMSampleHeader sampleHeader; file.ReadStruct(sampleHeader); sampleHeader.ConvertToMPT(Samples[smp]); m_szNames[smp] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, sampleHeader.samplename); } // Setting Channel Pan Position for(CHANNELINDEX chn = 0; chn < GetNumChannels(); chn++) { ChnSettings[chn].Reset(); ChnSettings[chn].nPan = ((fileHeader.panPos[chn] & 0x0F) << 4) + 8; } // Reading pattern order uint8 orders[128]; file.ReadArray(orders); ReadOrderFromArray(Order(), orders, fileHeader.lastOrder + 1, 0xFF, 0xFE); // Reading Patterns const ROWINDEX rowsPerPat = fileHeader.beatsPerTrack ? fileHeader.beatsPerTrack : 64; FileReader tracks = file.ReadChunk(192 * fileHeader.numTracks); if(loadFlags & loadPatternData) Patterns.ResizeArray(fileHeader.lastPattern + 1); for(PATTERNINDEX pat = 0; pat <= fileHeader.lastPattern; pat++) { if(!(loadFlags & loadPatternData) || !Patterns.Insert(pat, rowsPerPat)) { file.Skip(64); continue; } for(CHANNELINDEX chn = 0; chn < 32; chn++) { uint16 track = file.ReadUint16LE(); if(track == 0 || track > fileHeader.numTracks || chn >= GetNumChannels()) { continue; } tracks.Seek(192 * (track - 1)); ModCommand *m = Patterns[pat].GetpModCommand(0, chn); for(ROWINDEX row = 0; row < rowsPerPat; row++, m += GetNumChannels()) { const auto [noteInstr, instrCmd, par] = tracks.ReadArray(); if(noteInstr & 0xFC) m->note = (noteInstr >> 2) + 36 + NOTE_MIN; m->instr = ((noteInstr & 0x03) << 4) | (instrCmd >> 4); uint8 cmd = instrCmd & 0x0F; uint8 param = par; if(cmd == 0x0A) { if(param & 0xF0) param &= 0xF0; else param &= 0x0F; } else if(cmd == 0x08) { // No 8xx panning in MultiTracker, only E8x cmd = param = 0; } else if(cmd == 0x0E) { // MultiTracker does not support these commands switch(param & 0xF0) { case 0x00: case 0x30: case 0x40: case 0x60: case 0x70: case 0xF0: cmd = param = 0; break; } } if(cmd != 0 || param != 0) { m->command = cmd; m->param = param; ConvertModCommand(*m); #ifdef MODPLUG_TRACKER m->Convert(MOD_TYPE_MTM, MOD_TYPE_S3M, *this); #endif } } } } if(fileHeader.commentSize != 0) { // Read message with a fixed line length of 40 characters // (actually the last character is always null, so make that 39 + 1 padding byte) m_songMessage.ReadFixedLineLength(file, fileHeader.commentSize, 39, 1); } // Reading Samples if(loadFlags & loadSampleData) { for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++) { SampleIO( Samples[smp].uFlags[CHN_16BIT] ? SampleIO::_16bit : SampleIO::_8bit, SampleIO::mono, SampleIO::littleEndian, SampleIO::unsignedPCM) .ReadSample(Samples[smp], file); } } m_nMinPeriod = 64; m_nMaxPeriod = 32767; return true; } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Load_mus_km.cpp0000644000175000017500000002441214157732077021051 00000000000000/* * Load_mus_km.cpp * --------------- * Purpose: Karl Morton Music Format module loader * Notes : This is probably not the official name of this format. * Karl Morton's engine has been used in Psycho Pinball and Micro Machines 2 and also Back To Baghdad * but the latter game only uses its sound effect format, not the music format. * So there are only two known games using this music format, and no official tools or documentation are available. * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Loaders.h" OPENMPT_NAMESPACE_BEGIN struct KMChunkHeader { // 32-Bit chunk identifiers enum ChunkIdentifiers { idSONG = MagicLE("SONG"), idSMPL = MagicLE("SMPL"), }; uint32le id; // See ChunkIdentifiers uint32le length; // Chunk size including header size_t GetLength() const { return length <= 8 ? 0 : (length - 8); } ChunkIdentifiers GetID() const { return static_cast(id.get()); } }; MPT_BINARY_STRUCT(KMChunkHeader, 8) struct KMSampleHeader { char name[32]; uint32le loopStart; uint32le size; }; MPT_BINARY_STRUCT(KMSampleHeader, 40) struct KMSampleReference { char name[32]; uint8 finetune; uint8 volume; }; MPT_BINARY_STRUCT(KMSampleReference, 34) struct KMSongHeader { char name[32]; KMSampleReference samples[31]; uint16le unknown; // always 0 uint32le numChannels; uint32le restartPos; uint32le musicSize; }; MPT_BINARY_STRUCT(KMSongHeader, 32 + 31 * 34 + 14) struct KMFileHeader { KMChunkHeader chunkHeader; KMSongHeader songHeader; }; MPT_BINARY_STRUCT(KMFileHeader, sizeof(KMChunkHeader) + sizeof(KMSongHeader)) static uint64 GetHeaderMinimumAdditionalSize(const KMFileHeader &fileHeader) { // Require room for at least one more sample chunk header return static_cast(fileHeader.songHeader.musicSize) + sizeof(KMChunkHeader); } // Check if string only contains printable characters and doesn't contain any garbage after the required terminating null static bool IsValidKMString(const char (&str)[32]) { bool nullFound = false; for(char c : str) { if(c > 0x00 && c < 0x20) return false; else if(c == 0x00) nullFound = true; else if(nullFound) return false; } return nullFound; } static bool ValidateHeader(const KMFileHeader &fileHeader) { if(fileHeader.chunkHeader.id != KMChunkHeader::idSONG || fileHeader.chunkHeader.length < sizeof(fileHeader) || fileHeader.chunkHeader.length - sizeof(fileHeader) != fileHeader.songHeader.musicSize || fileHeader.chunkHeader.length > 0x40000 // That's enough space for 256 crammed 64-row patterns ;) || fileHeader.songHeader.unknown != 0 || fileHeader.songHeader.numChannels < 1 || fileHeader.songHeader.numChannels > 4 // Engine rejects anything above 32, channels 5 to 32 are simply ignored || !IsValidKMString(fileHeader.songHeader.name)) { return false; } for(const auto &sample : fileHeader.songHeader.samples) { if(sample.finetune > 15 || sample.volume > 64 || !IsValidKMString(sample.name)) return false; } return true; } CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderMUS_KM(MemoryFileReader file, const uint64 *pfilesize) { KMFileHeader fileHeader; if(!file.Read(fileHeader)) return ProbeWantMoreData; if(!ValidateHeader(fileHeader)) return ProbeFailure; return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); } bool CSoundFile::ReadMUS_KM(FileReader &file, ModLoadingFlags loadFlags) { { file.Rewind(); KMFileHeader fileHeader; if(!file.Read(fileHeader)) return false; if(!ValidateHeader(fileHeader)) return false; if(!file.CanRead(mpt::saturate_cast(GetHeaderMinimumAdditionalSize(fileHeader)))) return false; if(loadFlags == onlyVerifyHeader) return true; } file.Rewind(); const auto chunks = ChunkReader(file).ReadChunks(1); auto songChunks = chunks.GetAllChunks(KMChunkHeader::idSONG); auto sampleChunks = chunks.GetAllChunks(KMChunkHeader::idSMPL); if(songChunks.empty() || sampleChunks.empty()) return false; InitializeGlobals(MOD_TYPE_MOD); InitializeChannels(); m_SongFlags = SONG_AMIGALIMITS | SONG_IMPORTED | SONG_ISAMIGA; // Yes, those were not Amiga games but the format fully conforms to Amiga limits, so allow the Amiga Resampler to be used. m_nChannels = 4; m_nSamples = 0; static constexpr uint16 MUS_SAMPLE_UNUSED = 255; // Sentinel value to check if a sample needs to be duplicated for(auto &chunk : sampleChunks) { if(!CanAddMoreSamples()) break; m_nSamples++; ModSample &mptSample = Samples[m_nSamples]; mptSample.Initialize(MOD_TYPE_MOD); KMSampleHeader sampleHeader; if(!chunk.Read(sampleHeader) || !IsValidKMString(sampleHeader.name)) return false; m_szNames[m_nSamples] = sampleHeader.name; mptSample.nLoopEnd = mptSample.nLength = sampleHeader.size; mptSample.nLoopStart = sampleHeader.loopStart; mptSample.uFlags.set(CHN_LOOP); mptSample.nVolume = MUS_SAMPLE_UNUSED; if(!(loadFlags & loadSampleData)) continue; SampleIO(SampleIO::_8bit, SampleIO::mono, SampleIO::littleEndian, SampleIO::signedPCM) .ReadSample(mptSample, chunk); } bool firstSong = true; for(auto &chunk : songChunks) { if(!firstSong && !Order.AddSequence()) break; firstSong = false; Order().clear(); KMSongHeader songHeader; if(!chunk.Read(songHeader) || songHeader.unknown != 0 || songHeader.numChannels < 1 || songHeader.numChannels > 4) return false; Order().SetName(mpt::ToUnicode(mpt::Charset::CP437, songHeader.name)); FileReader musicData = (loadFlags & loadPatternData) ? chunk.ReadChunk(songHeader.musicSize) : FileReader{}; // Map the samples for this subsong std::array sampleMap{}; for(uint8 smp = 1; smp <= 31; smp++) { const auto &srcSample = songHeader.samples[smp - 1]; const auto srcName = mpt::String::ReadAutoBuf(srcSample.name); if(srcName.empty()) continue; if(srcSample.finetune > 15 || srcSample.volume > 64 || !IsValidKMString(srcSample.name)) return false; const auto finetune = MOD2XMFineTune(srcSample.finetune); const uint16 volume = srcSample.volume * 4u; SAMPLEINDEX copyFrom = 0; for(SAMPLEINDEX srcSmp = 1; srcSmp <= m_nSamples; srcSmp++) { if(srcName != m_szNames[srcSmp]) continue; auto &mptSample = Samples[srcSmp]; sampleMap[smp] = srcSmp; if(mptSample.nVolume == MUS_SAMPLE_UNUSED || (mptSample.nFineTune == finetune && mptSample.nVolume == volume)) { // Sample was not used yet, or it uses the same finetune and volume mptSample.nFineTune = finetune; mptSample.nVolume = volume; copyFrom = 0; break; } else { copyFrom = srcSmp; } } if(copyFrom && CanAddMoreSamples()) { m_nSamples++; sampleMap[smp] = m_nSamples; const auto &smpFrom = Samples[copyFrom]; auto &newSample = Samples[m_nSamples]; newSample.FreeSample(); newSample = smpFrom; newSample.nFineTune = finetune; newSample.nVolume = volume; newSample.CopyWaveform(smpFrom); m_szNames[m_nSamples] = m_szNames[copyFrom]; } } struct ChannelState { ModCommand prevCommand; uint8 repeat = 0; }; std::array chnStates{}; static constexpr ROWINDEX MUS_PATTERN_LENGTH = 64; const CHANNELINDEX numChannels = static_cast(songHeader.numChannels); PATTERNINDEX pat = PATTERNINDEX_INVALID; ROWINDEX row = MUS_PATTERN_LENGTH; ROWINDEX restartRow = 0; uint32 repeatsLeft = 0; while(repeatsLeft || musicData.CanRead(1)) { row++; if(row >= MUS_PATTERN_LENGTH) { pat = Patterns.InsertAny(MUS_PATTERN_LENGTH); if(pat == PATTERNINDEX_INVALID) break; Order().push_back(pat); row = 0; } ModCommand *m = Patterns[pat].GetpModCommand(row, 0); for(CHANNELINDEX chn = 0; chn < numChannels; chn++, m++) { auto &chnState = chnStates[chn]; if(chnState.repeat) { chnState.repeat--; repeatsLeft--; *m = chnState.prevCommand; continue; } if(!musicData.CanRead(1)) continue; if(musicData.GetPosition() == songHeader.restartPos) { Order().SetRestartPos(Order().GetLastIndex()); restartRow = row; } const uint8 note = musicData.ReadUint8(); if(note & 0x80) { chnState.repeat = note & 0x7F; repeatsLeft += chnState.repeat; *m = chnState.prevCommand; continue; } if(note > 0 && note <= 3 * 12) m->note = note + NOTE_MIDDLEC - 13; const auto instr = musicData.ReadUint8(); m->instr = static_cast(sampleMap[instr & 0x1F]); if(instr & 0x80) { m->command = chnState.prevCommand.command; m->param = chnState.prevCommand.param; } else { static constexpr struct { ModCommand::COMMAND command; uint8 mask; } effTrans[] = { {CMD_VOLUME, 0x00}, {CMD_MODCMDEX, 0xA0}, {CMD_MODCMDEX, 0xB0}, {CMD_MODCMDEX, 0x10}, {CMD_MODCMDEX, 0x20}, {CMD_MODCMDEX, 0x50}, {CMD_OFFSET, 0x00}, {CMD_TONEPORTAMENTO, 0x00}, {CMD_TONEPORTAVOL, 0x00}, {CMD_VIBRATO, 0x00}, {CMD_VIBRATOVOL, 0x00}, {CMD_ARPEGGIO, 0x00}, {CMD_PORTAMENTOUP, 0x00}, {CMD_PORTAMENTODOWN, 0x00}, {CMD_VOLUMESLIDE, 0x00}, {CMD_MODCMDEX, 0x90}, {CMD_TONEPORTAMENTO, 0xFF}, {CMD_MODCMDEX, 0xC0}, {CMD_SPEED, 0x00}, {CMD_TREMOLO, 0x00}, }; const auto [command, param] = musicData.ReadArray(); if(command < std::size(effTrans)) { m->command = effTrans[command].command; m->param = param; if(m->command == CMD_SPEED && m->param >= 0x20) m->command = CMD_TEMPO; else if(effTrans[command].mask) m->param = effTrans[command].mask | (m->param & 0x0F); } } chnState.prevCommand = *m; } } if((restartRow != 0 || row < (MUS_PATTERN_LENGTH - 1u)) && pat != PATTERNINDEX_INVALID) { Patterns[pat].WriteEffect(EffectWriter(CMD_PATTERNBREAK, static_cast(restartRow)).Row(row).RetryNextRow()); } } Order.SetSequence(0); m_modFormat.formatName = U_("Karl Morton Music Format"); m_modFormat.type = U_("mus"); m_modFormat.charset = mpt::Charset::CP437; return true; } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Load_okt.cpp0000644000175000017500000002730314126162046020342 00000000000000/* * Load_okt.cpp * ------------ * Purpose: OKT (Oktalyzer) module loader * Notes : (currently none) * Authors: Storlek (Original author - http://schismtracker.org/ - code ported with permission) * Johannes Schultz (OpenMPT Port, tweaks) * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Loaders.h" OPENMPT_NAMESPACE_BEGIN struct OktIffChunk { // IFF chunk names enum ChunkIdentifiers { idCMOD = MagicBE("CMOD"), idSAMP = MagicBE("SAMP"), idSPEE = MagicBE("SPEE"), idSLEN = MagicBE("SLEN"), idPLEN = MagicBE("PLEN"), idPATT = MagicBE("PATT"), idPBOD = MagicBE("PBOD"), idSBOD = MagicBE("SBOD"), }; uint32be signature; // IFF chunk name uint32be chunksize; // chunk size without header }; MPT_BINARY_STRUCT(OktIffChunk, 8) struct OktSample { char name[20]; uint32be length; // length in bytes uint16be loopStart; // *2 for real value uint16be loopLength; // ditto uint16be volume; // default volume uint16be type; // 7-/8-bit sample }; MPT_BINARY_STRUCT(OktSample, 32) // Parse the sample header block static void ReadOKTSamples(FileReader &chunk, CSoundFile &sndFile) { sndFile.m_nSamples = std::min(static_cast(chunk.BytesLeft() / sizeof(OktSample)), static_cast(MAX_SAMPLES - 1)); for(SAMPLEINDEX smp = 1; smp <= sndFile.GetNumSamples(); smp++) { ModSample &mptSmp = sndFile.GetSample(smp); OktSample oktSmp; chunk.ReadStruct(oktSmp); mptSmp.Initialize(); sndFile.m_szNames[smp] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, oktSmp.name); mptSmp.nC5Speed = 8287; mptSmp.nVolume = std::min(oktSmp.volume.get(), uint16(64)) * 4u; mptSmp.nLength = oktSmp.length & ~1; mptSmp.cues[0] = oktSmp.type; // Temporary storage for pattern reader, will be reset later // Parse loops const SmpLength loopStart = oktSmp.loopStart * 2; const SmpLength loopLength = oktSmp.loopLength * 2; if(loopLength > 2 && loopStart + loopLength <= mptSmp.nLength) { mptSmp.uFlags.set(CHN_SUSTAINLOOP); mptSmp.nSustainStart = loopStart; mptSmp.nSustainEnd = loopStart + loopLength; } } } // Parse a pattern block static void ReadOKTPattern(FileReader &chunk, PATTERNINDEX pat, CSoundFile &sndFile, const std::array pairedChn) { if(!chunk.CanRead(2)) { // Invent empty pattern sndFile.Patterns.Insert(pat, 64); return; } ROWINDEX rows = Clamp(static_cast(chunk.ReadUint16BE()), ROWINDEX(1), MAX_PATTERN_ROWS); if(!sndFile.Patterns.Insert(pat, rows)) { return; } const CHANNELINDEX chns = sndFile.GetNumChannels(); for(ROWINDEX row = 0; row < rows; row++) { auto rowCmd = sndFile.Patterns[pat].GetRow(row); for(CHANNELINDEX chn = 0; chn < chns; chn++) { ModCommand &m = rowCmd[chn]; const auto [note, instr, effect, param] = chunk.ReadArray(); if(note > 0 && note <= 36) { m.note = note + (NOTE_MIDDLEC - 13); m.instr = instr + 1; if(m.instr > 0 && m.instr <= sndFile.GetNumSamples()) { const auto &sample = sndFile.GetSample(m.instr); // Default volume only works on raw Paula channels if(pairedChn[chn] && sample.nVolume < 256) { m.volcmd = VOLCMD_VOLUME; m.vol = 64; } // If channel and sample type don't match, stop this channel (add 100 to the instrument number to make it understandable what happened during import) if((sample.cues[0] == 1 && pairedChn[chn] != 0) || (sample.cues[0] == 0 && pairedChn[chn] == 0)) { m.instr += 100; } } } switch(effect) { case 0: // Nothing break; case 1: // 1 Portamento Down (Period) if(param) { m.command = CMD_PORTAMENTOUP; m.param = param; } break; case 2: // 2 Portamento Up (Period) if(param) { m.command = CMD_PORTAMENTODOWN; m.param = param; } break; #if 0 /* these aren't like regular arpeggio: "down" means to *subtract* the offset from the note. For now I'm going to leave these unimplemented. */ case 10: // A Arpeggio 1 (down, orig, up) case 11: // B Arpeggio 2 (orig, up, orig, down) if(param) { m.command = CMD_ARPEGGIO; m.param = param; } break; #endif // This one is close enough to "standard" arpeggio -- I think! case 12: // C Arpeggio 3 (up, up, orig) if(param) { m.command = CMD_ARPEGGIO; m.param = param; } break; case 13: // D Slide Down (Notes) if(param) { m.command = CMD_NOTESLIDEDOWN; m.param = 0x10 | std::min(uint8(0x0F), param); } break; case 30: // U Slide Up (Notes) if(param) { m.command = CMD_NOTESLIDEUP; m.param = 0x10 | std::min(uint8(0x0F), param); } break; // Fine Slides are only implemented for libopenmpt. For OpenMPT, // sliding every 5 (non-note) ticks kind of works (at least at // speed 6), but implementing separate (format-agnostic) fine slide commands would of course be better. case 21: // L Slide Down Once (Notes) if(param) { m.command = CMD_NOTESLIDEDOWN; m.param = 0x50 | std::min(uint8(0x0F), param); } break; case 17: // H Slide Up Once (Notes) if(param) { m.command = CMD_NOTESLIDEUP; m.param = 0x50 | std::min(uint8(0x0F), param); } break; case 15: // F Set Filter <>00:ON m.command = CMD_MODCMDEX; m.param = !!param; break; case 25: // P Pos Jump m.command = CMD_POSITIONJUMP; m.param = param; break; case 27: // R Release sample (apparently not listed in the help!) m.Clear(); m.note = NOTE_KEYOFF; break; case 28: // S Speed if(param < 0x20) { m.command = CMD_SPEED; m.param = param; } break; case 31: // V Volume // Volume on mixed channels is permanent, on hardware channels it behaves like in regular MODs if(param & 0x0F) { m.command = pairedChn[chn] ? CMD_CHANNELVOLSLIDE : CMD_VOLUMESLIDE; m.param = param & 0x0F; } switch(param >> 4) { case 4: // Normal slide down if(param != 0x40) break; // 0x40 is set volume -- fall through [[fallthrough]]; case 0: case 1: case 2: case 3: if(pairedChn[chn]) { m.command = CMD_CHANNELVOLUME; m.param = param; } else { m.volcmd = VOLCMD_VOLUME; m.vol = param; m.command = CMD_NONE; } break; case 5: // Normal slide up m.param <<= 4; break; case 6: // Fine slide down m.param = 0xF0 | std::min(static_cast(m.param), uint8(0x0E)); break; case 7: // Fine slide up m.param = (std::min(static_cast(m.param), uint8(0x0E)) << 4) | 0x0F; break; default: // Junk. m.command = CMD_NONE; break; } // Volume is shared between two mixed channels, second channel has priority if(m.command == CMD_CHANNELVOLUME || m.command == CMD_CHANNELVOLSLIDE) { ModCommand &other = rowCmd[chn + pairedChn[chn]]; // Try to preserve effect if there already was one if(other.ConvertVolEffect(other.command, other.param, true)) { other.volcmd = other.command; other.vol = other.param; } other.command = m.command; other.param = m.param; } break; #if 0 case 24: // O Old Volume (???) m.command = CMD_VOLUMESLIDE; m.param = 0; break; #endif default: m.command = CMD_NONE; break; } } } } CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderOKT(MemoryFileReader file, const uint64 *pfilesize) { if(!file.CanRead(8)) { return ProbeWantMoreData; } if(!file.ReadMagic("OKTASONG")) { return ProbeFailure; } OktIffChunk iffHead; if(!file.ReadStruct(iffHead)) { return ProbeWantMoreData; } if(iffHead.chunksize == 0) { return ProbeFailure; } if((iffHead.signature & 0x80808080u) != 0) // ASCII? { return ProbeFailure; } MPT_UNREFERENCED_PARAMETER(pfilesize); return ProbeSuccess; } bool CSoundFile::ReadOKT(FileReader &file, ModLoadingFlags loadFlags) { file.Rewind(); if(!file.ReadMagic("OKTASONG")) { return false; } // prepare some arrays to store offsets etc. std::vector patternChunks; std::vector sampleChunks; std::array pairedChn{{}}; ORDERINDEX numOrders = 0; InitializeGlobals(MOD_TYPE_OKT); m_modFormat.formatName = U_("Oktalyzer"); m_modFormat.type = U_("okt"); m_modFormat.charset = mpt::Charset::ISO8859_1; // Go through IFF chunks... while(file.CanRead(sizeof(OktIffChunk))) { OktIffChunk iffHead; if(!file.ReadStruct(iffHead)) { break; } FileReader chunk = file.ReadChunk(iffHead.chunksize); if(!chunk.IsValid()) { break; } switch(iffHead.signature) { case OktIffChunk::idCMOD: // Channel setup table if(m_nChannels == 0 && chunk.GetLength() >= 8) { const auto chnTable = chunk.ReadArray(); for(CHANNELINDEX chn = 0; chn < 4; chn++) { if(chnTable[chn]) { pairedChn[m_nChannels] = 1; pairedChn[m_nChannels + 1] = -1; ChnSettings[m_nChannels].Reset(); ChnSettings[m_nChannels++].nPan = (((chn & 3) == 1) || ((chn & 3) == 2)) ? 0xC0 : 0x40; } ChnSettings[m_nChannels].Reset(); ChnSettings[m_nChannels++].nPan = (((chn & 3) == 1) || ((chn & 3) == 2)) ? 0xC0 : 0x40; } if(loadFlags == onlyVerifyHeader) { return true; } } break; case OktIffChunk::idSAMP: // Convert sample headers if(m_nSamples > 0) { break; } ReadOKTSamples(chunk, *this); break; case OktIffChunk::idSPEE: // Read default speed if(chunk.GetLength() >= 2) { m_nDefaultSpeed = Clamp(chunk.ReadUint16BE(), uint16(1), uint16(255)); } break; case OktIffChunk::idSLEN: // Number of patterns, we don't need this. break; case OktIffChunk::idPLEN: // Read number of valid orders if(chunk.GetLength() >= 2) { numOrders = chunk.ReadUint16BE(); } break; case OktIffChunk::idPATT: // Read the orderlist ReadOrderFromFile(Order(), chunk, chunk.GetLength(), 0xFF, 0xFE); break; case OktIffChunk::idPBOD: // Don't read patterns for now, as the number of channels might be unknown at this point. if(patternChunks.size() < 256) { patternChunks.push_back(chunk); } break; case OktIffChunk::idSBOD: // Sample data - same as with patterns, as we need to know the sample format / length if(sampleChunks.size() < MAX_SAMPLES - 1 && chunk.GetLength() > 0) { sampleChunks.push_back(chunk); } break; default: // Non-ASCII chunk ID? if(iffHead.signature & 0x80808080) return false; break; } } // If there wasn't even a CMOD chunk, we can't really load this. if(m_nChannels == 0) return false; m_nDefaultTempo.Set(125); m_nDefaultGlobalVolume = MAX_GLOBAL_VOLUME; m_nSamplePreAmp = m_nVSTiVolume = 48; m_nMinPeriod = 113 * 4; m_nMaxPeriod = 856 * 4; // Fix orderlist Order().resize(numOrders); // Read patterns if(loadFlags & loadPatternData) { Patterns.ResizeArray(static_cast(patternChunks.size())); for(PATTERNINDEX pat = 0; pat < patternChunks.size(); pat++) { ReadOKTPattern(patternChunks[pat], pat, *this, pairedChn); } } // Read samples size_t fileSmp = 0; for(SAMPLEINDEX smp = 1; smp < m_nSamples; smp++) { if(fileSmp >= sampleChunks.size() || !(loadFlags & loadSampleData)) break; ModSample &mptSample = Samples[smp]; mptSample.SetDefaultCuePoints(); if(mptSample.nLength == 0) continue; // Weird stuff? LimitMax(mptSample.nLength, mpt::saturate_cast(sampleChunks[fileSmp].GetLength())); SampleIO( SampleIO::_8bit, SampleIO::mono, SampleIO::bigEndian, SampleIO::signedPCM) .ReadSample(mptSample, sampleChunks[fileSmp]); fileSmp++; } return true; } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Load_plm.cpp0000644000175000017500000002523414137251202020331 00000000000000/* * Load_plm.cpp * ------------ * Purpose: PLM (Disorder Tracker 2) module loader * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Loaders.h" OPENMPT_NAMESPACE_BEGIN struct PLMFileHeader { char magic[4]; // "PLM\x1A" uint8le headerSize; // Number of bytes in header, including magic bytes uint8le version; // version code of file format (0x10) char songName[48]; uint8le numChannels; uint8le flags; // unused? uint8le maxVol; // Maximum volume for vol slides, normally 0x40 uint8le amplify; // SoundBlaster amplify, 0x40 = no amplify uint8le tempo; uint8le speed; uint8le panPos[32]; // 0...15 uint8le numSamples; uint8le numPatterns; uint16le numOrders; }; MPT_BINARY_STRUCT(PLMFileHeader, 96) struct PLMSampleHeader { enum SampleFlags { smp16Bit = 1, smpPingPong = 2, }; char magic[4]; // "PLS\x1A" uint8le headerSize; // Number of bytes in header, including magic bytes uint8le version; char name[32]; char filename[12]; uint8le panning; // 0...15, 255 = no pan uint8le volume; // 0...64 uint8le flags; // See SampleFlags uint16le sampleRate; char unused[4]; uint32le loopStart; uint32le loopEnd; uint32le length; }; MPT_BINARY_STRUCT(PLMSampleHeader, 71) struct PLMPatternHeader { uint32le size; uint8le numRows; uint8le numChannels; uint8le color; char name[25]; }; MPT_BINARY_STRUCT(PLMPatternHeader, 32) struct PLMOrderItem { uint16le x; // Starting position of pattern uint8le y; // Number of first channel uint8le pattern; }; MPT_BINARY_STRUCT(PLMOrderItem, 4) static bool ValidateHeader(const PLMFileHeader &fileHeader) { if(std::memcmp(fileHeader.magic, "PLM\x1A", 4) || fileHeader.version != 0x10 || fileHeader.numChannels == 0 || fileHeader.numChannels > 32 || fileHeader.headerSize < sizeof(PLMFileHeader) ) { return false; } return true; } static uint64 GetHeaderMinimumAdditionalSize(const PLMFileHeader &fileHeader) { return fileHeader.headerSize - sizeof(PLMFileHeader) + 4 * (fileHeader.numOrders + fileHeader.numPatterns + fileHeader.numSamples); } CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderPLM(MemoryFileReader file, const uint64 *pfilesize) { PLMFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return ProbeWantMoreData; } if(!ValidateHeader(fileHeader)) { return ProbeFailure; } return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); } bool CSoundFile::ReadPLM(FileReader &file, ModLoadingFlags loadFlags) { file.Rewind(); PLMFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return false; } if(!ValidateHeader(fileHeader)) { return false; } if(!file.CanRead(mpt::saturate_cast(GetHeaderMinimumAdditionalSize(fileHeader)))) { return false; } if(loadFlags == onlyVerifyHeader) { return true; } if(!file.Seek(fileHeader.headerSize)) { return false; } InitializeGlobals(MOD_TYPE_PLM); InitializeChannels(); m_SongFlags = SONG_ITOLDEFFECTS; m_playBehaviour.set(kApplyOffsetWithoutNote); m_modFormat.formatName = U_("Disorder Tracker 2"); m_modFormat.type = U_("plm"); m_modFormat.charset = mpt::Charset::CP437; // Some PLMs use ASCIIZ, some space-padding strings...weird. Oh, and the file browser stops at 0 bytes in the name, the main GUI doesn't. m_songName = mpt::String::ReadBuf(mpt::String::spacePadded, fileHeader.songName); m_nChannels = fileHeader.numChannels + 1; // Additional channel for writing pattern breaks m_nSamplePreAmp = fileHeader.amplify; m_nDefaultTempo.Set(fileHeader.tempo); m_nDefaultSpeed = fileHeader.speed; for(CHANNELINDEX chn = 0; chn < fileHeader.numChannels; chn++) { ChnSettings[chn].nPan = fileHeader.panPos[chn] * 0x11; } m_nSamples = fileHeader.numSamples; std::vector order(fileHeader.numOrders); file.ReadVector(order, fileHeader.numOrders); std::vector patternPos, samplePos; file.ReadVector(patternPos, fileHeader.numPatterns); file.ReadVector(samplePos, fileHeader.numSamples); for(SAMPLEINDEX smp = 0; smp < fileHeader.numSamples; smp++) { ModSample &sample = Samples[smp + 1]; sample.Initialize(); PLMSampleHeader sampleHeader; if(samplePos[smp] == 0 || !file.Seek(samplePos[smp]) || !file.ReadStruct(sampleHeader)) continue; m_szNames[smp + 1] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, sampleHeader.name); sample.filename = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, sampleHeader.filename); if(sampleHeader.panning <= 15) { sample.uFlags.set(CHN_PANNING); sample.nPan = sampleHeader.panning * 0x11; } sample.nGlobalVol = std::min(sampleHeader.volume.get(), uint8(64)); sample.nC5Speed = sampleHeader.sampleRate; sample.nLoopStart = sampleHeader.loopStart; sample.nLoopEnd = sampleHeader.loopEnd; sample.nLength = sampleHeader.length; if(sampleHeader.flags & PLMSampleHeader::smp16Bit) { sample.nLoopStart /= 2; sample.nLoopEnd /= 2; sample.nLength /= 2; } if(sample.nLoopEnd > sample.nLoopStart) { sample.uFlags.set(CHN_LOOP); if(sampleHeader.flags & PLMSampleHeader::smpPingPong) sample.uFlags.set(CHN_PINGPONGLOOP); } sample.SanitizeLoops(); if(loadFlags & loadSampleData) { file.Seek(samplePos[smp] + sampleHeader.headerSize); SampleIO( (sampleHeader.flags & PLMSampleHeader::smp16Bit) ? SampleIO::_16bit : SampleIO::_8bit, SampleIO::mono, SampleIO::littleEndian, SampleIO::unsignedPCM) .ReadSample(sample, file); } } if(!(loadFlags & loadPatternData)) { return true; } // PLM is basically one huge continuous pattern, so we split it up into smaller patterns. const ROWINDEX rowsPerPat = 64; uint32 maxPos = 0; static constexpr ModCommand::COMMAND effTrans[] = { CMD_NONE, CMD_PORTAMENTOUP, CMD_PORTAMENTODOWN, CMD_TONEPORTAMENTO, CMD_VOLUMESLIDE, CMD_TREMOLO, CMD_VIBRATO, CMD_S3MCMDEX, // Tremolo Waveform CMD_S3MCMDEX, // Vibrato Waveform CMD_TEMPO, CMD_SPEED, CMD_POSITIONJUMP, // Jump to order CMD_POSITIONJUMP, // Break to end of this order CMD_OFFSET, CMD_S3MCMDEX, // GUS Panning CMD_RETRIG, CMD_S3MCMDEX, // Note Delay CMD_S3MCMDEX, // Note Cut CMD_S3MCMDEX, // Pattern Delay CMD_FINEVIBRATO, CMD_VIBRATOVOL, CMD_TONEPORTAVOL, CMD_OFFSETPERCENTAGE, }; Order().clear(); for(const auto &ord : order) { if(ord.pattern >= fileHeader.numPatterns || ord.y > fileHeader.numChannels || !file.Seek(patternPos[ord.pattern])) continue; PLMPatternHeader patHeader; file.ReadStruct(patHeader); if(!patHeader.numRows) continue; static_assert(ORDERINDEX_MAX >= (std::numeric_limits::max() + 255) / rowsPerPat); ORDERINDEX curOrd = static_cast(ord.x / rowsPerPat); ROWINDEX curRow = static_cast(ord.x % rowsPerPat); const CHANNELINDEX numChannels = std::min(patHeader.numChannels.get(), static_cast(fileHeader.numChannels - ord.y)); const uint32 patternEnd = ord.x + patHeader.numRows; maxPos = std::max(maxPos, patternEnd); ModCommand::NOTE lastNote[32] = { 0 }; for(ROWINDEX r = 0; r < patHeader.numRows; r++, curRow++) { if(curRow >= rowsPerPat) { curRow = 0; curOrd++; } if(curOrd >= Order().size()) { Order().resize(curOrd + 1); Order()[curOrd] = Patterns.InsertAny(rowsPerPat); } PATTERNINDEX pat = Order()[curOrd]; if(!Patterns.IsValidPat(pat)) break; ModCommand *m = Patterns[pat].GetpModCommand(curRow, ord.y); for(CHANNELINDEX c = 0; c < numChannels; c++, m++) { const auto [note, instr, volume, command, param] = file.ReadArray(); if(note > 0 && note < 0x90) lastNote[c] = m->note = (note >> 4) * 12 + (note & 0x0F) + 12 + NOTE_MIN; else m->note = NOTE_NONE; m->instr = instr; m->volcmd = VOLCMD_VOLUME; if(volume != 0xFF) m->vol = volume; else m->volcmd = VOLCMD_NONE; if(command < std::size(effTrans)) { m->command = effTrans[command]; m->param = param; // Fix some commands switch(command) { case 0x07: // Tremolo waveform m->param = 0x40 | (m->param & 0x03); break; case 0x08: // Vibrato waveform m->param = 0x30 | (m->param & 0x03); break; case 0x0B: // Jump to order if(m->param < order.size()) { uint16 target = order[m->param].x; m->param = static_cast(target / rowsPerPat); ModCommand *mBreak = Patterns[pat].GetpModCommand(curRow, m_nChannels - 1); mBreak->command = CMD_PATTERNBREAK; mBreak->param = static_cast(target % rowsPerPat); } break; case 0x0C: // Jump to end of order { m->param = static_cast(patternEnd / rowsPerPat); ModCommand *mBreak = Patterns[pat].GetpModCommand(curRow, m_nChannels - 1); mBreak->command = CMD_PATTERNBREAK; mBreak->param = static_cast(patternEnd % rowsPerPat); } break; case 0x0E: // GUS Panning m->param = 0x80 | (m->param & 0x0F); break; case 0x10: // Delay Note m->param = 0xD0 | std::min(m->param, ModCommand::PARAM(0x0F)); break; case 0x11: // Cut Note m->param = 0xC0 | std::min(m->param, ModCommand::PARAM(0x0F)); break; case 0x12: // Pattern Delay m->param = 0xE0 | std::min(m->param, ModCommand::PARAM(0x0F)); break; case 0x04: // Volume Slide case 0x14: // Vibrato + Volume Slide case 0x15: // Tone Portamento + Volume Slide // If both nibbles of a volume slide are set, act as fine volume slide up if((m->param & 0xF0) && (m->param & 0x0F) && (m->param & 0xF0) != 0xF0) { m->param |= 0x0F; } break; case 0x0D: case 0x16: // Offset without note if(m->note == NOTE_NONE) { m->note = lastNote[c]; } break; } } } if(patHeader.numChannels > numChannels) { file.Skip(5 * (patHeader.numChannels - numChannels)); } } } // Module ends with the last row of the last order item ROWINDEX endPatSize = maxPos % rowsPerPat; ORDERINDEX endOrder = static_cast(maxPos / rowsPerPat); if(endPatSize > 0 && Order().IsValidPat(endOrder)) { Patterns[Order()[endOrder]].Resize(endPatSize, false); } // If there are still any non-existent patterns in our order list, insert some blank patterns. PATTERNINDEX blankPat = PATTERNINDEX_INVALID; for(auto &pat : Order()) { if(pat == Order.GetInvalidPatIndex()) { if(blankPat == PATTERNINDEX_INVALID) { blankPat = Patterns.InsertAny(rowsPerPat); } pat = blankPat; } } return true; } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Load_psm.cpp0000644000175000017500000011727114072320411020340 00000000000000/* * Load_psm.cpp * ------------ * Purpose: PSM16 and new PSM (ProTracker Studio / Epic MegaGames MASI) module loader * Notes : This is partly based on http://www.shikadi.net/moddingwiki/ProTracker_Studio_Module * and partly reverse-engineered. Also thanks to the author of foo_dumb, the source code gave me a few clues. :) * Authors: Johannes Schultz * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Loaders.h" #ifdef LIBOPENMPT_BUILD #define MPT_PSM_USE_REAL_SUBSONGS #endif OPENMPT_NAMESPACE_BEGIN //////////////////////////////////////////////////////////// // // New PSM support starts here. PSM16 structs are below. // // PSM File Header struct PSMFileHeader { char formatID[4]; // "PSM " (new format) uint32le fileSize; // Filesize - 12 char fileInfoID[4]; // "FILE" }; MPT_BINARY_STRUCT(PSMFileHeader, 12) // RIFF-style Chunk struct PSMChunk { // 32-Bit chunk identifiers enum ChunkIdentifiers { idTITL = MagicLE("TITL"), idSDFT = MagicLE("SDFT"), idPBOD = MagicLE("PBOD"), idSONG = MagicLE("SONG"), idDATE = MagicLE("DATE"), idOPLH = MagicLE("OPLH"), idPPAN = MagicLE("PPAN"), idPATT = MagicLE("PATT"), idDSAM = MagicLE("DSAM"), idDSMP = MagicLE("DSMP"), }; uint32le id; uint32le length; size_t GetLength() const { return length; } ChunkIdentifiers GetID() const { return static_cast(id.get()); } }; MPT_BINARY_STRUCT(PSMChunk, 8) // Song Information struct PSMSongHeader { char songType[9]; // Mostly "MAINSONG " (But not in Extreme Pinball!) uint8 compression; // 1 - uncompressed uint8 numChannels; // Number of channels }; MPT_BINARY_STRUCT(PSMSongHeader, 11) // Regular sample header struct PSMSampleHeader { uint8le flags; char fileName[8]; // Filename of the original module (without extension) char sampleID[4]; // Identifier like "INS0" (only last digit of sample ID, i.e. sample 1 and sample 11 are equal) or "I0 " char sampleName[33]; uint8le unknown1[6]; // 00 00 00 00 00 FF uint16le sampleNumber; uint32le sampleLength; uint32le loopStart; uint32le loopEnd; // FF FF FF FF = end of sample uint8le unknown3; uint8le finetune; // unused? always 0 uint8le defaultVolume; uint32le unknown4; uint32le c5Freq; // MASI ignores the high 16 bits char padding[19]; // Convert header data to OpenMPT's internal format void ConvertToMPT(ModSample &mptSmp) const { mptSmp.Initialize(); mptSmp.filename = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileName); mptSmp.nC5Speed = c5Freq; mptSmp.nLength = sampleLength; mptSmp.nLoopStart = loopStart; // Note that we shouldn't add + 1 for MTM conversions here (e.g. the OMF 2097 music), // but I think there is no way to figure out the original format, and in the case of the OMF 2097 soundtrack // it doesn't make a huge audible difference anyway (no chip samples are used). // On the other hand, sample 8 of MUSIC_A.PSM from Extreme Pinball will sound detuned if we don't adjust the loop end here. if(loopEnd) mptSmp.nLoopEnd = loopEnd + 1; mptSmp.nVolume = (defaultVolume + 1) * 2; mptSmp.uFlags.set(CHN_LOOP, (flags & 0x80) != 0); LimitMax(mptSmp.nLoopEnd, mptSmp.nLength); LimitMax(mptSmp.nLoopStart, mptSmp.nLoopEnd); } }; MPT_BINARY_STRUCT(PSMSampleHeader, 96) // Sinaria sample header (and possibly other games) struct PSMSinariaSampleHeader { uint8le flags; char fileName[8]; // Filename of the original module (without extension) char sampleID[8]; // INS0...INS99999 char sampleName[33]; uint8le unknown1[6]; // 00 00 00 00 00 FF uint16le sampleNumber; uint32le sampleLength; uint32le loopStart; uint32le loopEnd; uint16le unknown3; uint8le finetune; // Appears to be unused uint8le defaultVolume; uint32le unknown4; uint16le c5Freq; char padding[16]; // Convert header data to OpenMPT's internal format void ConvertToMPT(ModSample &mptSmp) const { mptSmp.Initialize(); mptSmp.filename = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileName); mptSmp.nC5Speed = c5Freq; mptSmp.nLength = sampleLength; mptSmp.nLoopStart = loopStart; mptSmp.nLoopEnd = loopEnd; mptSmp.nVolume = (defaultVolume + 1) * 2; mptSmp.uFlags.set(CHN_LOOP, (flags & 0x80) != 0); LimitMax(mptSmp.nLoopEnd, mptSmp.nLength); LimitMax(mptSmp.nLoopStart, mptSmp.nLoopEnd); } }; MPT_BINARY_STRUCT(PSMSinariaSampleHeader, 96) struct PSMSubSong // For internal use (pattern conversion) { std::vector channelPanning, channelVolume; std::vector channelSurround; ORDERINDEX startOrder = ORDERINDEX_INVALID, endOrder = ORDERINDEX_INVALID, restartPos = 0; uint8 defaultTempo = 125, defaultSpeed = 6; char songName[10] = {}; PSMSubSong() : channelPanning(MAX_BASECHANNELS, 128) , channelVolume(MAX_BASECHANNELS, 64) , channelSurround(MAX_BASECHANNELS, false) { } }; // Portamento effect conversion (depending on format version) static uint8 ConvertPSMPorta(uint8 param, bool sinariaFormat) { if(sinariaFormat) return param; if(param < 4) return (param | 0xF0); else return (param >> 2); } // Read a Pattern ID (something like "P0 " or "P13 ", or "PATT0 " in Sinaria) static PATTERNINDEX ReadPSMPatternIndex(FileReader &file, bool &sinariaFormat) { char patternID[5]; uint8 offset = 1; file.ReadString(patternID, 4); if(!memcmp(patternID, "PATT", 4)) { file.ReadString(patternID, 4); sinariaFormat = true; offset = 0; } return ConvertStrTo(&patternID[offset]); } static bool ValidateHeader(const PSMFileHeader &fileHeader) { if(!std::memcmp(fileHeader.formatID, "PSM ", 4) && !std::memcmp(fileHeader.fileInfoID, "FILE", 4)) { return true; } #ifdef MPT_PSM_DECRYPT if(!std::memcmp(fileHeader.formatID, "QUP$", 4) && !std::memcmp(fileHeader.fileInfoID, "OSWQ", 4)) { return true; } #endif return false; } CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderPSM(MemoryFileReader file, const uint64 *pfilesize) { PSMFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return ProbeWantMoreData; } if(!ValidateHeader(fileHeader)) { return ProbeFailure; } PSMChunk chunkHeader; if(!file.ReadStruct(chunkHeader)) { return ProbeWantMoreData; } if(chunkHeader.length == 0) { return ProbeFailure; } if((chunkHeader.id & 0x7F7F7F7Fu) != chunkHeader.id) // ASCII? { return ProbeFailure; } MPT_UNREFERENCED_PARAMETER(pfilesize); return ProbeSuccess; } bool CSoundFile::ReadPSM(FileReader &file, ModLoadingFlags loadFlags) { file.Rewind(); PSMFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return false; } #ifdef MPT_PSM_DECRYPT // CONVERT.EXE /K - I don't think any game ever used this. std::vector decrypted; if(!memcmp(fileHeader.formatID, "QUP$", 4) && !memcmp(fileHeader.fileInfoID, "OSWQ", 4)) { if(loadFlags == onlyVerifyHeader) return true; file.Rewind(); decrypted.resize(file.GetLength()); file.ReadRaw(decrypted.data(), decrypted.size()); uint8 i = 0; for(auto &c : decrypted) { c -= ++i; } file = FileReader(mpt::as_span(decrypted)); file.ReadStruct(fileHeader); } #endif // MPT_PSM_DECRYPT // Check header if(!ValidateHeader(fileHeader)) { return false; } ChunkReader chunkFile(file); ChunkReader::ChunkList chunks; if(loadFlags == onlyVerifyHeader) chunks = chunkFile.ReadChunksUntil(1, PSMChunk::idSDFT); else chunks = chunkFile.ReadChunks(1); // "SDFT" - Format info (song data starts here) if(!chunks.GetChunk(PSMChunk::idSDFT).ReadMagic("MAINSONG")) return false; else if(loadFlags == onlyVerifyHeader) return true; // Yep, this seems to be a valid file. InitializeGlobals(MOD_TYPE_PSM); m_SongFlags = SONG_ITOLDEFFECTS | SONG_ITCOMPATGXX; // "TITL" - Song Title FileReader titleChunk = chunks.GetChunk(PSMChunk::idTITL); titleChunk.ReadString(m_songName, titleChunk.GetLength()); Order().clear(); // Subsong setup std::vector subsongs; bool subsongPanningDiffers = false; // Do we have subsongs with different panning positions? bool sinariaFormat = false; // The game "Sinaria" uses a slightly modified PSM structure - in some ways it's more like PSM16 (e.g. effects). // "SONG" - Subsong information (channel count etc) auto songChunks = chunks.GetAllChunks(PSMChunk::idSONG); for(ChunkReader chunk : songChunks) { PSMSongHeader songHeader; if(!chunk.ReadStruct(songHeader) || songHeader.compression != 0x01) // No compression for PSM files { return false; } // Subsongs *might* have different channel count m_nChannels = Clamp(static_cast(songHeader.numChannels), m_nChannels, MAX_BASECHANNELS); PSMSubSong subsong; mpt::String::WriteAutoBuf(subsong.songName) = mpt::String::ReadBuf(mpt::String::nullTerminated, songHeader.songType); #ifdef MPT_PSM_USE_REAL_SUBSONGS if(!Order().empty()) { // Add a new sequence for this subsong if(Order.AddSequence() == SEQUENCEINDEX_INVALID) break; } Order().SetName(mpt::ToUnicode(mpt::Charset::CP437, subsong.songName)); #endif // MPT_PSM_USE_REAL_SUBSONGS // Read "Sub chunks" auto subChunks = chunk.ReadChunks(1); for(const auto &subChunkIter : subChunks.chunks) { FileReader subChunk(subChunkIter.GetData()); PSMChunk subChunkHead = subChunkIter.GetHeader(); switch(subChunkHead.GetID()) { #if 0 case PSMChunk::idDATE: // "DATE" - Conversion date (YYMMDD) if(subChunkHead.GetLength() == 6) { char cversion[7]; subChunk.ReadString(cversion, 6); uint32 version = ConvertStrTo(cversion); // Sinaria song dates (just to go sure...) if(version == 800211 || version == 940902 || version == 940903 || version == 940906 || version == 940914 || version == 941213) sinariaFormat = true; } break; #endif case PSMChunk::idOPLH: // "OPLH" - Order list, channel + module settings if(subChunkHead.GetLength() >= 9) { // First two bytes = Number of chunks that follow //uint16 totalChunks = subChunk.ReadUint16LE(); subChunk.Skip(2); // Now, the interesting part begins! uint16 chunkCount = 0, firstOrderChunk = uint16_max; // "Sub sub chunks" (grrrr, silly format) while(subChunk.CanRead(1)) { uint8 opcode = subChunk.ReadUint8(); if(!opcode) { // Last chunk was reached. break; } // Note: This is more like a playlist than a collection of global values. // In theory, a tempo item inbetween two order items should modify the // tempo when switching patterns. No module uses this feature in practice // though, so we can keep our loader simple. // Unimplemented opcodes do nothing or freeze MASI. switch(opcode) { case 0x01: // Play order list item { if(subsong.startOrder == ORDERINDEX_INVALID) subsong.startOrder = Order().GetLength(); subsong.endOrder = Order().GetLength(); PATTERNINDEX pat = ReadPSMPatternIndex(subChunk, sinariaFormat); if(pat == 0xFF) pat = Order.GetInvalidPatIndex(); else if(pat == 0xFE) pat = Order.GetIgnoreIndex(); Order().push_back(pat); // Decide whether this is the first order chunk or not (for finding out the correct restart position) if(firstOrderChunk == uint16_max) firstOrderChunk = chunkCount; } break; // 0x02: Play Range // 0x03: Jump Loop case 0x04: // Jump Line (Restart position) { uint16 restartChunk = subChunk.ReadUint16LE(); if(restartChunk >= firstOrderChunk) subsong.restartPos = static_cast(restartChunk - firstOrderChunk); // Close enough - we assume that order list is continuous (like in any real-world PSM) Order().SetRestartPos(subsong.restartPos); } break; // 0x05: Channel Flip // 0x06: Transpose case 0x07: // Default Speed subsong.defaultSpeed = subChunk.ReadUint8(); break; case 0x08: // Default Tempo subsong.defaultTempo = subChunk.ReadUint8(); break; case 0x0C: // Sample map table // Never seems to be different, so... // This is probably a part of the never-implemented "mini programming language" mentioned in the PSM docs. // Output of PLAY.EXE: "SMapTabl from pos 0 to pos -1 starting at 0 and adding 1 to it each time" // It appears that this maps e.g. what is "I0" in the file to sample 1. // If we were being fancy, we could implement this, but in practice it won't matter. { uint8 mapTable[6]; if(!subChunk.ReadArray(mapTable) || mapTable[0] != 0x00 || mapTable[1] != 0xFF // "0 to -1" (does not seem to do anything) || mapTable[2] != 0x00 || mapTable[3] != 0x00 // "at 0" (actually this appears to be the adding part - changing this to 0x01 0x00 offsets all samples by 1) || mapTable[4] != 0x01 || mapTable[5] != 0x00) // "adding 1" (does not seem to do anything) { return false; } } break; case 0x0D: // Channel panning table - can be set using CONVERT.EXE /E { const auto [chn, pan, type] = subChunk.ReadArray(); if(chn < subsong.channelPanning.size()) { switch(type) { case 0: // use panning subsong.channelPanning[chn] = pan ^ 128; subsong.channelSurround[chn] = false; break; case 2: // surround subsong.channelPanning[chn] = 128; subsong.channelSurround[chn] = true; break; case 4: // center subsong.channelPanning[chn] = 128; subsong.channelSurround[chn] = false; break; } if(subsongPanningDiffers == false && subsongs.size() > 0) { if(subsongs.back().channelPanning[chn] != subsong.channelPanning[chn] || subsongs.back().channelSurround[chn] != subsong.channelSurround[chn]) subsongPanningDiffers = true; } } } break; case 0x0E: // Channel volume table (0...255) - can be set using CONVERT.EXE /E, is 255 in all "official" PSMs except for some OMF 2097 tracks { const auto [chn, vol] = subChunk.ReadArray(); if(chn < subsong.channelVolume.size()) { subsong.channelVolume[chn] = (vol / 4u) + 1; } } break; default: // Should never happen in "real" PSM files. But in this case, we have to quit as we don't know how big the chunk really is. return false; } chunkCount++; } } break; case PSMChunk::idPPAN: // PPAN - Channel panning table (used in Sinaria) // In some Sinaria tunes, this is actually longer than 2 * channels... MPT_ASSERT(subChunkHead.GetLength() >= m_nChannels * 2u); for(CHANNELINDEX chn = 0; chn < m_nChannels; chn++) { if(!subChunk.CanRead(2)) break; const auto [type, pan] = subChunk.ReadArray(); switch(type) { case 0: // use panning subsong.channelPanning[chn] = pan ^ 128; subsong.channelSurround[chn] = false; break; case 2: // surround subsong.channelPanning[chn] = 128; subsong.channelSurround[chn] = true; break; case 4: // center subsong.channelPanning[chn] = 128; subsong.channelSurround[chn] = false; break; default: break; } } break; case PSMChunk::idPATT: // PATT - Pattern list // We don't really need this. break; case PSMChunk::idDSAM: // DSAM - Sample list // We don't need this either. break; default: break; } } // Attach this subsong to the subsong list - finally, all "sub sub sub ..." chunks are parsed. if(subsong.startOrder != ORDERINDEX_INVALID && subsong.endOrder != ORDERINDEX_INVALID) { // Separate subsongs by "---" patterns Order().push_back(); subsongs.push_back(subsong); } } #ifdef MPT_PSM_USE_REAL_SUBSONGS Order.SetSequence(0); #endif // MPT_PSM_USE_REAL_SUBSONGS if(subsongs.empty()) return false; // DSMP - Samples if(loadFlags & loadSampleData) { auto sampleChunks = chunks.GetAllChunks(PSMChunk::idDSMP); for(auto &chunk : sampleChunks) { SAMPLEINDEX smp; if(!sinariaFormat) { // Original header PSMSampleHeader sampleHeader; if(!chunk.ReadStruct(sampleHeader)) continue; smp = static_cast(sampleHeader.sampleNumber + 1); if(smp > 0 && smp < MAX_SAMPLES) { m_nSamples = std::max(m_nSamples, smp); sampleHeader.ConvertToMPT(Samples[smp]); m_szNames[smp] = mpt::String::ReadBuf(mpt::String::nullTerminated, sampleHeader.sampleName); } } else { // Sinaria uses a slightly different sample header PSMSinariaSampleHeader sampleHeader; if(!chunk.ReadStruct(sampleHeader)) continue; smp = static_cast(sampleHeader.sampleNumber + 1); if(smp > 0 && smp < MAX_SAMPLES) { m_nSamples = std::max(m_nSamples, smp); sampleHeader.ConvertToMPT(Samples[smp]); m_szNames[smp] = mpt::String::ReadBuf(mpt::String::nullTerminated, sampleHeader.sampleName); } } if(smp > 0 && smp < MAX_SAMPLES) { SampleIO( SampleIO::_8bit, SampleIO::mono, SampleIO::littleEndian, SampleIO::deltaPCM).ReadSample(Samples[smp], chunk); } } } // Make the default variables of the first subsong global m_nDefaultSpeed = subsongs[0].defaultSpeed; m_nDefaultTempo.Set(subsongs[0].defaultTempo); Order().SetRestartPos(subsongs[0].restartPos); for(CHANNELINDEX chn = 0; chn < m_nChannels; chn++) { ChnSettings[chn].Reset(); ChnSettings[chn].nVolume = subsongs[0].channelVolume[chn]; ChnSettings[chn].nPan = subsongs[0].channelPanning[chn]; ChnSettings[chn].dwFlags.set(CHN_SURROUND, subsongs[0].channelSurround[chn]); } m_modFormat.formatName = sinariaFormat ? U_("Epic MegaGames MASI (New Version / Sinaria)") : U_("Epic MegaGames MASI (New Version)"); m_modFormat.type = U_("psm"); m_modFormat.charset = mpt::Charset::CP437; if(!(loadFlags & loadPatternData) || m_nChannels == 0) { return true; } // "PBOD" - Pattern data of a single pattern // Now that we know the number of channels, we can go through all the patterns. auto pattChunks = chunks.GetAllChunks(PSMChunk::idPBOD); Patterns.ResizeArray(static_cast(pattChunks.size())); for(auto &chunk : pattChunks) { if(chunk.GetLength() != chunk.ReadUint32LE() // Same value twice || !chunk.LengthIsAtLeast(8)) { continue; } PATTERNINDEX pat = ReadPSMPatternIndex(chunk, sinariaFormat); uint16 numRows = chunk.ReadUint16LE(); if(!Patterns.Insert(pat, numRows)) { continue; } enum { noteFlag = 0x80, instrFlag = 0x40, volFlag = 0x20, effectFlag = 0x10, }; // Read pattern. for(ROWINDEX row = 0; row < numRows; row++) { PatternRow rowBase = Patterns[pat].GetRow(row); uint16 rowSize = chunk.ReadUint16LE(); if(rowSize <= 2) { continue; } FileReader rowChunk = chunk.ReadChunk(rowSize - 2); while(rowChunk.CanRead(3)) { const auto [flags, channel] = rowChunk.ReadArray(); // Point to the correct channel ModCommand &m = rowBase[std::min(static_cast(m_nChannels - 1), static_cast(channel))]; if(flags & noteFlag) { // Note present uint8 note = rowChunk.ReadUint8(); if(!sinariaFormat) { if(note == 0xFF) // Can be found in a few files but is apparently not supported by MASI note = NOTE_NOTECUT; else if(note < 129) note = (note & 0x0F) + 12 * (note >> 4) + 13; } else { if(note < 85) note += 36; } m.note = note; } if(flags & instrFlag) { // Instrument present m.instr = rowChunk.ReadUint8() + 1; } if(flags & volFlag) { // Volume present uint8 vol = rowChunk.ReadUint8(); m.volcmd = VOLCMD_VOLUME; m.vol = (std::min(vol, uint8(127)) + 1) / 2; } if(flags & effectFlag) { // Effect present - convert const auto [command, param] = rowChunk.ReadArray(); m.param = param; // This list is annoyingly similar to PSM16, but not quite identical. switch(command) { // Volslides case 0x01: // fine volslide up m.command = CMD_VOLUMESLIDE; if (sinariaFormat) m.param = (m.param << 4) | 0x0F; else m.param = ((m.param & 0x1E) << 3) | 0x0F; break; case 0x02: // volslide up m.command = CMD_VOLUMESLIDE; if (sinariaFormat) m.param = 0xF0 & (m.param << 4); else m.param = 0xF0 & (m.param << 3); break; case 0x03: // fine volslide down m.command = CMD_VOLUMESLIDE; if (sinariaFormat) m.param |= 0xF0; else m.param = 0xF0 | (m.param >> 1); break; case 0x04: // volslide down m.command = CMD_VOLUMESLIDE; if (sinariaFormat) m.param &= 0x0F; else if(m.param < 2) m.param |= 0xF0; else m.param = (m.param >> 1) & 0x0F; break; // Portamento case 0x0B: // fine portamento up m.command = CMD_PORTAMENTOUP; m.param = 0xF0 | ConvertPSMPorta(m.param, sinariaFormat); break; case 0x0C: // portamento up m.command = CMD_PORTAMENTOUP; m.param = ConvertPSMPorta(m.param, sinariaFormat); break; case 0x0D: // fine portamento down m.command = CMD_PORTAMENTODOWN; m.param = 0xF0 | ConvertPSMPorta(m.param, sinariaFormat); break; case 0x0E: // portamento down m.command = CMD_PORTAMENTODOWN; m.param = ConvertPSMPorta(m.param, sinariaFormat); break; case 0x0F: // tone portamento m.command = CMD_TONEPORTAMENTO; if(!sinariaFormat) m.param >>= 2; break; case 0x11: // glissando control m.command = CMD_S3MCMDEX; m.param = 0x10 | (m.param & 0x01); break; case 0x10: // tone portamento + volslide up m.command = CMD_TONEPORTAVOL; m.param = m.param & 0xF0; break; case 0x12: // tone portamento + volslide down m.command = CMD_TONEPORTAVOL; m.param = (m.param >> 4) & 0x0F; break; case 0x13: // ScreamTracker command S - actually hangs / crashes MASI m.command = CMD_S3MCMDEX; break; // Vibrato case 0x15: // vibrato m.command = CMD_VIBRATO; break; case 0x16: // vibrato waveform m.command = CMD_S3MCMDEX; m.param = 0x30 | (m.param & 0x0F); break; case 0x17: // vibrato + volslide up m.command = CMD_VIBRATOVOL; m.param = 0xF0 | m.param; break; case 0x18: // vibrato + volslide down m.command = CMD_VIBRATOVOL; break; // Tremolo case 0x1F: // tremolo m.command = CMD_TREMOLO; break; case 0x20: // tremolo waveform m.command = CMD_S3MCMDEX; m.param = 0x40 | (m.param & 0x0F); break; // Sample commands case 0x29: // 3-byte offset - we only support the middle byte. m.command = CMD_OFFSET; m.param = rowChunk.ReadUint8(); rowChunk.Skip(1); break; case 0x2A: // retrigger m.command = CMD_RETRIG; break; case 0x2B: // note cut m.command = CMD_S3MCMDEX; m.param = 0xC0 | (m.param & 0x0F); break; case 0x2C: // note delay m.command = CMD_S3MCMDEX; m.param = 0xD0 | (m.param & 0x0F); break; // Position change case 0x33: // position jump - MASI seems to ignore this command, and CONVERT.EXE never writes it m.command = CMD_POSITIONJUMP; m.param /= 2u; // actually it is probably just an index into the order table rowChunk.Skip(1); break; case 0x34: // pattern break m.command = CMD_PATTERNBREAK; // When converting from S3M, the parameter is double-BDC-encoded (wtf!) // When converting from MOD, it's in binary. // MASI ignores the parameter entirely, and so do we. m.param = 0; break; case 0x35: // loop pattern m.command = CMD_S3MCMDEX; m.param = 0xB0 | (m.param & 0x0F); break; case 0x36: // pattern delay m.command = CMD_S3MCMDEX; m.param = 0xE0 | (m.param & 0x0F); break; // speed change case 0x3D: // set speed m.command = CMD_SPEED; break; case 0x3E: // set tempo m.command = CMD_TEMPO; break; // misc commands case 0x47: // arpeggio m.command = CMD_ARPEGGIO; break; case 0x48: // set finetune m.command = CMD_S3MCMDEX; m.param = 0x20 | (m.param & 0x0F); break; case 0x49: // set balance m.command = CMD_S3MCMDEX; m.param = 0x80 | (m.param & 0x0F); break; default: m.command = CMD_NONE; break; } } } } } if(subsongs.size() > 1) { // Write subsong "configuration" to patterns (only if there are multiple subsongs) for(size_t i = 0; i < subsongs.size(); i++) { #ifdef MPT_PSM_USE_REAL_SUBSONGS ModSequence &order = Order(static_cast(i)); #else ModSequence &order = Order(); #endif // MPT_PSM_USE_REAL_SUBSONGS const PSMSubSong &subsong = subsongs[i]; PATTERNINDEX startPattern = order[subsong.startOrder]; if(Patterns.IsValidPat(startPattern)) { startPattern = order.EnsureUnique(subsong.startOrder); // Subsongs with different panning setup -> write to pattern (MUSIC_C.PSM) // Don't write channel volume for now, as there is no real-world module which needs it. if(subsongPanningDiffers) { for(CHANNELINDEX chn = 0; chn < m_nChannels; chn++) { if(subsong.channelSurround[chn]) Patterns[startPattern].WriteEffect(EffectWriter(CMD_S3MCMDEX, 0x91).Row(0).Channel(chn).RetryNextRow()); else Patterns[startPattern].WriteEffect(EffectWriter(CMD_PANNING8, subsong.channelPanning[chn]).Row(0).Channel(chn).RetryNextRow()); } } // Write default tempo/speed to pattern Patterns[startPattern].WriteEffect(EffectWriter(CMD_SPEED, subsong.defaultSpeed).Row(0).RetryNextRow()); Patterns[startPattern].WriteEffect(EffectWriter(CMD_TEMPO, subsong.defaultTempo).Row(0).RetryNextRow()); } #ifndef MPT_PSM_USE_REAL_SUBSONGS // Add restart position to the last pattern PATTERNINDEX endPattern = order[subsong.endOrder]; if(Patterns.IsValidPat(endPattern)) { endPattern = order.EnsureUnique(subsong.endOrder); ROWINDEX lastRow = Patterns[endPattern].GetNumRows() - 1; auto m = Patterns[endPattern].cbegin(); for(uint32 cell = 0; cell < m_nChannels * Patterns[endPattern].GetNumRows(); cell++, m++) { if(m->command == CMD_PATTERNBREAK || m->command == CMD_POSITIONJUMP) { lastRow = cell / m_nChannels; break; } } Patterns[endPattern].WriteEffect(EffectWriter(CMD_POSITIONJUMP, mpt::saturate_cast(subsong.startOrder + subsong.restartPos)).Row(lastRow).RetryPreviousRow()); } // Set the subsong name to all pattern names for(ORDERINDEX ord = subsong.startOrder; ord <= subsong.endOrder; ord++) { if(Patterns.IsValidIndex(order[ord])) Patterns[order[ord]].SetName(subsong.songName); } #endif // MPT_PSM_USE_REAL_SUBSONGS } } return true; } //////////////////////////////// // // PSM16 support starts here. // struct PSM16FileHeader { char formatID[4]; // "PSM\xFE" (PSM16) char songName[59]; // Song title, padded with nulls uint8le lineEnd; // $1A uint8le songType; // Song Type bitfield uint8le formatVersion; // $10 uint8le patternVersion; // 0 or 1 uint8le songSpeed; // 1 ... 255 uint8le songTempo; // 32 ... 255 uint8le masterVolume; // 0 ... 255 uint16le songLength; // 0 ... 255 (number of patterns to play in the song) uint16le songOrders; // 0 ... 255 (same as previous value as no subsongs are present) uint16le numPatterns; // 1 ... 255 uint16le numSamples; // 1 ... 255 uint16le numChannelsPlay; // 0 ... 32 (max. number of channels to play) uint16le numChannelsReal; // 0 ... 32 (max. number of channels to process) uint32le orderOffset; // Pointer to order list uint32le panOffset; // Pointer to pan table uint32le patOffset; // Pointer to pattern data uint32le smpOffset; // Pointer to sample headers uint32le commentsOffset; // Pointer to song comment uint32le patSize; // Size of all patterns char filler[40]; }; MPT_BINARY_STRUCT(PSM16FileHeader, 146) struct PSM16SampleHeader { enum SampleFlags { smpMask = 0x7F, smp16Bit = 0x04, smpUnsigned = 0x08, smpDelta = 0x10, smpPingPong = 0x20, smpLoop = 0x80, }; char filename[13]; // null-terminated char name[24]; // ditto uint32le offset; // in file uint32le memoffset; // not used uint16le sampleNumber; // 1 ... 255 uint8le flags; // sample flag bitfield uint32le length; // in bytes uint32le loopStart; // in samples? uint32le loopEnd; // in samples? uint8le finetune; // Low nibble = MOD finetune, high nibble = transpose (7 = center) uint8le volume; // default volume uint16le c2freq; // Middle-C frequency, which has to be combined with the finetune and transpose. // Convert sample header to OpenMPT's internal format void ConvertToMPT(ModSample &mptSmp) const { mptSmp.Initialize(); mptSmp.filename = mpt::String::ReadBuf(mpt::String::nullTerminated, filename); mptSmp.nLength = length; mptSmp.nLoopStart = loopStart; mptSmp.nLoopEnd = loopEnd; // It seems like that finetune and transpose are added to the already given c2freq... That's a double WTF! // Why on earth would you want to use both systems at the same time? mptSmp.nC5Speed = c2freq; mptSmp.Transpose(((finetune ^ 0x08) - 0x78) / (12.0 * 16.0)); mptSmp.nVolume = std::min(volume.get(), uint8(64)) * 4u; mptSmp.uFlags.reset(); if(flags & PSM16SampleHeader::smp16Bit) { mptSmp.uFlags.set(CHN_16BIT); mptSmp.nLength /= 2u; } if(flags & PSM16SampleHeader::smpPingPong) { mptSmp.uFlags.set(CHN_PINGPONGLOOP); } if(flags & PSM16SampleHeader::smpLoop) { mptSmp.uFlags.set(CHN_LOOP); } } // Retrieve the internal sample format flags for this sample. SampleIO GetSampleFormat() const { SampleIO sampleIO( (flags & PSM16SampleHeader::smp16Bit) ? SampleIO::_16bit : SampleIO::_8bit, SampleIO::mono, SampleIO::littleEndian, SampleIO::signedPCM); if(flags & PSM16SampleHeader::smpUnsigned) { sampleIO |= SampleIO::unsignedPCM; } else if((flags & PSM16SampleHeader::smpDelta) || (flags & PSM16SampleHeader::smpMask) == 0) { sampleIO |= SampleIO::deltaPCM; } return sampleIO; } }; MPT_BINARY_STRUCT(PSM16SampleHeader, 64) struct PSM16PatternHeader { uint16le size; // includes header bytes uint8le numRows; // 1 ... 64 uint8le numChans; // 1 ... 32 }; MPT_BINARY_STRUCT(PSM16PatternHeader, 4) static bool ValidateHeader(const PSM16FileHeader &fileHeader) { if(std::memcmp(fileHeader.formatID, "PSM\xFE", 4) || fileHeader.lineEnd != 0x1A || (fileHeader.formatVersion != 0x10 && fileHeader.formatVersion != 0x01) // why is this sometimes 0x01? || fileHeader.patternVersion != 0 // 255ch pattern version not supported (did anyone use this?) || (fileHeader.songType & 3) != 0 || fileHeader.numChannelsPlay > MAX_BASECHANNELS || fileHeader.numChannelsReal > MAX_BASECHANNELS || std::max(fileHeader.numChannelsPlay, fileHeader.numChannelsReal) == 0) { return false; } return true; } CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderPSM16(MemoryFileReader file, const uint64 *pfilesize) { PSM16FileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return ProbeWantMoreData; } if(!ValidateHeader(fileHeader)) { return ProbeFailure; } MPT_UNREFERENCED_PARAMETER(pfilesize); return ProbeSuccess; } bool CSoundFile::ReadPSM16(FileReader &file, ModLoadingFlags loadFlags) { file.Rewind(); // Is it a valid PSM16 file? PSM16FileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return false; } if(!ValidateHeader(fileHeader)) { return false; } if(loadFlags == onlyVerifyHeader) { return true; } // Seems to be valid! InitializeGlobals(MOD_TYPE_PSM); m_modFormat.formatName = U_("Epic MegaGames MASI (Old Version)"); m_modFormat.type = U_("psm"); m_modFormat.charset = mpt::Charset::CP437; m_nChannels = Clamp(CHANNELINDEX(fileHeader.numChannelsPlay), CHANNELINDEX(fileHeader.numChannelsReal), MAX_BASECHANNELS); m_nSamplePreAmp = fileHeader.masterVolume; if(m_nSamplePreAmp == 255) { // Most of the time, the master volume value makes sense... Just not when it's 255. m_nSamplePreAmp = 48; } m_nDefaultSpeed = fileHeader.songSpeed; m_nDefaultTempo.Set(fileHeader.songTempo); m_songName = mpt::String::ReadBuf(mpt::String::spacePadded, fileHeader.songName); // Read orders if(fileHeader.orderOffset > 4 && file.Seek(fileHeader.orderOffset - 4) && file.ReadMagic("PORD")) { ReadOrderFromFile(Order(), file, fileHeader.songOrders); } // Read pan positions if(fileHeader.panOffset > 4 && file.Seek(fileHeader.panOffset - 4) && file.ReadMagic("PPAN")) { for(CHANNELINDEX i = 0; i < 32; i++) { ChnSettings[i].Reset(); ChnSettings[i].nPan = ((15 - (file.ReadUint8() & 0x0F)) * 256 + 8) / 15; // 15 seems to be left and 0 seems to be right... // ChnSettings[i].dwFlags = (i >= fileHeader.numChannelsPlay) ? CHN_MUTE : 0; // don't mute channels, as muted channels are completely ignored in S3M } } // Read samples if(fileHeader.smpOffset > 4 && file.Seek(fileHeader.smpOffset - 4) && file.ReadMagic("PSAH")) { FileReader sampleChunk = file.ReadChunk(fileHeader.numSamples * sizeof(PSM16SampleHeader)); for(SAMPLEINDEX fileSample = 0; fileSample < fileHeader.numSamples; fileSample++) { PSM16SampleHeader sampleHeader; if(!sampleChunk.ReadStruct(sampleHeader)) { break; } const SAMPLEINDEX smp = sampleHeader.sampleNumber; if(smp > 0 && smp < MAX_SAMPLES && !Samples[smp].HasSampleData()) { m_nSamples = std::max(m_nSamples, smp); sampleHeader.ConvertToMPT(Samples[smp]); m_szNames[smp] = mpt::String::ReadBuf(mpt::String::nullTerminated, sampleHeader.name); if(loadFlags & loadSampleData) { file.Seek(sampleHeader.offset); sampleHeader.GetSampleFormat().ReadSample(Samples[smp], file); } } } } // Read patterns if(!(loadFlags & loadPatternData)) { return true; } if(fileHeader.patOffset > 4 && file.Seek(fileHeader.patOffset - 4) && file.ReadMagic("PPAT")) { Patterns.ResizeArray(fileHeader.numPatterns); for(PATTERNINDEX pat = 0; pat < fileHeader.numPatterns; pat++) { PSM16PatternHeader patternHeader; if(!file.ReadStruct(patternHeader)) { break; } if(patternHeader.size < sizeof(PSM16PatternHeader)) { continue; } // Patterns are padded to 16 Bytes FileReader patternChunk = file.ReadChunk(((patternHeader.size + 15) & ~15) - sizeof(PSM16PatternHeader)); if(!Patterns.Insert(pat, patternHeader.numRows)) { continue; } enum { channelMask = 0x1F, noteFlag = 0x80, volFlag = 0x40, effectFlag = 0x20, }; ROWINDEX curRow = 0; while(patternChunk.CanRead(1) && curRow < patternHeader.numRows) { uint8 chnFlag = patternChunk.ReadUint8(); if(chnFlag == 0) { curRow++; continue; } ModCommand &m = *Patterns[pat].GetpModCommand(curRow, std::min(static_cast(chnFlag & channelMask), static_cast(m_nChannels - 1))); if(chnFlag & noteFlag) { // note + instr present const auto [note, instr] = patternChunk.ReadArray(); m.note = note + 36; m.instr = instr; } if(chnFlag & volFlag) { // volume present m.volcmd = VOLCMD_VOLUME; m.vol = std::min(patternChunk.ReadUint8(), uint8(64)); } if(chnFlag & effectFlag) { // effect present - convert const auto [command, param] = patternChunk.ReadArray(); m.param = param; switch(command) { // Volslides case 0x01: // fine volslide up m.command = CMD_VOLUMESLIDE; m.param = (m.param << 4) | 0x0F; break; case 0x02: // volslide up m.command = CMD_VOLUMESLIDE; m.param = (m.param << 4) & 0xF0; break; case 0x03: // fine voslide down m.command = CMD_VOLUMESLIDE; m.param = 0xF0 | m.param; break; case 0x04: // volslide down m.command = CMD_VOLUMESLIDE; m.param = m.param & 0x0F; break; // Portamento case 0x0A: // fine portamento up m.command = CMD_PORTAMENTOUP; m.param |= 0xF0; break; case 0x0B: // portamento down m.command = CMD_PORTAMENTOUP; break; case 0x0C: // fine portamento down m.command = CMD_PORTAMENTODOWN; m.param |= 0xF0; break; case 0x0D: // portamento down m.command = CMD_PORTAMENTODOWN; break; case 0x0E: // tone portamento m.command = CMD_TONEPORTAMENTO; break; case 0x0F: // glissando control m.command = CMD_S3MCMDEX; m.param |= 0x10; break; case 0x10: // tone portamento + volslide up m.command = CMD_TONEPORTAVOL; m.param <<= 4; break; case 0x11: // tone portamento + volslide down m.command = CMD_TONEPORTAVOL; m.param &= 0x0F; break; // Vibrato case 0x14: // vibrato m.command = CMD_VIBRATO; break; case 0x15: // vibrato waveform m.command = CMD_S3MCMDEX; m.param |= 0x30; break; case 0x16: // vibrato + volslide up m.command = CMD_VIBRATOVOL; m.param <<= 4; break; case 0x17: // vibrato + volslide down m.command = CMD_VIBRATOVOL; m.param &= 0x0F; break; // Tremolo case 0x1E: // tremolo m.command = CMD_TREMOLO; break; case 0x1F: // tremolo waveform m.command = CMD_S3MCMDEX; m.param |= 0x40; break; // Sample commands case 0x28: // 3-byte offset - we only support the middle byte. m.command = CMD_OFFSET; m.param = patternChunk.ReadUint8(); patternChunk.Skip(1); break; case 0x29: // retrigger m.command = CMD_RETRIG; m.param &= 0x0F; break; case 0x2A: // note cut m.command = CMD_S3MCMDEX; #ifdef MODPLUG_TRACKER if(m.param == 0) // in S3M mode, SC0 is ignored, so we convert it to a note cut. { if(m.note == NOTE_NONE) { m.note = NOTE_NOTECUT; m.command = CMD_NONE; } else { m.param = 1; } } #endif // MODPLUG_TRACKER m.param |= 0xC0; break; case 0x2B: // note delay m.command = CMD_S3MCMDEX; m.param |= 0xD0; break; // Position change case 0x32: // position jump m.command = CMD_POSITIONJUMP; break; case 0x33: // pattern break m.command = CMD_PATTERNBREAK; break; case 0x34: // loop pattern m.command = CMD_S3MCMDEX; m.param |= 0xB0; break; case 0x35: // pattern delay m.command = CMD_S3MCMDEX; m.param |= 0xE0; break; // speed change case 0x3C: // set speed m.command = CMD_SPEED; break; case 0x3D: // set tempo m.command = CMD_TEMPO; break; // misc commands case 0x46: // arpeggio m.command = CMD_ARPEGGIO; break; case 0x47: // set finetune m.command = CMD_S3MCMDEX; m.param = 0x20 | (m.param & 0x0F); break; case 0x48: // set balance (panning?) m.command = CMD_S3MCMDEX; m.param = 0x80 | (m.param & 0x0F); break; default: m.command = CMD_NONE; break; } } } // Pattern break for short patterns (so saving the modules as S3M won't break it) if(patternHeader.numRows != 64) { Patterns[pat].WriteEffect(EffectWriter(CMD_PATTERNBREAK, 0).Row(patternHeader.numRows - 1).RetryNextRow()); } } } if(fileHeader.commentsOffset != 0) { file.Seek(fileHeader.commentsOffset); m_songMessage.Read(file, file.ReadUint16LE(), SongMessage::leAutodetect); } return true; } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Load_ptm.cpp0000644000175000017500000001742014120160253020334 00000000000000/* * Load_ptm.cpp * ------------ * Purpose: PTM (PolyTracker) module loader * Notes : (currently none) * Authors: Olivier Lapicque * OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Loaders.h" OPENMPT_NAMESPACE_BEGIN struct PTMFileHeader { char songname[28]; // Name of song, asciiz string uint8le dosEOF; // 26 uint8le versionLo; // 03 version of file, currently 0203h uint8le versionHi; // 02 uint8le reserved1; // Reserved, set to 0 uint16le numOrders; // Number of orders (0..256) uint16le numSamples; // Number of instruments (1..255) uint16le numPatterns; // Number of patterns (1..128) uint16le numChannels; // Number of channels (voices) used (1..32) uint16le flags; // Set to 0 uint8le reserved2[2]; // Reserved, set to 0 char magic[4]; // Song identification, 'PTMF' uint8le reserved3[16]; // Reserved, set to 0 uint8le chnPan[32]; // Channel panning settings, 0..15, 0 = left, 7 = middle, 15 = right uint8le orders[256]; // Order list, valid entries 0..nOrders-1 uint16le patOffsets[128]; // Pattern offsets (*16) }; MPT_BINARY_STRUCT(PTMFileHeader, 608) struct PTMSampleHeader { enum SampleFlags { smpTypeMask = 0x03, smpPCM = 0x01, smpLoop = 0x04, smpPingPong = 0x08, smp16Bit = 0x10, }; uint8le flags; // Sample type (see SampleFlags) char filename[12]; // Name of external sample file uint8le volume; // Default volume uint16le c4speed; // C-4 speed (yep, not C-5) uint8le smpSegment[2]; // Sample segment (used internally) uint32le dataOffset; // Offset of sample data uint32le length; // Sample size (in bytes) uint32le loopStart; // Start of loop uint32le loopEnd; // End of loop uint8le gusdata[14]; char samplename[28]; // Name of sample, ASCIIZ char magic[4]; // Sample identification, 'PTMS' // Convert an PTM sample header to OpenMPT's internal sample header. SampleIO ConvertToMPT(ModSample &mptSmp) const { mptSmp.Initialize(MOD_TYPE_S3M); mptSmp.nVolume = std::min(volume.get(), uint8(64)) * 4; mptSmp.nC5Speed = c4speed * 2; mptSmp.filename = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, filename); SampleIO sampleIO( SampleIO::_8bit, SampleIO::mono, SampleIO::littleEndian, SampleIO::deltaPCM); if((flags & smpTypeMask) == smpPCM) { mptSmp.nLength = length; mptSmp.nLoopStart = loopStart; mptSmp.nLoopEnd = loopEnd; if(mptSmp.nLoopEnd > mptSmp.nLoopStart) mptSmp.nLoopEnd--; if(flags & smpLoop) mptSmp.uFlags.set(CHN_LOOP); if(flags & smpPingPong) mptSmp.uFlags.set(CHN_PINGPONGLOOP); if(flags & smp16Bit) { sampleIO |= SampleIO::_16bit; sampleIO |= SampleIO::PTM8Dto16; mptSmp.nLength /= 2; mptSmp.nLoopStart /= 2; mptSmp.nLoopEnd /= 2; } } return sampleIO; } }; MPT_BINARY_STRUCT(PTMSampleHeader, 80) static bool ValidateHeader(const PTMFileHeader &fileHeader) { if(std::memcmp(fileHeader.magic, "PTMF", 4) || fileHeader.dosEOF != 26 || fileHeader.versionHi > 2 || fileHeader.flags != 0 || !fileHeader.numChannels || fileHeader.numChannels > 32 || !fileHeader.numOrders || fileHeader.numOrders > 256 || !fileHeader.numSamples || fileHeader.numSamples > 255 || !fileHeader.numPatterns || fileHeader.numPatterns > 128 ) { return false; } return true; } static uint64 GetHeaderMinimumAdditionalSize(const PTMFileHeader &fileHeader) { return fileHeader.numSamples * sizeof(PTMSampleHeader); } CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderPTM(MemoryFileReader file, const uint64 *pfilesize) { PTMFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return ProbeWantMoreData; } if(!ValidateHeader(fileHeader)) { return ProbeFailure; } return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); } bool CSoundFile::ReadPTM(FileReader &file, ModLoadingFlags loadFlags) { file.Rewind(); PTMFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return false; } if(!ValidateHeader(fileHeader)) { return false; } if(!file.CanRead(mpt::saturate_cast(GetHeaderMinimumAdditionalSize(fileHeader)))) { return false; } if(loadFlags == onlyVerifyHeader) { return true; } InitializeGlobals(MOD_TYPE_PTM); m_songName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileHeader.songname); m_modFormat.formatName = U_("PolyTracker"); m_modFormat.type = U_("ptm"); m_modFormat.madeWithTracker = MPT_UFORMAT("PolyTracker {}.{}")(fileHeader.versionHi.get(), mpt::ufmt::hex0<2>(fileHeader.versionLo.get())); m_modFormat.charset = mpt::Charset::CP437; m_SongFlags = SONG_ITCOMPATGXX | SONG_ITOLDEFFECTS; m_nChannels = fileHeader.numChannels; m_nSamples = std::min(static_cast(fileHeader.numSamples), static_cast(MAX_SAMPLES - 1)); ReadOrderFromArray(Order(), fileHeader.orders, fileHeader.numOrders, 0xFF, 0xFE); // Reading channel panning for(CHANNELINDEX chn = 0; chn < m_nChannels; chn++) { ChnSettings[chn].Reset(); ChnSettings[chn].nPan = ((fileHeader.chnPan[chn] & 0x0F) << 4) + 4; } // Reading samples FileReader sampleHeaderChunk = file.ReadChunk(fileHeader.numSamples * sizeof(PTMSampleHeader)); for(SAMPLEINDEX smp = 0; smp < m_nSamples; smp++) { PTMSampleHeader sampleHeader; sampleHeaderChunk.ReadStruct(sampleHeader); ModSample &sample = Samples[smp + 1]; m_szNames[smp + 1] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, sampleHeader.samplename); SampleIO sampleIO = sampleHeader.ConvertToMPT(sample); if((loadFlags & loadSampleData) && sample.nLength && file.Seek(sampleHeader.dataOffset)) { sampleIO.ReadSample(sample, file); } } // Reading Patterns if(!(loadFlags & loadPatternData)) { return true; } Patterns.ResizeArray(fileHeader.numPatterns); for(PATTERNINDEX pat = 0; pat < fileHeader.numPatterns; pat++) { if(!Patterns.Insert(pat, 64) || fileHeader.patOffsets[pat] == 0 || !file.Seek(fileHeader.patOffsets[pat] << 4)) { continue; } ModCommand *rowBase = Patterns[pat].GetpModCommand(0, 0); ROWINDEX row = 0; while(row < 64 && file.CanRead(1)) { uint8 b = file.ReadUint8(); if(b == 0) { row++; rowBase += m_nChannels; continue; } CHANNELINDEX chn = (b & 0x1F); ModCommand dummy = ModCommand(); ModCommand &m = chn < GetNumChannels() ? rowBase[chn] : dummy; if(b & 0x20) { const auto [note, instr] = file.ReadArray(); m.note = note; m.instr = instr; if(m.note == 254) m.note = NOTE_NOTECUT; else if(!m.note || m.note > 120) m.note = NOTE_NONE; } if(b & 0x40) { const auto [command, param] = file.ReadArray(); m.command = command; m.param = param; static constexpr EffectCommand effTrans[] = { CMD_GLOBALVOLUME, CMD_RETRIG, CMD_FINEVIBRATO, CMD_NOTESLIDEUP, CMD_NOTESLIDEDOWN, CMD_NOTESLIDEUPRETRIG, CMD_NOTESLIDEDOWNRETRIG, CMD_REVERSEOFFSET }; if(m.command < 0x10) { // Beware: Effect letters are as in MOD, but portamento and volume slides behave like in S3M (i.e. fine slides share the same effect letters) ConvertModCommand(m); } else if(m.command < 0x10 + std::size(effTrans)) { m.command = effTrans[m.command - 0x10]; } else { m.command = CMD_NONE; } switch(m.command) { case CMD_PANNING8: // Don't be surprised about the strange formula, this is directly translated from original disassembly... m.command = CMD_S3MCMDEX; m.param = 0x80 | ((std::max(m.param >> 3, 1u) - 1u) & 0x0F); break; case CMD_GLOBALVOLUME: m.param = std::min(m.param, uint8(0x40)) * 2u; break; } } if(b & 0x80) { m.volcmd = VOLCMD_VOLUME; m.vol = file.ReadUint8(); } } } return true; } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Load_s3m.cpp0000644000175000017500000010717514173265146020264 00000000000000/* * Load_s3m.cpp * ------------ * Purpose: S3M (ScreamTracker 3) module loader / saver * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Loaders.h" #include "S3MTools.h" #include "ITTools.h" #ifndef MODPLUG_NO_FILESAVE #include "mpt/io/base.hpp" #include "mpt/io/io.hpp" #include "mpt/io/io_stdstream.hpp" #include "../common/mptFileIO.h" #ifdef MODPLUG_TRACKER #include "../mptrack/TrackerSettings.h" #endif // MODPLUG_TRACKER #endif // MODPLUG_NO_FILESAVE #include "../common/version.h" OPENMPT_NAMESPACE_BEGIN void CSoundFile::S3MConvert(ModCommand &m, bool fromIT) { switch(m.command | 0x40) { case '@': m.command = (m.param ? CMD_DUMMY : CMD_NONE); break; case 'A': m.command = CMD_SPEED; break; case 'B': m.command = CMD_POSITIONJUMP; break; case 'C': m.command = CMD_PATTERNBREAK; if (!fromIT) m.param = (m.param >> 4) * 10 + (m.param & 0x0F); break; case 'D': m.command = CMD_VOLUMESLIDE; break; case 'E': m.command = CMD_PORTAMENTODOWN; break; case 'F': m.command = CMD_PORTAMENTOUP; break; case 'G': m.command = CMD_TONEPORTAMENTO; break; case 'H': m.command = CMD_VIBRATO; break; case 'I': m.command = CMD_TREMOR; break; case 'J': m.command = CMD_ARPEGGIO; break; case 'K': m.command = CMD_VIBRATOVOL; break; case 'L': m.command = CMD_TONEPORTAVOL; break; case 'M': m.command = CMD_CHANNELVOLUME; break; case 'N': m.command = CMD_CHANNELVOLSLIDE; break; case 'O': m.command = CMD_OFFSET; break; case 'P': m.command = CMD_PANNINGSLIDE; break; case 'Q': m.command = CMD_RETRIG; break; case 'R': m.command = CMD_TREMOLO; break; case 'S': m.command = CMD_S3MCMDEX; break; case 'T': m.command = CMD_TEMPO; break; case 'U': m.command = CMD_FINEVIBRATO; break; case 'V': m.command = CMD_GLOBALVOLUME; break; case 'W': m.command = CMD_GLOBALVOLSLIDE; break; case 'X': m.command = CMD_PANNING8; break; case 'Y': m.command = CMD_PANBRELLO; break; case 'Z': m.command = CMD_MIDI; break; case '\\': m.command = fromIT ? CMD_SMOOTHMIDI : CMD_MIDI; break; // Chars under 0x40 don't save properly, so the following commands don't map to their pattern editor representations case ']': m.command = fromIT ? CMD_DELAYCUT : CMD_NONE; break; case '[': m.command = fromIT ? CMD_XPARAM : CMD_NONE; break; case '^': m.command = fromIT ? CMD_FINETUNE : CMD_NONE; break; case '_': m.command = fromIT ? CMD_FINETUNE_SMOOTH : CMD_NONE; break; // BeRoTracker extensions case '1' + 0x41: m.command = fromIT ? CMD_KEYOFF : CMD_NONE; break; case '2' + 0x41: m.command = fromIT ? CMD_SETENVPOSITION : CMD_NONE; break; default: m.command = CMD_NONE; } } #ifndef MODPLUG_NO_FILESAVE void CSoundFile::S3MSaveConvert(uint8 &command, uint8 ¶m, bool toIT, bool compatibilityExport) const { const bool extendedIT = !compatibilityExport && toIT; switch(command) { case CMD_DUMMY: command = (param ? '@' : 0); break; case CMD_SPEED: command = 'A'; break; case CMD_POSITIONJUMP: command = 'B'; break; case CMD_PATTERNBREAK: command = 'C'; if(!toIT) param = ((param / 10) << 4) + (param % 10); break; case CMD_VOLUMESLIDE: command = 'D'; break; case CMD_PORTAMENTODOWN: command = 'E'; if (param >= 0xE0 && (GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM))) param = 0xDF; break; case CMD_PORTAMENTOUP: command = 'F'; if (param >= 0xE0 && (GetType() & (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_OFFSETPERCENTAGE: 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(toIT && !(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_XM | MOD_TYPE_MOD))) { if (param == 0xA4) { command = 'S'; param = 0x91; } else if (param == 0x80) { param = 0xFF; } else if (param < 0x80) { param <<= 1; } else command = 0; } else if (!toIT && (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_XM | MOD_TYPE_MOD))) { param >>= 1; } break; case CMD_PANBRELLO: command = 'Y'; break; case CMD_MIDI: command = 'Z'; break; case CMD_SMOOTHMIDI: if(extendedIT) command = '\\'; else command = 'Z'; break; case CMD_XFINEPORTAUPDOWN: 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 = 0; } break; case CMD_MODCMDEX: { ModCommand m; m.command = CMD_MODCMDEX; m.param = param; m.ExtendedMODtoS3MEffect(); command = m.command; param = m.param; S3MSaveConvert(command, param, toIT, compatibilityExport); } return; // Chars under 0x40 don't save properly, so map : to ] and # to [. case CMD_DELAYCUT: command = extendedIT ? ']' : 0; break; case CMD_XPARAM: command = extendedIT ? '[' : 0; break; case CMD_FINETUNE: command = extendedIT ? '^' : 0; break; case CMD_FINETUNE_SMOOTH: command = extendedIT ? '_' : 0; break; default: command = 0; } if(command == 0) { param = 0; } command &= ~0x40; } #endif // MODPLUG_NO_FILESAVE static bool ValidateHeader(const S3MFileHeader &fileHeader) { if(std::memcmp(fileHeader.magic, "SCRM", 4) || fileHeader.fileType != S3MFileHeader::idS3MType || (fileHeader.formatVersion != S3MFileHeader::oldVersion && fileHeader.formatVersion != S3MFileHeader::newVersion) ) { return false; } return true; } static uint64 GetHeaderMinimumAdditionalSize(const S3MFileHeader &fileHeader) { return fileHeader.ordNum + (fileHeader.smpNum + fileHeader.patNum) * 2; } CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderS3M(MemoryFileReader file, const uint64 *pfilesize) { S3MFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return ProbeWantMoreData; } if(!ValidateHeader(fileHeader)) { return ProbeFailure; } return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); } bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags) { file.Rewind(); // Is it a valid S3M file? S3MFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return false; } if(!ValidateHeader(fileHeader)) { return false; } if(!file.CanRead(mpt::saturate_cast(GetHeaderMinimumAdditionalSize(fileHeader)))) { return false; } if(loadFlags == onlyVerifyHeader) { return true; } InitializeGlobals(MOD_TYPE_S3M); m_nMinPeriod = 64; m_nMaxPeriod = 32767; // ST3 ignored Zxx commands, so if we find that a file was made with ST3, we should erase all MIDI macros. bool keepMidiMacros = false; mpt::ustring madeWithTracker; bool formatTrackerStr = false; bool nonCompatTracker = false; bool isST3 = false; bool isSchism = false; const int32 schismDateVersion = SchismTrackerEpoch + ((fileHeader.cwtv == 0x4FFF) ? fileHeader.reserved2 : (fileHeader.cwtv - 0x4050)); switch(fileHeader.cwtv & S3MFileHeader::trackerMask) { case S3MFileHeader::trkAkord & S3MFileHeader::trackerMask: if(fileHeader.cwtv == S3MFileHeader::trkAkord) madeWithTracker = U_("Akord"); break; case S3MFileHeader::trkScreamTracker: if(fileHeader.cwtv == S3MFileHeader::trkST3_20 && fileHeader.special == 0 && (fileHeader.ordNum & 0x0F) == 0 && fileHeader.ultraClicks == 0 && (fileHeader.flags & ~0x50) == 0) { // MPT and OpenMPT before 1.17.03.02 - Simply keep default (filter) MIDI macros if((fileHeader.masterVolume & 0x80) != 0) { m_dwLastSavedWithVersion = MPT_V("1.16.00.00"); madeWithTracker = U_("ModPlug Tracker / OpenMPT 1.17"); } else { // MPT 1.0 alpha5 doesn't set the stereo flag, but MPT 1.0 beta1 does. m_dwLastSavedWithVersion = MPT_V("1.00.00.00"); madeWithTracker = U_("ModPlug Tracker 1.0 alpha"); } keepMidiMacros = true; nonCompatTracker = true; m_playBehaviour.set(kST3LimitPeriod); } else if(fileHeader.cwtv == S3MFileHeader::trkST3_20 && fileHeader.special == 0 && fileHeader.ultraClicks == 0 && fileHeader.flags == 0 && fileHeader.usePanningTable == 0) { madeWithTracker = U_("Velvet Studio"); } else { // ST3.20 should only ever write ultra-click values 16, 24 and 32 (corresponding to 8, 12 and 16 in the GUI), ST3.01/3.03 should only write 0, // though several ST3.01/3.03 files with ultra-click values of 16 have been found as well. // However, we won't fingerprint these values here as it's unlikely that there is any other tracker out there disguising as ST3 and using a strange ultra-click value. // Also, re-saving a file with a strange ultra-click value in ST3 doesn't fix this value unless the user manually changes it, or if it's below 16. madeWithTracker = U_("Scream Tracker"); formatTrackerStr = true; isST3 = true; } break; case S3MFileHeader::trkImagoOrpheus: madeWithTracker = U_("Imago Orpheus"); formatTrackerStr = true; nonCompatTracker = true; break; case S3MFileHeader::trkImpulseTracker: if(fileHeader.cwtv <= S3MFileHeader::trkIT2_14) { madeWithTracker = U_("Impulse Tracker"); formatTrackerStr = true; } else { madeWithTracker = MPT_UFORMAT("Impulse Tracker 2.14p{}")(fileHeader.cwtv - S3MFileHeader::trkIT2_14); } if(fileHeader.cwtv >= S3MFileHeader::trkIT2_07 && fileHeader.reserved3 != 0) { // Starting from version 2.07, IT stores the total edit time of a module in the "reserved" field uint32 editTime = DecodeITEditTimer(fileHeader.cwtv, fileHeader.reserved3); FileHistory hist; hist.openTime = static_cast(editTime * (HISTORY_TIMER_PRECISION / 18.2)); m_FileHistory.push_back(hist); } nonCompatTracker = true; m_playBehaviour.set(kPeriodsAreHertz); m_playBehaviour.set(kITRetrigger); m_playBehaviour.set(kITShortSampleRetrig); m_playBehaviour.set(kST3SampleSwap); // Not exactly like ST3, but close enough m_nMinPeriod = 1; break; case S3MFileHeader::trkSchismTracker: if(fileHeader.cwtv == S3MFileHeader::trkBeRoTrackerOld) { madeWithTracker = U_("BeRoTracker"); m_playBehaviour.set(kST3LimitPeriod); } else { madeWithTracker = GetSchismTrackerVersion(fileHeader.cwtv, fileHeader.reserved2); m_nMinPeriod = 1; isSchism = true; if(schismDateVersion >= SchismVersionFromDate<2021, 05, 02>::date) m_playBehaviour.set(kPeriodsAreHertz); if(schismDateVersion >= SchismVersionFromDate<2016, 05, 13>::date) m_playBehaviour.set(kITShortSampleRetrig); } nonCompatTracker = true; break; case S3MFileHeader::trkOpenMPT: { uint32 mptVersion = (fileHeader.cwtv & S3MFileHeader::versionMask) << 16; if(mptVersion >= 0x01'29'00'00) mptVersion |= fileHeader.reserved2; m_dwLastSavedWithVersion = Version(mptVersion); madeWithTracker = U_("OpenMPT ") + mpt::ufmt::val(m_dwLastSavedWithVersion); } break; case S3MFileHeader::trkBeRoTracker: madeWithTracker = U_("BeRoTracker"); m_playBehaviour.set(kST3LimitPeriod); break; case S3MFileHeader::trkCreamTracker: madeWithTracker = U_("CreamTracker"); break; default: if(fileHeader.cwtv == S3MFileHeader::trkCamoto) madeWithTracker = U_("Camoto"); break; } if(formatTrackerStr) { madeWithTracker = MPT_UFORMAT("{} {}.{}")(madeWithTracker, (fileHeader.cwtv & 0xF00) >> 8, mpt::ufmt::hex0<2>(fileHeader.cwtv & 0xFF)); } m_modFormat.formatName = U_("Scream Tracker 3"); m_modFormat.type = U_("s3m"); m_modFormat.madeWithTracker = std::move(madeWithTracker); m_modFormat.charset = m_dwLastSavedWithVersion ? mpt::Charset::Windows1252 : mpt::Charset::CP437; if(nonCompatTracker) { m_playBehaviour.reset(kST3NoMutedChannels); m_playBehaviour.reset(kST3EffectMemory); m_playBehaviour.reset(kST3PortaSampleChange); m_playBehaviour.reset(kST3VibratoMemory); m_playBehaviour.reset(KST3PortaAfterArpeggio); m_playBehaviour.reset(kST3OffsetWithoutInstrument); m_playBehaviour.reset(kApplyUpperPeriodLimit); } if((fileHeader.cwtv & S3MFileHeader::trackerMask) > S3MFileHeader::trkScreamTracker) { if((fileHeader.cwtv & S3MFileHeader::trackerMask) != S3MFileHeader::trkImpulseTracker || fileHeader.cwtv >= S3MFileHeader::trkIT2_14) { // Keep MIDI macros if this is not an old IT version (BABYLON.S3M by Necros has Zxx commands and was saved with IT 2.05) keepMidiMacros = true; } } m_MidiCfg.Reset(); if(!keepMidiMacros) { // Remove macros so they don't interfere with tunes made in trackers that don't support Zxx m_MidiCfg.ClearZxxMacros(); } m_songName = mpt::String::ReadBuf(mpt::String::nullTerminated, fileHeader.name); if(fileHeader.flags & S3MFileHeader::amigaLimits) m_SongFlags.set(SONG_AMIGALIMITS); if(fileHeader.flags & S3MFileHeader::st2Vibrato) m_SongFlags.set(SONG_S3MOLDVIBRATO); if(fileHeader.cwtv == S3MFileHeader::trkST3_00 || (fileHeader.flags & S3MFileHeader::fastVolumeSlides) != 0) { m_SongFlags.set(SONG_FASTVOLSLIDES); } // Speed m_nDefaultSpeed = fileHeader.speed; if(m_nDefaultSpeed == 0 || (m_nDefaultSpeed == 255 && isST3)) { // Even though ST3 accepts the command AFF as expected, it mysteriously fails to load a default speed of 255... m_nDefaultSpeed = 6; } // Tempo m_nDefaultTempo.Set(fileHeader.tempo); if(fileHeader.tempo < 33) { // ST3 also fails to load an otherwise valid default tempo of 32... m_nDefaultTempo.Set(isST3 ? 125 : 32); } // Global Volume m_nDefaultGlobalVolume = std::min(fileHeader.globalVol.get(), uint8(64)) * 4u; // The following check is probably not very reliable, but it fixes a few tunes, e.g. // DARKNESS.S3M by Purple Motion (ST 3.00) and "Image of Variance" by C.C.Catch (ST 3.01): if(m_nDefaultGlobalVolume == 0 && fileHeader.cwtv < S3MFileHeader::trkST3_20) { m_nDefaultGlobalVolume = MAX_GLOBAL_VOLUME; } if(fileHeader.formatVersion == S3MFileHeader::oldVersion && fileHeader.masterVolume < 8) m_nSamplePreAmp = std::min((fileHeader.masterVolume + 1) * 0x10, 0x7F); // These changes were probably only supposed to be done for older format revisions, where supposedly 0x10 was the stereo flag. // However, this version check is missing in ST3, so any mono file with a master volume of 18 will be converted to a stereo file with master volume 32. else if(fileHeader.masterVolume == 2 || fileHeader.masterVolume == (2 | 0x10)) m_nSamplePreAmp = 0x20; else if(!(fileHeader.masterVolume & 0x7F)) m_nSamplePreAmp = 48; else m_nSamplePreAmp = std::max(fileHeader.masterVolume & 0x7F, 0x10); // Bit 7 = Stereo (we always use stereo) const bool isStereo = (fileHeader.masterVolume & 0x80) != 0 || m_dwLastSavedWithVersion; if(!isStereo) m_nSamplePreAmp = Util::muldivr_unsigned(m_nSamplePreAmp, 8, 11); // Approximately as loud as in DOSBox and a real SoundBlaster 16 m_nVSTiVolume = 36; if(isSchism && schismDateVersion < SchismVersionFromDate<2018, 11, 12>::date) m_nVSTiVolume = 64; // Channel setup m_nChannels = 4; std::bitset<32> isAdlibChannel; for(CHANNELINDEX i = 0; i < 32; i++) { ChnSettings[i].Reset(); uint8 ctype = fileHeader.channels[i] & ~0x80; if(fileHeader.channels[i] != 0xFF) { m_nChannels = i + 1; if(isStereo) ChnSettings[i].nPan = (ctype & 8) ? 0xCC : 0x33; // 200 : 56 } if(fileHeader.channels[i] & 0x80) { ChnSettings[i].dwFlags = CHN_MUTE; } if(ctype >= 16 && ctype <= 29) { // Adlib channel - except for OpenMPT 1.19 and older, which would write wrong channel types for PCM channels 16-32. // However, MPT/OpenMPT always wrote the extra panning table, so there is no need to consider this here. ChnSettings[i].nPan = 128; isAdlibChannel[i] = true; } } if(m_nChannels < 1) { m_nChannels = 1; } ReadOrderFromFile(Order(), file, fileHeader.ordNum, 0xFF, 0xFE); // Read sample header offsets std::vector sampleOffsets; file.ReadVector(sampleOffsets, fileHeader.smpNum); // Read pattern offsets std::vector patternOffsets; file.ReadVector(patternOffsets, fileHeader.patNum); // Read extended channel panning if(fileHeader.usePanningTable == S3MFileHeader::idPanning) { uint8 pan[32]; file.ReadArray(pan); for(CHANNELINDEX i = 0; i < 32; i++) { if((pan[i] & 0x20) != 0 && (!isST3 || !isAdlibChannel[i])) { ChnSettings[i].nPan = (static_cast(pan[i] & 0x0F) * 256 + 8) / 15; } } } // Reading sample headers m_nSamples = std::min(static_cast(fileHeader.smpNum), static_cast(MAX_SAMPLES - 1)); bool anySamples = false; uint16 gusAddresses = 0; for(SAMPLEINDEX smp = 0; smp < m_nSamples; smp++) { S3MSampleHeader sampleHeader; if(!file.Seek(sampleOffsets[smp] * 16) || !file.ReadStruct(sampleHeader)) { continue; } sampleHeader.ConvertToMPT(Samples[smp + 1], isST3); m_szNames[smp + 1] = mpt::String::ReadBuf(mpt::String::nullTerminated, sampleHeader.name); if(sampleHeader.sampleType < S3MSampleHeader::typeAdMel) { const uint32 sampleOffset = sampleHeader.GetSampleOffset(); if((loadFlags & loadSampleData) && sampleHeader.length != 0 && file.Seek(sampleOffset)) { sampleHeader.GetSampleFormat((fileHeader.formatVersion == S3MFileHeader::oldVersion)).ReadSample(Samples[smp + 1], file); anySamples = true; } gusAddresses |= sampleHeader.gusAddress; } } if(isST3 && anySamples && !gusAddresses && fileHeader.cwtv != S3MFileHeader::trkST3_00) { // All Scream Tracker versions except for some probably early revisions of Scream Tracker 3.00 write GUS addresses. GUS support might not have existed at that point (1992). // Hence if a file claims to be written with ST3 (but not ST3.00), but has no GUS addresses, we deduce that it must be written by some other software (e.g. some PSM -> S3M conversions) isST3 = false; m_modFormat.madeWithTracker = U_("Unknown"); } else if(isST3) { // Saving an S3M file in ST3 with the Gravis Ultrasound driver loaded will write a unique GUS memory address for each non-empty sample slot (and 0 for unused slots). // Re-saving that file in ST3 with the SoundBlaster driver loaded will reset the GUS address for all samples to 0 (unused) or 1 (used). // The first used sample will also have an address of 1 with the GUS driver. // So this is a safe way of telling if the file was last saved with the GUS driver loaded or not if there's more than one sample. const bool useGUS = gusAddresses > 1; m_playBehaviour.set(kST3PortaSampleChange, useGUS); m_playBehaviour.set(kST3SampleSwap, !useGUS); m_playBehaviour.set(kITShortSampleRetrig, !useGUS); // Only half the truth but close enough for now m_modFormat.madeWithTracker += useGUS ? UL_(" (GUS)") : UL_(" (SB)"); // ST3's GUS driver doesn't use this value. Ignoring it fixes the balance between FM and PCM samples (e.g. in Rotagilla by Manwe) if(useGUS) m_nSamplePreAmp = 48; } // Try to find out if Zxx commands are supposed to be panning commands (PixPlay). // Actually I am only aware of one module that uses this panning style, namely "Crawling Despair" by $volkraq // and I have no idea what PixPlay is, so this code is solely based on the sample text of that module. // We won't convert if there are not enough Zxx commands, too "high" Zxx commands // or there are only "left" or "right" pannings (we assume that stereo should be somewhat balanced), // and modules not made with an old version of ST3 were probably made in a tracker that supports panning anyway. bool pixPlayPanning = (fileHeader.cwtv < S3MFileHeader::trkST3_20); int zxxCountRight = 0, zxxCountLeft = 0; // Reading patterns if(!(loadFlags & loadPatternData)) { return true; } // Order list cannot contain pattern indices > 255, so do not even try to load higher patterns const PATTERNINDEX readPatterns = std::min(static_cast(fileHeader.patNum), static_cast(uint8_max)); Patterns.ResizeArray(readPatterns); for(PATTERNINDEX pat = 0; pat < readPatterns; pat++) { // A zero parapointer indicates an empty pattern. if(!Patterns.Insert(pat, 64) || patternOffsets[pat] == 0 || !file.Seek(patternOffsets[pat] * 16)) { continue; } // Skip pattern length indication. // Some modules, for example http://aminet.net/mods/8voic/s3m_hunt.lha seem to have a wrong pattern length - // If you strictly adhere the pattern length, you won't read some patterns (e.g. 17) correctly in that module. // It's most likely a broken copy because there are other versions of the track which don't have this issue. // Still, we don't really need this information, so we just ignore it. file.Skip(2); // Read pattern data ROWINDEX row = 0; PatternRow rowBase = Patterns[pat].GetRow(0); while(row < 64) { uint8 info = file.ReadUint8(); if(info == s3mEndOfRow) { // End of row if(++row < 64) { rowBase = Patterns[pat].GetRow(row); } continue; } CHANNELINDEX channel = (info & s3mChannelMask); ModCommand dummy; ModCommand &m = (channel < GetNumChannels()) ? rowBase[channel] : dummy; if(info & s3mNotePresent) { const auto [note, instr] = file.ReadArray(); if(note < 0xF0) m.note = static_cast(Clamp((note & 0x0F) + 12 * (note >> 4) + 12 + NOTE_MIN, NOTE_MIN, NOTE_MAX)); else if(note == s3mNoteOff) m.note = NOTE_NOTECUT; else if(note == s3mNoteNone) m.note = NOTE_NONE; m.instr = instr; } if(info & s3mVolumePresent) { uint8 volume = file.ReadUint8(); if(volume >= 128 && volume <= 192) { m.volcmd = VOLCMD_PANNING; m.vol = volume - 128; } else { m.volcmd = VOLCMD_VOLUME; m.vol = std::min(volume, uint8(64)); } } if(info & s3mEffectPresent) { const auto [command, param] = file.ReadArray(); m.command = command; m.param = param; S3MConvert(m, false); if(m.command == CMD_S3MCMDEX && (m.param & 0xF0) == 0xA0 && fileHeader.cwtv < S3MFileHeader::trkST3_20) { // Convert old SAx panning to S8x (should only be found in PANIC.S3M by Purple Motion) m.param = 0x80 | ((m.param & 0x0F) ^ 8); } else if(m.command == CMD_MIDI) { // PixPlay panning test if(m.param > 0x0F) { // PixPlay has Z00 to Z0F panning, so we ignore this. pixPlayPanning = false; } else { if(m.param < 0x08) zxxCountLeft++; else if(m.param > 0x08) zxxCountRight++; } } else if(m.command == CMD_OFFSET && m.param == 0 && fileHeader.cwtv <= S3MFileHeader::trkST3_01) { // Offset command didn't have effect memory in ST3.01; fixed in ST3.03 m.command = CMD_DUMMY; } } } } if(pixPlayPanning && zxxCountLeft + zxxCountRight >= m_nChannels && (-zxxCountLeft + zxxCountRight) < static_cast(m_nChannels)) { // There are enough Zxx commands, so let's assume this was made to be played with PixPlay Patterns.ForEachModCommand([](ModCommand &m) { if(m.command == CMD_MIDI) { m.command = CMD_S3MCMDEX; m.param |= 0x80; } }); } return true; } #ifndef MODPLUG_NO_FILESAVE bool CSoundFile::SaveS3M(std::ostream &f) const { static constexpr uint8 filler[16] = { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, }; if(m_nChannels == 0) { return false; } const bool saveMuteStatus = #ifdef MODPLUG_TRACKER TrackerSettings::Instance().MiscSaveChannelMuteStatus; #else true; #endif S3MFileHeader fileHeader; MemsetZero(fileHeader); mpt::String::WriteBuf(mpt::String::nullTerminated, fileHeader.name) = m_songName; fileHeader.dosEof = S3MFileHeader::idEOF; fileHeader.fileType = S3MFileHeader::idS3MType; // Orders ORDERINDEX writeOrders = Order().GetLengthTailTrimmed(); if(writeOrders < 2) { writeOrders = 2; } else if((writeOrders % 2u) != 0) { // Number of orders should be even writeOrders++; } LimitMax(writeOrders, static_cast(256)); fileHeader.ordNum = static_cast(writeOrders); // Samples SAMPLEINDEX writeSamples = static_cast(GetNumInstruments()); if(writeSamples == 0) { writeSamples = GetNumSamples(); } writeSamples = Clamp(writeSamples, static_cast(1), static_cast(99)); fileHeader.smpNum = static_cast(writeSamples); // Patterns PATTERNINDEX writePatterns = std::min(Patterns.GetNumPatterns(), PATTERNINDEX(100)); fileHeader.patNum = static_cast(writePatterns); // Flags if(m_SongFlags[SONG_FASTVOLSLIDES]) { fileHeader.flags |= S3MFileHeader::fastVolumeSlides; } if(m_nMaxPeriod < 20000 || m_SongFlags[SONG_AMIGALIMITS]) { fileHeader.flags |= S3MFileHeader::amigaLimits; } if(m_SongFlags[SONG_S3MOLDVIBRATO]) { fileHeader.flags |= S3MFileHeader::st2Vibrato; } // Version info following: ST3.20 = 0x1320 // Most significant nibble = Tracker ID, see S3MFileHeader::S3MTrackerVersions // Following: One nibble = Major version, one byte = Minor version (hex) const uint32 mptVersion = Version::Current().GetRawVersion(); fileHeader.cwtv = S3MFileHeader::trkOpenMPT | static_cast((mptVersion >> 16) & S3MFileHeader::versionMask); fileHeader.reserved2 = static_cast(mptVersion); fileHeader.formatVersion = S3MFileHeader::newVersion; memcpy(fileHeader.magic, "SCRM", 4); // Song Variables fileHeader.globalVol = static_cast(std::min(m_nDefaultGlobalVolume / 4u, uint32(64))); fileHeader.speed = static_cast(Clamp(m_nDefaultSpeed, 1u, 254u)); fileHeader.tempo = static_cast(Clamp(m_nDefaultTempo.GetInt(), 33u, 255u)); fileHeader.masterVolume = static_cast(Clamp(m_nSamplePreAmp, 16u, 127u) | 0x80); fileHeader.ultraClicks = 16; fileHeader.usePanningTable = S3MFileHeader::idPanning; mpt::IO::Write(f, fileHeader); Order().WriteAsByte(f, writeOrders); // Comment about parapointers stolen from Schism Tracker: // The sample data parapointers are 24+4 bits, whereas pattern data and sample headers are only 16+4 // bits -- so while the sample data can be written up to 268 MB within the file (starting at 0xffffff0), // the pattern data and sample headers are restricted to the first 1 MB (starting at 0xffff0). In effect, // this practically requires the sample data to be written last in the file, as it is entirely possible // (and quite easy, even) to write more than 1 MB of sample data in a file. // The "practical standard order" listed in TECH.DOC is sample headers, patterns, then sample data. // Calculate offset of first sample header... mpt::IO::Offset sampleHeaderOffset = mpt::IO::TellWrite(f) + (writeSamples + writePatterns) * 2 + 32; // ...which must be a multiple of 16, because parapointers omit the lowest 4 bits. sampleHeaderOffset = (sampleHeaderOffset + 15) & ~15; std::vector sampleOffsets(writeSamples); for(SAMPLEINDEX smp = 0; smp < writeSamples; smp++) { static_assert((sizeof(S3MSampleHeader) % 16) == 0); sampleOffsets[smp] = static_cast((sampleHeaderOffset + smp * sizeof(S3MSampleHeader)) / 16); } mpt::IO::Write(f, sampleOffsets); mpt::IO::Offset patternPointerOffset = mpt::IO::TellWrite(f); mpt::IO::Offset firstPatternOffset = sampleHeaderOffset + writeSamples * sizeof(S3MSampleHeader); std::vector patternOffsets(writePatterns); // Need to calculate the real offsets later. mpt::IO::Write(f, patternOffsets); // Write channel panning uint8 chnPan[32]; for(CHANNELINDEX chn = 0; chn < 32; chn++) { if(chn < GetNumChannels()) chnPan[chn] = static_cast(((ChnSettings[chn].nPan * 15 + 128) / 256) | 0x20); else chnPan[chn] = 0x08; } mpt::IO::Write(f, chnPan); // Do we need to fill up the file with some padding bytes for 16-Byte alignment? mpt::IO::Offset curPos = mpt::IO::TellWrite(f); if(curPos < sampleHeaderOffset) { MPT_ASSERT(sampleHeaderOffset - curPos < 16); mpt::IO::WriteRaw(f, filler, static_cast(sampleHeaderOffset - curPos)); } // Don't write sample headers for now, we are lacking the sample offset data. mpt::IO::SeekAbsolute(f, firstPatternOffset); // Write patterns enum class S3MChannelType : uint8 { kUnused = 0, kPCM = 1, kAdlib = 2 }; FlagSet channelType[32] = { S3MChannelType::kUnused }; bool globalCmdOnMutedChn = false; for(PATTERNINDEX pat = 0; pat < writePatterns; pat++) { if(Patterns.IsPatternEmpty(pat)) { patternOffsets[pat] = 0; continue; } mpt::IO::Offset patOffset = mpt::IO::TellWrite(f); if(patOffset > 0xFFFF0) { AddToLog(LogError, MPT_UFORMAT("Too much pattern data! Writing patterns failed starting from pattern {}.")(pat)); break; } MPT_ASSERT((patOffset % 16) == 0); patternOffsets[pat] = static_cast(patOffset / 16); std::vector buffer; buffer.reserve(5 * 1024); // Reserve space for length bytes buffer.resize(2, 0); if(Patterns.IsValidPat(pat)) { for(ROWINDEX row = 0; row < 64; row++) { if(row >= Patterns[pat].GetNumRows()) { // Invent empty row buffer.push_back(s3mEndOfRow); continue; } const PatternRow rowBase = Patterns[pat].GetRow(row); CHANNELINDEX writeChannels = std::min(CHANNELINDEX(32), GetNumChannels()); for(CHANNELINDEX chn = 0; chn < writeChannels; chn++) { const ModCommand &m = rowBase[chn]; uint8 info = static_cast(chn); uint8 note = m.note; ModCommand::VOLCMD volcmd = m.volcmd; uint8 vol = m.vol; uint8 command = m.command; uint8 param = m.param; if(note != NOTE_NONE || m.instr != 0) { info |= s3mNotePresent; if(note == NOTE_NONE) { note = s3mNoteNone; } else if(ModCommand::IsSpecialNote(note)) { // Note Cut note = s3mNoteOff; } else if(note < 12 + NOTE_MIN) { // Too low note = 0; } else if(note <= NOTE_MAX) { note -= (12 + NOTE_MIN); note = (note % 12) + ((note / 12) << 4); } if(m.instr > 0 && m.instr <= GetNumSamples()) { const ModSample &smp = Samples[m.instr]; if(smp.uFlags[CHN_ADLIB]) channelType[chn].set(S3MChannelType::kAdlib); else if(smp.HasSampleData()) channelType[chn].set(S3MChannelType::kPCM); } } if(command == CMD_VOLUME) { command = CMD_NONE; volcmd = VOLCMD_VOLUME; vol = std::min(param, uint8(64)); } if(volcmd == VOLCMD_VOLUME) { info |= s3mVolumePresent; } else if(volcmd == VOLCMD_PANNING) { info |= s3mVolumePresent; vol |= 0x80; } if(command != CMD_NONE) { S3MSaveConvert(command, param, false, true); if(command || param) { info |= s3mEffectPresent; if(saveMuteStatus && ChnSettings[chn].dwFlags[CHN_MUTE] && m.IsGlobalCommand()) { globalCmdOnMutedChn = true; } } } if(info & s3mAnyPresent) { buffer.push_back(info); if(info & s3mNotePresent) { buffer.push_back(note); buffer.push_back(m.instr); } if(info & s3mVolumePresent) { buffer.push_back(vol); } if(info & s3mEffectPresent) { buffer.push_back(command); buffer.push_back(param); } } } buffer.push_back(s3mEndOfRow); } } else { // Invent empty pattern buffer.insert(buffer.end(), 64, s3mEndOfRow); } uint16 length = mpt::saturate_cast(buffer.size()); buffer[0] = static_cast(length & 0xFF); buffer[1] = static_cast((length >> 8) & 0xFF); if((buffer.size() % 16u) != 0) { // Add padding bytes buffer.insert(buffer.end(), 16 - (buffer.size() % 16u), 0); } mpt::IO::Write(f, buffer); } if(globalCmdOnMutedChn) { //AddToLog(LogWarning, U_("Global commands on muted channels are interpreted only by some S3M players.")); } mpt::IO::Offset sampleDataOffset = mpt::IO::TellWrite(f); // Write samples std::vector sampleHeader(writeSamples); for(SAMPLEINDEX smp = 0; smp < writeSamples; smp++) { SAMPLEINDEX realSmp = smp + 1; if(GetNumInstruments() != 0 && Instruments[smp] != nullptr) { // Find some valid sample associated with this instrument. for(SAMPLEINDEX keySmp : Instruments[smp]->Keyboard) { if(keySmp > 0 && keySmp <= GetNumSamples()) { realSmp = keySmp; break; } } } if(realSmp > GetNumSamples()) { continue; } const SmpLength smpLength = sampleHeader[smp].ConvertToS3M(Samples[realSmp]); mpt::String::WriteBuf(mpt::String::nullTerminated, sampleHeader[smp].name) = m_szNames[realSmp]; if(smpLength != 0) { // Write sample data if(sampleDataOffset > 0xFFFFFF0) { AddToLog(LogError, MPT_UFORMAT("Too much sample data! Writing samples failed starting from sample {}.")(realSmp)); break; } sampleHeader[smp].dataPointer[1] = static_cast((sampleDataOffset >> 4) & 0xFF); sampleHeader[smp].dataPointer[2] = static_cast((sampleDataOffset >> 12) & 0xFF); sampleHeader[smp].dataPointer[0] = static_cast((sampleDataOffset >> 20) & 0xFF); size_t writtenLength = sampleHeader[smp].GetSampleFormat(false).WriteSample(f, Samples[realSmp], smpLength); sampleDataOffset += writtenLength; if((writtenLength % 16u) != 0) { size_t fillSize = 16 - (writtenLength % 16u); mpt::IO::WriteRaw(f, filler, fillSize); sampleDataOffset += fillSize; } } } // Channel Table uint8 sampleCh = 0, adlibCh = 0; for(CHANNELINDEX chn = 0; chn < 32; chn++) { if(chn < GetNumChannels()) { if(channelType[chn][S3MChannelType::kPCM] && channelType[chn][S3MChannelType::kAdlib]) { AddToLog(LogWarning, MPT_UFORMAT("Pattern channel {} constains both samples and OPL instruments, which is not supported by Scream Tracker 3.")(chn + 1)); } // ST3 only supports 16 PCM channels, so if channels 17-32 are used, // they must be mapped to the same "internal channels" as channels 1-16. // The channel indices determine in which order channels are evaluated in ST3. // First, the "left" channels (0...7) are evaluated, then the "right" channels (8...15). // Previously, an alternating LRLR scheme was written, which would lead to a different // effect processing in ST3 than LLL...RRR, but since OpenMPT doesn't care about the // channel order and always parses them left to right as they appear in the pattern, // we should just write in the LLL...RRR manner. uint8 ch = sampleCh % 16u; // If there are neither PCM nor AdLib instruments on this channel, just fall back a regular sample-based channel for maximum compatibility. if(channelType[chn][S3MChannelType::kPCM]) ch = (sampleCh++) % 16u; else if(channelType[chn][S3MChannelType::kAdlib]) ch = 16 + ((adlibCh++) % 9u); if(saveMuteStatus && ChnSettings[chn].dwFlags[CHN_MUTE]) { ch |= 0x80; } fileHeader.channels[chn] = ch; } else { fileHeader.channels[chn] = 0xFF; } } if(sampleCh > 16) { AddToLog(LogWarning, MPT_UFORMAT("This module has more than 16 ({}) sample channels, which is not supported by Scream Tracker 3.")(sampleCh)); } if(adlibCh > 9) { AddToLog(LogWarning, MPT_UFORMAT("This module has more than 9 ({}) OPL channels, which is not supported by Scream Tracker 3.")(adlibCh)); } mpt::IO::SeekAbsolute(f, 0); mpt::IO::Write(f, fileHeader); // Now we know where the patterns are. if(writePatterns != 0) { mpt::IO::SeekAbsolute(f, patternPointerOffset); mpt::IO::Write(f, patternOffsets); } // And we can finally write the sample headers. if(writeSamples != 0) { mpt::IO::SeekAbsolute(f, sampleHeaderOffset); mpt::IO::Write(f, sampleHeader); } return true; } #endif // MODPLUG_NO_FILESAVE OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Load_sfx.cpp0000644000175000017500000002555113707346470020361 00000000000000/* * Load_sfx.cpp * ------------ * Purpose: SFX / MMS (SoundFX / MultiMedia Sound) module loader * Notes : Mostly based on the Soundtracker loader, some effect behavior is based on Flod's implementation. * Authors: Devin Acker * OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Loaders.h" #include "Tables.h" OPENMPT_NAMESPACE_BEGIN // File Header struct SFXFileHeader { uint8be numOrders; uint8be restartPos; uint8be orderList[128]; }; MPT_BINARY_STRUCT(SFXFileHeader, 130) // Sample Header struct SFXSampleHeader { char name[22]; char dummy[2]; // Supposedly sample length, but almost always incorrect uint8be finetune; uint8be volume; uint16be loopStart; uint16be loopLength; // Convert an MOD sample header to OpenMPT's internal sample header. void ConvertToMPT(ModSample &mptSmp, uint32 length) const { mptSmp.Initialize(MOD_TYPE_MOD); mptSmp.nLength = length; mptSmp.nFineTune = MOD2XMFineTune(finetune); mptSmp.nVolume = 4u * std::min(volume.get(), uint8(64)); SmpLength lStart = loopStart; SmpLength lLength = loopLength * 2u; if(mptSmp.nLength) { mptSmp.nLoopStart = lStart; mptSmp.nLoopEnd = lStart + lLength; if(mptSmp.nLoopStart >= mptSmp.nLength) { mptSmp.nLoopStart = mptSmp.nLength - 1; } if(mptSmp.nLoopEnd > mptSmp.nLength) { mptSmp.nLoopEnd = mptSmp.nLength; } if(mptSmp.nLoopStart > mptSmp.nLoopEnd || mptSmp.nLoopEnd < 4 || mptSmp.nLoopEnd - mptSmp.nLoopStart < 4) { mptSmp.nLoopStart = 0; mptSmp.nLoopEnd = 0; } if(mptSmp.nLoopEnd > mptSmp.nLoopStart) { mptSmp.uFlags.set(CHN_LOOP); } } } }; MPT_BINARY_STRUCT(SFXSampleHeader, 30) static uint8 ClampSlideParam(uint8 value, uint8 lowNote, uint8 highNote) { uint16 lowPeriod, highPeriod; if(lowNote < highNote && lowNote >= 24 + NOTE_MIN && highNote >= 24 + NOTE_MIN && lowNote < std::size(ProTrackerPeriodTable) + 24 + NOTE_MIN && highNote < std::size(ProTrackerPeriodTable) + 24 + NOTE_MIN) { lowPeriod = ProTrackerPeriodTable[lowNote - 24 - NOTE_MIN]; highPeriod = ProTrackerPeriodTable[highNote - 24 - NOTE_MIN]; // with a fixed speed of 6 ticks/row, and excluding the first row, // 1xx/2xx param has a max value of (low-high)/5 to avoid sliding too far return std::min(value, static_cast((lowPeriod - highPeriod) / 5)); } return 0; } static bool ValidateHeader(const SFXFileHeader &fileHeader) { if(fileHeader.numOrders > 128) { return false; } return true; } CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderSFX(MemoryFileReader file, const uint64 *pfilesize) { SAMPLEINDEX numSamples = 0; if(numSamples == 0) { file.Rewind(); if(!file.CanRead(0x40)) { return ProbeWantMoreData; } if(file.Seek(0x3c) && file.ReadMagic("SONG")) { numSamples = 15; } } if(numSamples == 0) { file.Rewind(); if(!file.CanRead(0x80)) { return ProbeWantMoreData; } if(file.Seek(0x7C) && file.ReadMagic("SO31")) { numSamples = 31; } } if(numSamples == 0) { return ProbeFailure; } file.Rewind(); for(SAMPLEINDEX smp = 0; smp < numSamples; smp++) { if(file.ReadUint32BE() > 131072) { return ProbeFailure; } } file.Skip(4); if(!file.CanRead(2)) { return ProbeWantMoreData; } uint16 speed = file.ReadUint16BE(); if(speed < 178) { return ProbeFailure; } if(!file.CanRead(sizeof(SFXSampleHeader) * numSamples)) { return ProbeWantMoreData; } file.Skip(sizeof(SFXSampleHeader) * numSamples); SFXFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return ProbeWantMoreData; } if(!ValidateHeader(fileHeader)) { return ProbeFailure; } MPT_UNREFERENCED_PARAMETER(pfilesize); return ProbeSuccess; } bool CSoundFile::ReadSFX(FileReader &file, ModLoadingFlags loadFlags) { if(file.Seek(0x3C), file.ReadMagic("SONG")) { InitializeGlobals(MOD_TYPE_SFX); m_nSamples = 15; } else if(file.Seek(0x7C), file.ReadMagic("SO31")) { InitializeGlobals(MOD_TYPE_SFX); m_nSamples = 31; } else { return false; } uint32 sampleLen[31]; file.Rewind(); for(SAMPLEINDEX smp = 0; smp < m_nSamples; smp++) { sampleLen[smp] = file.ReadUint32BE(); if(sampleLen[smp] > 131072) return false; } m_nChannels = 4; m_nInstruments = 0; m_nDefaultSpeed = 6; m_nMinPeriod = 14 * 4; m_nMaxPeriod = 3424 * 4; m_nSamplePreAmp = 64; // Setup channel pan positions and volume SetupMODPanning(true); file.Skip(4); uint16 speed = file.ReadUint16BE(); if(speed < 178) return false; m_nDefaultTempo = TEMPO((14565.0 * 122.0) / speed); file.Skip(14); uint32 invalidChars = 0; for(SAMPLEINDEX smp = 1; smp <= m_nSamples; smp++) { SFXSampleHeader sampleHeader; file.ReadStruct(sampleHeader); sampleHeader.ConvertToMPT(Samples[smp], sampleLen[smp - 1]); // Get rid of weird characters in sample names. for(char &c : sampleHeader.name) { if(c > 0 && c < ' ') { c = ' '; invalidChars++; } } if(invalidChars >= 128) return false; m_szNames[smp] = mpt::String::ReadBuf(mpt::String::spacePadded, sampleHeader.name); } // Broken conversions of the "Operation Stealth" soundtrack (BOND23 / BOND32) // There is a converter that shifts all note values except FFFD (empty note) to the left by 1 bit, // but it should not do that for FFFE (STP) notes - as a consequence, they turn into pattern breaks (FFFC). const bool fixPatternBreaks = (m_szNames[1] == "BASSE2.AMI") || (m_szNames[1] == "PRA1.AMI"); SFXFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return false; } if(!ValidateHeader(fileHeader)) { return false; } if(loadFlags == onlyVerifyHeader) { return true; } PATTERNINDEX numPatterns = 0; for(ORDERINDEX ord = 0; ord < fileHeader.numOrders; ord++) { numPatterns = std::max(numPatterns, static_cast(fileHeader.orderList[ord] + 1)); } if(fileHeader.restartPos < fileHeader.numOrders) Order().SetRestartPos(fileHeader.restartPos); else Order().SetRestartPos(0); ReadOrderFromArray(Order(), fileHeader.orderList, fileHeader.numOrders); // SFX v2 / MMS modules have 4 extra bytes here for some reason if(m_nSamples == 31) file.Skip(4); uint8 lastNote[4] = {0}; uint8 slideTo[4] = {0}; uint8 slideRate[4] = {0}; uint8 version = 0; // Reading patterns if(loadFlags & loadPatternData) Patterns.ResizeArray(numPatterns); for(PATTERNINDEX pat = 0; pat < numPatterns; pat++) { if(!(loadFlags & loadPatternData) || !Patterns.Insert(pat, 64)) { file.Skip(64 * 4 * 4); continue; } for(ROWINDEX row = 0; row < 64; row++) { PatternRow rowBase = Patterns[pat].GetpModCommand(row, 0); for(CHANNELINDEX chn = 0; chn < 4; chn++) { ModCommand &m = rowBase[chn]; auto data = file.ReadArray(); if(data[0] == 0xFF) { lastNote[chn] = slideRate[chn] = 0; if(fixPatternBreaks && data[1] == 0xFC) data[1] = 0xFE; switch(data[1]) { case 0xFE: // STP (note cut) m.command = CMD_VOLUME; continue; case 0xFD: // PIC (null) continue; case 0xFC: // BRK (pattern break) m.command = CMD_PATTERNBREAK; version = 9; continue; } } ReadMODPatternEntry(data, m); if(m.note != NOTE_NONE) { lastNote[chn] = m.note; slideRate[chn] = 0; if(m.note < NOTE_MIDDLEC - 12) { version = std::max(version, uint8(8)); } } if(m.command || m.param) { switch(m.command) { case 0x1: // Arpeggio m.command = CMD_ARPEGGIO; break; case 0x2: // Portamento (like Ultimate Soundtracker) if(m.param & 0xF0) { m.command = CMD_PORTAMENTODOWN; m.param >>= 4; } else if(m.param & 0xF) { m.command = CMD_PORTAMENTOUP; m.param &= 0x0F; } else { m.command = m.param = 0; } break; case 0x3: // Enable LED filter // Give precedence to 7xy/8xy slides if(slideRate[chn]) { m.command = m.param = 0; break; } m.command = CMD_MODCMDEX; m.param = 0; break; case 0x4: // Disable LED filter // Give precedence to 7xy/8xy slides if(slideRate[chn]) { m.command = m.param = 0; break; } m.command = CMD_MODCMDEX; m.param = 1; break; case 0x5: // Increase volume if(m.instr) { m.command = CMD_VOLUME; m.param = std::min(ModCommand::PARAM(0x3F), static_cast((Samples[m.instr].nVolume / 4u) + m.param)); // Give precedence to 7xy/8xy slides (and move this to the volume column) if(slideRate[chn]) { m.volcmd = VOLCMD_VOLUME; m.vol = m.param; m.command = m.param = 0; break; } } else { m.command = m.param = 0; } break; case 0x6: // Decrease volume if(m.instr) { m.command = CMD_VOLUME; if((Samples[m.instr].nVolume / 4u) >= m.param) m.param = static_cast(Samples[m.instr].nVolume / 4u) - m.param; else m.param = 0; // Give precedence to 7xy/8xy slides (and move this to the volume column) if(slideRate[chn]) { m.volcmd = VOLCMD_VOLUME; m.vol = m.param; m.command = m.param = 0; break; } } else { m.command = m.param = 0; } break; case 0x7: // 7xy: Slide down x semitones at speed y slideTo[chn] = lastNote[chn] - (m.param >> 4); m.command = CMD_PORTAMENTODOWN; slideRate[chn] = m.param & 0xF; m.param = ClampSlideParam(slideRate[chn], slideTo[chn], lastNote[chn]); break; case 0x8: // 8xy: Slide up x semitones at speed y slideTo[chn] = lastNote[chn] + (m.param >> 4); m.command = CMD_PORTAMENTOUP; slideRate[chn] = m.param & 0xF; m.param = ClampSlideParam(slideRate[chn], lastNote[chn], slideTo[chn]); break; case 0x9: // 9xy: Auto slide version = std::max(version, uint8(8)); [[fallthrough]]; default: m.command = CMD_NONE; break; } } // Continue 7xy/8xy slides if needed if(m.command == CMD_NONE && slideRate[chn]) { if(slideTo[chn]) { m.note = lastNote[chn] = slideTo[chn]; m.param = slideRate[chn]; slideTo[chn] = 0; } m.command = CMD_TONEPORTAMENTO; } } } } // Reading samples if(loadFlags & loadSampleData) { for(SAMPLEINDEX smp = 1; smp <= m_nSamples; smp++) if(Samples[smp].nLength) { SampleIO( SampleIO::_8bit, SampleIO::mono, SampleIO::littleEndian, SampleIO::signedPCM) .ReadSample(Samples[smp], file); } } m_modFormat.formatName = m_nSamples == 15 ? MPT_UFORMAT("SoundFX 1.{}")(version) : U_("SoundFX 2.0 / MultiMedia Sound"); m_modFormat.type = m_nSamples == 15 ? UL_("sfx") : UL_("sfx2"); m_modFormat.charset = mpt::Charset::ISO8859_1; return true; } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Load_stm.cpp0000644000175000017500000004173014147753013020353 00000000000000/* * Load_stm.cpp * ------------ * Purpose: STM (Scream Tracker 2) and STX (Scream Tracker Music Interface Kit - a mixture of STM and S3M) module loaders * Notes : (currently none) * Authors: Olivier Lapicque * OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Loaders.h" #include "S3MTools.h" OPENMPT_NAMESPACE_BEGIN // STM sample header struct struct STMSampleHeader { char filename[12]; // Can't have long comments - just filename comments :) uint8le zero; uint8le disk; // A blast from the past uint16le offset; // 20-bit offset in file (lower 4 bits are zero) uint16le length; // Sample length uint16le loopStart; // Loop start point uint16le loopEnd; // Loop end point uint8le volume; // Volume uint8le reserved2; uint16le sampleRate; uint8le reserved3[6]; // Convert an STM sample header to OpenMPT's internal sample header. void ConvertToMPT(ModSample &mptSmp) const { mptSmp.Initialize(); mptSmp.filename = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, filename); mptSmp.nC5Speed = sampleRate; mptSmp.nVolume = std::min(volume.get(), uint8(64)) * 4; mptSmp.nLength = length; mptSmp.nLoopStart = loopStart; mptSmp.nLoopEnd = loopEnd; if(mptSmp.nLength < 2) mptSmp.nLength = 0; if(mptSmp.nLoopStart < mptSmp.nLength && mptSmp.nLoopEnd > mptSmp.nLoopStart && mptSmp.nLoopEnd != 0xFFFF) { mptSmp.uFlags = CHN_LOOP; mptSmp.nLoopEnd = std::min(mptSmp.nLoopEnd, mptSmp.nLength); } } }; MPT_BINARY_STRUCT(STMSampleHeader, 32) // STM file header struct STMFileHeader { char songname[20]; char trackerName[8]; // !Scream! for ST 2.xx uint8 dosEof; // 0x1A uint8 filetype; // 1=song, 2=module (only 2 is supported, of course) :) uint8 verMajor; uint8 verMinor; uint8 initTempo; uint8 numPatterns; uint8 globalVolume; uint8 reserved[13]; bool Validate() const { if(filetype != 2 || (dosEof != 0x1A && dosEof != 2) // ST2 ignores this, ST3 doesn't. Broken versions of putup10.stm / putup11.stm have dosEof = 2. || verMajor != 2 || (verMinor != 0 && verMinor != 10 && verMinor != 20 && verMinor != 21) || numPatterns > 64 || (globalVolume > 64 && globalVolume != 0x58)) // 0x58 may be a placeholder value in earlier ST2 versions. { return false; } return ValidateTrackerName(trackerName); } static bool ValidateTrackerName(const char (&trackerName)[8]) { // Tracker string can be anything really (ST2 and ST3 won't check it), // but we do not want to generate too many false positives here, as // STM already has very few magic bytes anyway. // Magic bytes that have been found in the wild are !Scream!, BMOD2STM, WUZAMOD! and SWavePro. for(uint8 c : trackerName) { if(c < 0x20 || c >= 0x7F) return false; } return true; } uint64 GetHeaderMinimumAdditionalSize() const { return 31 * sizeof(STMSampleHeader) + (verMinor == 0 ? 64 : 128) + numPatterns * 64 * 4; } }; MPT_BINARY_STRUCT(STMFileHeader, 48) static bool ValidateSTMOrderList(ModSequence &order) { for(auto &pat : order) { if(pat == 99 || pat == 255) // 99 is regular, sometimes a single 255 entry can be found too pat = order.GetInvalidPatIndex(); else if(pat > 63) return false; } return true; } static void ConvertSTMCommand(ModCommand &m, const ROWINDEX row, const uint8 fileVerMinor, uint8 &newTempo, ORDERINDEX &breakPos, ROWINDEX &breakRow) { static constexpr EffectCommand stmEffects[] = { CMD_NONE, CMD_SPEED, CMD_POSITIONJUMP, CMD_PATTERNBREAK, // .ABC CMD_VOLUMESLIDE, CMD_PORTAMENTODOWN, CMD_PORTAMENTOUP, CMD_TONEPORTAMENTO, // DEFG CMD_VIBRATO, CMD_TREMOR, CMD_ARPEGGIO, CMD_NONE, // HIJK CMD_NONE, CMD_NONE, CMD_NONE, CMD_NONE, // LMNO // KLMNO can be entered in the editor but don't do anything }; m.command = stmEffects[m.command & 0x0F]; switch(m.command) { case CMD_VOLUMESLIDE: // Lower nibble always has precedence, and there are no fine slides. if(m.param & 0x0F) m.param &= 0x0F; else m.param &= 0xF0; break; case CMD_PATTERNBREAK: m.param = (m.param & 0xF0) * 10 + (m.param & 0x0F); if(breakPos != ORDERINDEX_INVALID && m.param == 0) { // Merge Bxx + C00 into just Bxx m.command = CMD_POSITIONJUMP; m.param = static_cast(breakPos); breakPos = ORDERINDEX_INVALID; } LimitMax(breakRow, row); break; case CMD_POSITIONJUMP: // This effect is also very weird. // Bxx doesn't appear to cause an immediate break -- it merely // sets the next order for when the pattern ends (either by // playing it all the way through, or via Cxx effect) breakPos = m.param; breakRow = 63; m.command = CMD_NONE; break; case CMD_TREMOR: // this actually does something with zero values, and has no // effect memory. which makes SENSE for old-effects tremor, // but ST3 went and screwed it all up by adding an effect // memory and IT followed that, and those are much more popular // than STM so we kind of have to live with this effect being // broken... oh well. not a big loss. break; case CMD_SPEED: if(fileVerMinor < 21) m.param = ((m.param / 10u) << 4u) + m.param % 10u; if(!m.param) { m.command = CMD_NONE; break; } #ifdef MODPLUG_TRACKER // ST2 has a very weird tempo mode where the length of a tick depends both // on the ticks per row and a scaling factor. Try to write the tempo into a separate command. newTempo = m.param; m.param >>= 4; #else MPT_UNUSED_VARIABLE(newTempo); #endif // MODPLUG_TRACKER break; default: // Anything not listed above is a no-op if there's no value, as ST2 doesn't have effect memory. if(!m.param) m.command = CMD_NONE; break; } } CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderSTM(MemoryFileReader file, const uint64 *pfilesize) { STMFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) return ProbeWantMoreData; if(!fileHeader.Validate()) return ProbeFailure; return ProbeAdditionalSize(file, pfilesize, fileHeader.GetHeaderMinimumAdditionalSize()); } bool CSoundFile::ReadSTM(FileReader &file, ModLoadingFlags loadFlags) { file.Rewind(); STMFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) return false; if(!fileHeader.Validate()) return false; if(!file.CanRead(mpt::saturate_cast(fileHeader.GetHeaderMinimumAdditionalSize()))) return false; if(loadFlags == onlyVerifyHeader) return true; InitializeGlobals(MOD_TYPE_STM); m_songName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileHeader.songname); m_modFormat.formatName = U_("Scream Tracker 2"); m_modFormat.type = U_("stm"); m_modFormat.madeWithTracker = MPT_UFORMAT("Scream Tracker {}.{}")(fileHeader.verMajor, mpt::ufmt::dec0<2>(fileHeader.verMinor)); m_modFormat.charset = mpt::Charset::CP437; m_playBehaviour.set(kST3SampleSwap); m_nSamples = 31; m_nChannels = 4; m_nMinPeriod = 64; m_nMaxPeriod = 0x7FFF; m_playBehaviour.set(kST3SampleSwap); uint8 initTempo = fileHeader.initTempo; if(fileHeader.verMinor < 21) initTempo = ((initTempo / 10u) << 4u) + initTempo % 10u; if(initTempo == 0) initTempo = 0x60; m_nDefaultTempo = ConvertST2Tempo(initTempo); m_nDefaultSpeed = initTempo >> 4; if(fileHeader.verMinor > 10) m_nDefaultGlobalVolume = std::min(fileHeader.globalVolume, uint8(64)) * 4u; // Setting up channels for(CHANNELINDEX chn = 0; chn < 4; chn++) { ChnSettings[chn].Reset(); ChnSettings[chn].nPan = (chn & 1) ? 0x40 : 0xC0; } // Read samples uint16 sampleOffsets[31]; for(SAMPLEINDEX smp = 1; smp <= 31; smp++) { STMSampleHeader sampleHeader; file.ReadStruct(sampleHeader); if(sampleHeader.zero != 0 && sampleHeader.zero != 46) // putup10.stm has zero = 46 return false; sampleHeader.ConvertToMPT(Samples[smp]); m_szNames[smp] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, sampleHeader.filename); sampleOffsets[smp - 1] = sampleHeader.offset; } // Read order list ReadOrderFromFile(Order(), file, fileHeader.verMinor == 0 ? 64 : 128); if(!ValidateSTMOrderList(Order())) return false; if(loadFlags & loadPatternData) Patterns.ResizeArray(fileHeader.numPatterns); for(PATTERNINDEX pat = 0; pat < fileHeader.numPatterns; pat++) { if(!(loadFlags & loadPatternData) || !Patterns.Insert(pat, 64)) { for(int i = 0; i < 64 * 4; i++) { uint8 note = file.ReadUint8(); if(note < 0xFB || note > 0xFD) file.Skip(3); } continue; } auto m = Patterns[pat].begin(); ORDERINDEX breakPos = ORDERINDEX_INVALID; ROWINDEX breakRow = 63; // Candidate row for inserting pattern break for(ROWINDEX row = 0; row < 64; row++) { uint8 newTempo = 0; for(CHANNELINDEX chn = 0; chn < 4; chn++, m++) { uint8 note = file.ReadUint8(), insVol, volCmd, cmdInf; switch(note) { case 0xFB: note = insVol = volCmd = cmdInf = 0x00; break; case 0xFC: continue; case 0xFD: m->note = NOTE_NOTECUT; continue; default: { const auto patData = file.ReadArray(); insVol = patData[0]; volCmd = patData[1]; cmdInf = patData[2]; } break; } if(note == 0xFE) m->note = NOTE_NOTECUT; else if(note < 0x60) m->note = (note >> 4) * 12 + (note & 0x0F) + 36 + NOTE_MIN; m->instr = insVol >> 3; if(m->instr > 31) { m->instr = 0; } uint8 vol = (insVol & 0x07) | ((volCmd & 0xF0) >> 1); if(vol <= 64) { m->volcmd = VOLCMD_VOLUME; m->vol = vol; } m->command = volCmd & 0x0F; m->param = cmdInf; ConvertSTMCommand(*m, row, fileHeader.verMinor, newTempo, breakPos, breakRow); } if(newTempo != 0) { Patterns[pat].WriteEffect(EffectWriter(CMD_TEMPO, mpt::saturate_round(ConvertST2Tempo(newTempo).ToDouble())).Row(row).RetryPreviousRow()); } } if(breakPos != ORDERINDEX_INVALID) { Patterns[pat].WriteEffect(EffectWriter(CMD_POSITIONJUMP, static_cast(breakPos)).Row(breakRow).RetryPreviousRow()); } } // Reading Samples if(loadFlags & loadSampleData) { const SampleIO sampleIO( SampleIO::_8bit, SampleIO::mono, SampleIO::littleEndian, SampleIO::signedPCM); for(SAMPLEINDEX smp = 1; smp <= 31; smp++) { ModSample &sample = Samples[smp]; // ST2 just plays random noise for samples with a default volume of 0 if(sample.nLength && sample.nVolume > 0) { FileReader::off_t sampleOffset = sampleOffsets[smp - 1] << 4; // acidlamb.stm has some bogus samples with sample offsets past EOF if(sampleOffset > sizeof(STMFileHeader) && file.Seek(sampleOffset)) { sampleIO.ReadSample(sample, file); } } } } return true; } // STX file header struct STXFileHeader { char songName[20]; char trackerName[8]; // Typically !Scream! but mustn't be relied upon, like for STM uint16le patternSize; // or EOF in newer file version (except for future brain.stx?!) uint16le unknown1; uint16le patTableOffset; uint16le smpTableOffset; uint16le chnTableOffset; uint32le unknown2; uint8 globalVolume; uint8 initTempo; uint32le unknown3; uint16le numPatterns; uint16le numSamples; uint16le numOrders; char unknown4[6]; char magic[4]; bool Validate() const { if(std::memcmp(magic, "SCRM", 4) || (patternSize < 64 && patternSize != 0x1A) || patternSize > 0x840 || (globalVolume > 64 && globalVolume != 0x58) // 0x58 may be a placeholder value in earlier ST2 versions. || numPatterns > 64 || numSamples > 96 // Some STX files have more sample slots than their STM counterpart for mysterious reasons || (numOrders > 0x81 && numOrders != 0x101) || unknown1 != 0 || unknown2 != 0 || unknown3 != 1) { return false; } return STMFileHeader::ValidateTrackerName(trackerName); } uint64 GetHeaderMinimumAdditionalSize() const { return std::max({(patTableOffset << 4) + numPatterns * 2, (smpTableOffset << 4) + numSamples * 2, (chnTableOffset << 4) + 32 + numOrders * 5 }); } }; MPT_BINARY_STRUCT(STXFileHeader, 64) CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderSTX(MemoryFileReader file, const uint64 *pfilesize) { STXFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) return ProbeWantMoreData; if(!fileHeader.Validate()) return ProbeFailure; return ProbeAdditionalSize(file, pfilesize, fileHeader.GetHeaderMinimumAdditionalSize()); } bool CSoundFile::ReadSTX(FileReader &file, ModLoadingFlags loadFlags) { file.Rewind(); STXFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) return false; if(!fileHeader.Validate()) return false; if (!file.CanRead(mpt::saturate_cast(fileHeader.GetHeaderMinimumAdditionalSize()))) return false; if(loadFlags == onlyVerifyHeader) return true; InitializeGlobals(MOD_TYPE_STM); m_songName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileHeader.songName); m_nSamples = fileHeader.numSamples; m_nChannels = 4; m_nMinPeriod = 64; m_nMaxPeriod = 0x7FFF; m_playBehaviour.set(kST3SampleSwap); uint8 initTempo = fileHeader.initTempo; if(initTempo == 0) initTempo = 0x60; m_nDefaultTempo = ConvertST2Tempo(initTempo); m_nDefaultSpeed = initTempo >> 4; m_nDefaultGlobalVolume = std::min(fileHeader.globalVolume, uint8(64)) * 4u; // Setting up channels for(CHANNELINDEX chn = 0; chn < 4; chn++) { ChnSettings[chn].Reset(); ChnSettings[chn].nPan = (chn & 1) ? 0x40 : 0xC0; } std::vector patternOffsets, sampleOffsets; file.Seek(fileHeader.patTableOffset << 4); file.ReadVector(patternOffsets, fileHeader.numPatterns); file.Seek(fileHeader.smpTableOffset << 4); file.ReadVector(sampleOffsets, fileHeader.numSamples); // Read order list file.Seek((fileHeader.chnTableOffset << 4) + 32); Order().resize(fileHeader.numOrders); for(auto &pat : Order()) { pat = file.ReadUint8(); file.Skip(4); } if(!ValidateSTMOrderList(Order())) return false; // Read samples for(SAMPLEINDEX smp = 1; smp <= fileHeader.numSamples; smp++) { if(!file.Seek(sampleOffsets[smp - 1] << 4)) return false; S3MSampleHeader sampleHeader; file.ReadStruct(sampleHeader); sampleHeader.ConvertToMPT(Samples[smp]); m_szNames[smp] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, sampleHeader.filename); const uint32 sampleOffset = sampleHeader.GetSampleOffset(); if((loadFlags & loadSampleData) && sampleHeader.length != 0 && file.Seek(sampleOffset)) { sampleHeader.GetSampleFormat(true).ReadSample(Samples[smp], file); } } // Read patterns uint8 formatVersion = 1; if(!patternOffsets.empty() && fileHeader.patternSize != 0x1A) { if(!file.Seek(patternOffsets.front() << 4)) return false; // First two bytes describe pattern size, like in S3M if(file.ReadUint16LE() == fileHeader.patternSize) formatVersion = 0; } if(loadFlags & loadPatternData) Patterns.ResizeArray(fileHeader.numPatterns); for(PATTERNINDEX pat = 0; pat < fileHeader.numPatterns; pat++) { if(!(loadFlags & loadPatternData) || !Patterns.Insert(pat, 64)) break; if(!file.Seek(patternOffsets[pat] << 4)) return false; if(formatVersion == 0 && file.ReadUint16LE() > 0x840) return false; ORDERINDEX breakPos = ORDERINDEX_INVALID; ROWINDEX breakRow = 63; // Candidate row for inserting pattern break auto rowBase = Patterns[pat].GetRow(0); ROWINDEX row = 0; uint8 newTempo = 0; while(row < 64) { uint8 info = file.ReadUint8(); if(info == s3mEndOfRow) { // End of row if(newTempo != 0) { Patterns[pat].WriteEffect(EffectWriter(CMD_TEMPO, mpt::saturate_round(ConvertST2Tempo(newTempo).ToDouble())).Row(row).RetryPreviousRow()); newTempo = 0; } if(++row < 64) { rowBase = Patterns[pat].GetRow(row); } continue; } CHANNELINDEX channel = (info & s3mChannelMask); ModCommand dummy; ModCommand &m = (channel < GetNumChannels()) ? rowBase[channel] : dummy; if(info & s3mNotePresent) { const auto [note, instr] = file.ReadArray(); if(note < 0xF0) m.note = static_cast(Clamp((note & 0x0F) + 12 * (note >> 4) + 36 + NOTE_MIN, NOTE_MIN, NOTE_MAX)); else if(note == s3mNoteOff) m.note = NOTE_NOTECUT; else if(note == s3mNoteNone) m.note = NOTE_NONE; m.instr = instr; } if(info & s3mVolumePresent) { uint8 volume = file.ReadUint8(); m.volcmd = VOLCMD_VOLUME; m.vol = std::min(volume, uint8(64)); } if(info & s3mEffectPresent) { const auto [command, param] = file.ReadArray(); m.command = command; m.param = param; ConvertSTMCommand(m, row, 0xFF, newTempo, breakPos, breakRow); } } if(breakPos != ORDERINDEX_INVALID) { Patterns[pat].WriteEffect(EffectWriter(CMD_POSITIONJUMP, static_cast(breakPos)).Row(breakRow).RetryPreviousRow()); } } m_modFormat.formatName = U_("Scream Tracker Music Interface Kit"); m_modFormat.type = U_("stx"); m_modFormat.charset = mpt::Charset::CP437; m_modFormat.madeWithTracker = MPT_UFORMAT("STM2STX 1.{}")(formatVersion); return true; } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Load_stp.cpp0000644000175000017500000005214314114455135020354 00000000000000/* * Load_stp.cpp * ------------ * Purpose: STP (Soundtracker Pro II) module loader * Notes : A few exotic effects aren't supported. * Multiple sample loops are supported, but only the first 10 can be used as cue points * (with 16xx and 18xx). * Fractional speed values and combined auto effects are handled whenever possible, * but some effects may be omitted (and there may be tempo accuracy issues). * Authors: Devin Acker * OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. * * Wisdom from the Soundtracker Pro II manual: * "To create shorter patterns, simply create shorter patterns." */ #include "stdafx.h" #include "Loaders.h" OPENMPT_NAMESPACE_BEGIN // File header struct STPFileHeader { char magic[4]; uint16be version; uint8be numOrders; uint8be patternLength; uint8be orderList[128]; uint16be speed; uint16be speedFrac; uint16be timerCount; uint16be flags; uint32be reserved; uint16be midiCount; // always 50 uint8be midi[50]; uint16be numSamples; uint16be sampleStructSize; }; MPT_BINARY_STRUCT(STPFileHeader, 204) // Sample header (common part between all versions) struct STPSampleHeader { uint32be length; uint8be volume; uint8be reserved1; uint32be loopStart; uint32be loopLength; uint16be defaultCommand; // Default command to put next to note when editing patterns; not relevant for playback // The following 4 bytes are reserved in version 0 and 1. uint16be defaultPeriod; uint8be finetune; uint8be reserved2; void ConvertToMPT(ModSample &mptSmp) const { mptSmp.nLength = length; mptSmp.nVolume = 4u * std::min(volume.get(), uint8(64)); mptSmp.nLoopStart = loopStart; mptSmp.nLoopEnd = loopStart + loopLength; if(mptSmp.nLoopStart >= mptSmp.nLength) { mptSmp.nLoopStart = mptSmp.nLength - 1; } if(mptSmp.nLoopEnd > mptSmp.nLength) { mptSmp.nLoopEnd = mptSmp.nLength; } if(mptSmp.nLoopStart > mptSmp.nLoopEnd) { mptSmp.nLoopStart = 0; mptSmp.nLoopEnd = 0; } else if(mptSmp.nLoopEnd > mptSmp.nLoopStart) { mptSmp.uFlags.set(CHN_LOOP); mptSmp.cues[0] = mptSmp.nLoopStart; } } }; MPT_BINARY_STRUCT(STPSampleHeader, 20) struct STPLoopInfo { SmpLength loopStart; SmpLength loopLength; SAMPLEINDEX looped; SAMPLEINDEX nonLooped; }; typedef std::vector STPLoopList; static TEMPO ConvertTempo(uint16 ciaSpeed) { // 3546 is the resulting CIA timer value when using 4F7D (tempo 125 bpm) command in STProII return TEMPO((125.0 * 3546.0) / ciaSpeed); } static void ConvertLoopSlice(ModSample &src, ModSample &dest, SmpLength start, SmpLength len, bool loop) { if(!src.HasSampleData() || start >= src.nLength || src.nLength - start < len) { return; } dest.FreeSample(); dest = src; dest.nLength = len; dest.pData.pSample = nullptr; if(!dest.AllocateSample()) { return; } // only preserve cue points if the target sample length is the same if(len != src.nLength) MemsetZero(dest.cues); std::memcpy(dest.sampleb(), src.sampleb() + start, len); dest.uFlags.set(CHN_LOOP, loop); if(loop) { dest.nLoopStart = 0; dest.nLoopEnd = len; } else { dest.nLoopStart = 0; dest.nLoopEnd = 0; } } static void ConvertLoopSequence(ModSample &smp, STPLoopList &loopList) { // This should only modify a sample if it has more than one loop // (otherwise, it behaves like a normal sample loop) if(!smp.HasSampleData() || loopList.size() < 2) return; ModSample newSmp = smp; newSmp.nLength = 0; newSmp.pData.pSample = nullptr; size_t numLoops = loopList.size(); // Get the total length of the sample after combining all looped sections for(size_t i = 0; i < numLoops; i++) { STPLoopInfo &info = loopList[i]; // If adding this loop would cause the sample length to exceed maximum, // then limit and bail out if(info.loopStart >= smp.nLength || smp.nLength - info.loopStart < info.loopLength || newSmp.nLength > MAX_SAMPLE_LENGTH - info.loopLength) { numLoops = i; break; } newSmp.nLength += info.loopLength; } if(!newSmp.AllocateSample()) { return; } // start copying the looped sample data parts SmpLength start = 0; for(size_t i = 0; i < numLoops; i++) { STPLoopInfo &info = loopList[i]; memcpy(newSmp.sampleb() + start, smp.sampleb() + info.loopStart, info.loopLength); // update loop info based on position in edited sample info.loopStart = start; if(i > 0 && i <= std::size(newSmp.cues)) { newSmp.cues[i - 1] = start; } start += info.loopLength; } // replace old sample with new one smp.FreeSample(); smp = newSmp; smp.nLoopStart = 0; smp.nLoopEnd = smp.nLength; smp.uFlags.set(CHN_LOOP); } static bool ValidateHeader(const STPFileHeader &fileHeader) { if(std::memcmp(fileHeader.magic, "STP3", 4) || fileHeader.version > 2 || fileHeader.numOrders > 128 || fileHeader.numSamples >= MAX_SAMPLES || fileHeader.timerCount == 0 || fileHeader.midiCount != 50) { return false; } return true; } CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderSTP(MemoryFileReader file, const uint64 *pfilesize) { STPFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return ProbeWantMoreData; } if(!ValidateHeader(fileHeader)) { return ProbeFailure; } MPT_UNREFERENCED_PARAMETER(pfilesize); return ProbeSuccess; } bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags) { file.Rewind(); STPFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return false; } if(!ValidateHeader(fileHeader)) { return false; } if(loadFlags == onlyVerifyHeader) { return true; } InitializeGlobals(MOD_TYPE_STP); m_modFormat.formatName = MPT_UFORMAT("Soundtracker Pro II v{}")(fileHeader.version); m_modFormat.type = U_("stp"); m_modFormat.charset = mpt::Charset::ISO8859_1; m_nChannels = 4; m_nSamples = 0; m_nDefaultSpeed = fileHeader.speed; m_nDefaultTempo = ConvertTempo(fileHeader.timerCount); m_nMinPeriod = 14 * 4; m_nMaxPeriod = 3424 * 4; ReadOrderFromArray(Order(), fileHeader.orderList, fileHeader.numOrders); std::vector loopInfo; // Non-looped versions of samples with loops (when needed) std::vector nonLooped; // Load sample headers SAMPLEINDEX samplesInFile = 0; for(SAMPLEINDEX smp = 0; smp < fileHeader.numSamples; smp++) { SAMPLEINDEX actualSmp = file.ReadUint16BE(); if(actualSmp == 0 || actualSmp >= MAX_SAMPLES) return false; uint32 chunkSize = fileHeader.sampleStructSize; if(fileHeader.version == 2) chunkSize = file.ReadUint32BE() - 2; FileReader chunk = file.ReadChunk(chunkSize); samplesInFile = m_nSamples = std::max(m_nSamples, actualSmp); ModSample &mptSmp = Samples[actualSmp]; mptSmp.Initialize(MOD_TYPE_MOD); if(fileHeader.version < 2) { // Read path chunk.ReadString(mptSmp.filename, 31); // Ignore flags, they are all not relevant for us chunk.Skip(1); // Read filename / sample text chunk.ReadString(m_szNames[actualSmp], 30); } else { std::string str; // Read path chunk.ReadNullString(str, 257); mptSmp.filename = str; // Ignore flags, they are all not relevant for us chunk.Skip(1); // Read filename / sample text chunk.ReadNullString(str, 31); m_szNames[actualSmp] = str; // Seek to even boundary if(chunk.GetPosition() % 2u) chunk.Skip(1); } STPSampleHeader sampleHeader; chunk.ReadStruct(sampleHeader); sampleHeader.ConvertToMPT(mptSmp); if(fileHeader.version == 2) { mptSmp.nFineTune = static_cast(sampleHeader.finetune << 3); } if(fileHeader.version >= 1) { nonLooped.resize(samplesInFile); loopInfo.resize(samplesInFile); STPLoopList &loopList = loopInfo[actualSmp - 1]; loopList.clear(); const uint16 numLoops = file.ReadUint16BE(); if(!file.CanRead(numLoops * 8u)) return false; loopList.reserve(numLoops); STPLoopInfo loop; loop.looped = loop.nonLooped = 0; if(numLoops == 0 && mptSmp.uFlags[CHN_LOOP]) { loop.loopStart = mptSmp.nLoopStart; loop.loopLength = mptSmp.nLoopEnd - mptSmp.nLoopStart; loopList.push_back(loop); } else for(uint16 i = 0; i < numLoops; i++) { loop.loopStart = file.ReadUint32BE(); loop.loopLength = file.ReadUint32BE(); loopList.push_back(loop); } } } // Load patterns uint16 numPatterns = 128; if(fileHeader.version == 0) numPatterns = file.ReadUint16BE(); uint16 patternLength = fileHeader.patternLength; CHANNELINDEX channels = 4; if(fileHeader.version > 0) { // Scan for total number of channels FileReader::off_t patOffset = file.GetPosition(); for(uint16 pat = 0; pat < numPatterns; pat++) { PATTERNINDEX actualPat = file.ReadUint16BE(); if(actualPat == 0xFFFF) break; patternLength = file.ReadUint16BE(); channels = file.ReadUint16BE(); if(channels > MAX_BASECHANNELS) return false; m_nChannels = std::max(m_nChannels, channels); file.Skip(channels * patternLength * 4u); } file.Seek(patOffset); } struct ChannelMemory { uint8 autoFinePorta, autoPortaUp, autoPortaDown, autoVolSlide, autoVibrato; uint8 vibratoMem, autoTremolo, autoTonePorta, tonePortaMem; }; std::vector channelMemory(m_nChannels); uint8 globalVolSlide = 0; uint8 speedFrac = static_cast(fileHeader.speedFrac); for(uint16 pat = 0; pat < numPatterns; pat++) { PATTERNINDEX actualPat = pat; if(fileHeader.version > 0) { actualPat = file.ReadUint16BE(); if(actualPat == 0xFFFF) break; patternLength = file.ReadUint16BE(); channels = file.ReadUint16BE(); } if(!file.CanRead(channels * patternLength * 4u)) break; if(!(loadFlags & loadPatternData) || !Patterns.Insert(actualPat, patternLength)) { file.Skip(channels * patternLength * 4u); continue; } for(ROWINDEX row = 0; row < patternLength; row++) { auto rowBase = Patterns[actualPat].GetRow(row); bool didGlobalVolSlide = false; // if a fractional speed value is in use then determine if we should stick a fine pattern delay somewhere bool shouldDelay; switch(speedFrac & 3) { default: shouldDelay = false; break; // 1/4 case 1: shouldDelay = (row & 3) == 0; break; // 1/2 case 2: shouldDelay = (row & 1) == 0; break; // 3/4 case 3: shouldDelay = (row & 3) != 3; break; } for(CHANNELINDEX chn = 0; chn < channels; chn++) { ChannelMemory &chnMem = channelMemory[chn]; ModCommand &m = rowBase[chn]; const auto [instr, note, command, param] = file.ReadArray(); m.instr = instr; m.note = note; m.param = param; if(m.note) { m.note += 24 + NOTE_MIN; chnMem = ChannelMemory(); } // this is a nibble-swapped param value used for auto fine volside // and auto global fine volside uint8 swapped = (m.param >> 4) | (m.param << 4); if((command & 0xF0) == 0xF0) { // 12-bit CIA tempo uint16 ciaTempo = (static_cast(command & 0x0F) << 8) | m.param; if(ciaTempo) { m.param = mpt::saturate_round(ConvertTempo(ciaTempo).ToDouble()); m.command = CMD_TEMPO; } else { m.command = CMD_NONE; } } else switch(command) { case 0x00: // arpeggio if(m.param) m.command = CMD_ARPEGGIO; else m.command = CMD_NONE; break; case 0x01: // portamento up m.command = CMD_PORTAMENTOUP; break; case 0x02: // portamento down m.command = CMD_PORTAMENTODOWN; break; case 0x03: // auto fine portamento up chnMem.autoFinePorta = 0x10 | std::min(m.param, ModCommand::PARAM(15)); chnMem.autoPortaUp = 0; chnMem.autoPortaDown = 0; chnMem.autoTonePorta = 0; m.command = CMD_NONE; break; case 0x04: // auto fine portamento down chnMem.autoFinePorta = 0x20 | std::min(m.param, ModCommand::PARAM(15)); chnMem.autoPortaUp = 0; chnMem.autoPortaDown = 0; chnMem.autoTonePorta = 0; m.command = CMD_NONE; break; case 0x05: // auto portamento up chnMem.autoFinePorta = 0; chnMem.autoPortaUp = m.param; chnMem.autoPortaDown = 0; chnMem.autoTonePorta = 0; m.command = CMD_NONE; break; case 0x06: // auto portamento down chnMem.autoFinePorta = 0; chnMem.autoPortaUp = 0; chnMem.autoPortaDown = m.param; chnMem.autoTonePorta = 0; m.command = CMD_NONE; break; case 0x07: // set global volume m.command = CMD_GLOBALVOLUME; globalVolSlide = 0; break; case 0x08: // auto global fine volume slide globalVolSlide = swapped; m.command = CMD_NONE; break; case 0x09: // fine portamento up m.command = CMD_MODCMDEX; m.param = 0x10 | std::min(m.param, ModCommand::PARAM(15)); break; case 0x0A: // fine portamento down m.command = CMD_MODCMDEX; m.param = 0x20 | std::min(m.param, ModCommand::PARAM(15)); break; case 0x0B: // auto fine volume slide chnMem.autoVolSlide = swapped; m.command = CMD_NONE; break; case 0x0C: // set volume m.volcmd = VOLCMD_VOLUME; m.vol = m.param; chnMem.autoVolSlide = 0; m.command = CMD_NONE; break; case 0x0D: // volume slide (param is swapped compared to .mod) if(m.param & 0xF0) { m.volcmd = VOLCMD_VOLSLIDEDOWN; m.vol = m.param >> 4; } else if(m.param & 0x0F) { m.volcmd = VOLCMD_VOLSLIDEUP; m.vol = m.param & 0xF; } chnMem.autoVolSlide = 0; m.command = CMD_NONE; break; case 0x0E: // set filter (also uses opposite value compared to .mod) m.command = CMD_MODCMDEX; m.param = 1 ^ (m.param ? 1 : 0); break; case 0x0F: // set speed m.command = CMD_SPEED; speedFrac = m.param & 0x0F; m.param >>= 4; break; case 0x10: // auto vibrato chnMem.autoVibrato = m.param; chnMem.vibratoMem = 0; m.command = CMD_NONE; break; case 0x11: // auto tremolo if(m.param & 0xF) chnMem.autoTremolo = m.param; else chnMem.autoTremolo = 0; m.command = CMD_NONE; break; case 0x12: // pattern break m.command = CMD_PATTERNBREAK; break; case 0x13: // auto tone portamento chnMem.autoFinePorta = 0; chnMem.autoPortaUp = 0; chnMem.autoPortaDown = 0; chnMem.autoTonePorta = m.param; chnMem.tonePortaMem = 0; m.command = CMD_NONE; break; case 0x14: // position jump m.command = CMD_POSITIONJUMP; break; case 0x16: // start loop sequence if(m.instr && m.instr <= loopInfo.size()) { STPLoopList &loopList = loopInfo[m.instr - 1]; m.param--; if(m.param < std::min(std::size(ModSample().cues), loopList.size())) { m.volcmd = VOLCMD_OFFSET; m.vol = m.param; } } m.command = CMD_NONE; break; case 0x17: // play only loop nn if(m.instr && m.instr <= loopInfo.size()) { STPLoopList &loopList = loopInfo[m.instr - 1]; m.param--; if(m.param < loopList.size()) { if(!loopList[m.param].looped && CanAddMoreSamples()) loopList[m.param].looped = ++m_nSamples; m.instr = static_cast(loopList[m.param].looped); } } m.command = CMD_NONE; break; case 0x18: // play sequence without loop if(m.instr && m.instr <= loopInfo.size()) { STPLoopList &loopList = loopInfo[m.instr - 1]; m.param--; if(m.param < std::min(std::size(ModSample().cues), loopList.size())) { m.volcmd = VOLCMD_OFFSET; m.vol = m.param; } // switch to non-looped version of sample and create it if needed if(!nonLooped[m.instr - 1] && CanAddMoreSamples()) nonLooped[m.instr - 1] = ++m_nSamples; m.instr = static_cast(nonLooped[m.instr - 1]); } m.command = CMD_NONE; break; case 0x19: // play only loop nn without loop if(m.instr && m.instr <= loopInfo.size()) { STPLoopList &loopList = loopInfo[m.instr - 1]; m.param--; if(m.param < loopList.size()) { if(!loopList[m.param].nonLooped && CanAddMoreSamples()) loopList[m.param].nonLooped = ++m_nSamples; m.instr = static_cast(loopList[m.param].nonLooped); } } m.command = CMD_NONE; break; case 0x1D: // fine volume slide (nibble order also swapped) m.command = CMD_VOLUMESLIDE; m.param = swapped; if(m.param & 0xF0) // slide down m.param |= 0x0F; else if(m.param & 0x0F) m.param |= 0xF0; break; case 0x20: // "delayed fade" // just behave like either a normal fade or a notecut // depending on the speed if(m.param & 0xF0) { chnMem.autoVolSlide = m.param >> 4; m.command = CMD_NONE; } else { m.command = CMD_MODCMDEX; m.param = 0xC0 | (m.param & 0xF); } break; case 0x21: // note delay m.command = CMD_MODCMDEX; m.param = 0xD0 | std::min(m.param, ModCommand::PARAM(15)); break; case 0x22: // retrigger note m.command = CMD_MODCMDEX; m.param = 0x90 | std::min(m.param, ModCommand::PARAM(15)); break; case 0x49: // set sample offset m.command = CMD_OFFSET; break; case 0x4E: // other protracker commands (pattern loop / delay) if((m.param & 0xF0) == 0x60 || (m.param & 0xF0) == 0xE0) m.command = CMD_MODCMDEX; else m.command = CMD_NONE; break; case 0x4F: // set speed/tempo if(m.param < 0x20) { m.command = CMD_SPEED; speedFrac = 0; } else { m.command = CMD_TEMPO; } break; default: m.command = CMD_NONE; break; } bool didVolSlide = false; // try to put volume slide in volume command if(chnMem.autoVolSlide && m.volcmd == VOLCMD_NONE) { if(chnMem.autoVolSlide & 0xF0) { m.volcmd = VOLCMD_FINEVOLUP; m.vol = chnMem.autoVolSlide >> 4; } else { m.volcmd = VOLCMD_FINEVOLDOWN; m.vol = chnMem.autoVolSlide & 0xF; } didVolSlide = true; } // try to place/combine all remaining running effects. if(m.command == CMD_NONE) { if(chnMem.autoPortaUp) { m.command = CMD_PORTAMENTOUP; m.param = chnMem.autoPortaUp; } else if(chnMem.autoPortaDown) { m.command = CMD_PORTAMENTODOWN; m.param = chnMem.autoPortaDown; } else if(chnMem.autoFinePorta) { m.command = CMD_MODCMDEX; m.param = chnMem.autoFinePorta; } else if(chnMem.autoTonePorta) { m.command = CMD_TONEPORTAMENTO; m.param = chnMem.tonePortaMem = chnMem.autoTonePorta; } else if(chnMem.autoVibrato) { m.command = CMD_VIBRATO; m.param = chnMem.vibratoMem = chnMem.autoVibrato; } else if(!didVolSlide && chnMem.autoVolSlide) { m.command = CMD_VOLUMESLIDE; m.param = chnMem.autoVolSlide; // convert to a "fine" value by setting the other nibble to 0xF if(m.param & 0x0F) m.param |= 0xF0; else if(m.param & 0xF0) m.param |= 0x0F; didVolSlide = true; } else if(chnMem.autoTremolo) { m.command = CMD_TREMOLO; m.param = chnMem.autoTremolo; } else if(shouldDelay) { // insert a fine pattern delay here m.command = CMD_S3MCMDEX; m.param = 0x61; shouldDelay = false; } else if(!didGlobalVolSlide && globalVolSlide) { m.command = CMD_GLOBALVOLSLIDE; m.param = globalVolSlide; // convert to a "fine" value by setting the other nibble to 0xF if(m.param & 0x0F) m.param |= 0xF0; else if(m.param & 0xF0) m.param |= 0x0F; didGlobalVolSlide = true; } } } // TODO: create/use extra channels for global volslide/delay if needed } } // after we know how many channels there really are... m_nSamplePreAmp = 256 / m_nChannels; // Setup channel pan positions and volume SetupMODPanning(true); // Skip over scripts and drumpad info if(fileHeader.version > 0) { while(file.CanRead(2)) { uint16 scriptNum = file.ReadUint16BE(); if(scriptNum == 0xFFFF) break; file.Skip(2); uint32 length = file.ReadUint32BE(); file.Skip(length); } // Skip drumpad stuff file.Skip(17 * 2); } // Reading samples if(loadFlags & loadSampleData) { for(SAMPLEINDEX smp = 1; smp <= samplesInFile; smp++) if(Samples[smp].nLength) { SampleIO( SampleIO::_8bit, SampleIO::mono, SampleIO::littleEndian, SampleIO::signedPCM) .ReadSample(Samples[smp], file); if(smp > loopInfo.size()) continue; ConvertLoopSequence(Samples[smp], loopInfo[smp - 1]); // make a non-looping duplicate of this sample if needed if(nonLooped[smp - 1]) { ConvertLoopSlice(Samples[smp], Samples[nonLooped[smp - 1]], 0, Samples[smp].nLength, false); } for(const auto &info : loopInfo[smp - 1]) { // make duplicate samples for this individual section if needed if(info.looped) { ConvertLoopSlice(Samples[smp], Samples[info.looped], info.loopStart, info.loopLength, true); } if(info.nonLooped) { ConvertLoopSlice(Samples[smp], Samples[info.nonLooped], info.loopStart, info.loopLength, false); } } } } return true; } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Load_symmod.cpp0000644000175000017500000017471114175337043021070 00000000000000/* * Load_symmod.cpp * --------------- * Purpose: SymMOD (Symphonie / Symphonie Pro) module loader * Notes : Based in part on Patrick Meng's Java-based Symphonie player and its source. * Some effect behaviour and other things are based on the original Amiga assembly source. * Symphonie is an interesting beast, with a surprising combination of features and lack thereof. * It offers advanced DSPs (for its time) but has a fixed track tempo. It can handle stereo samples * but free panning support was only added in one of the very last versions. Still, a good number * of high-quality modules were made with it despite (or because of) its lack of features. * Authors: Devin Acker * OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Loaders.h" #include "Mixer.h" #include "MixFuncTable.h" #include "modsmp_ctrl.h" #include "openmpt/soundbase/SampleConvert.hpp" #include "openmpt/soundbase/SampleConvertFixedPoint.hpp" #include "openmpt/soundbase/SampleDecode.hpp" #include "SampleCopy.h" #ifdef MPT_EXTERNAL_SAMPLES #include "../common/mptPathString.h" #endif // MPT_EXTERNAL_SAMPLES #include "mpt/base/numbers.hpp" #include OPENMPT_NAMESPACE_BEGIN struct SymFileHeader { char magic[4]; // "SymM" uint32be version; bool Validate() const { return !std::memcmp(magic, "SymM", 4) && version == 1; } }; MPT_BINARY_STRUCT(SymFileHeader, 8) struct SymEvent { enum Command : uint8 { KeyOn = 0, VolSlideUp, VolSlideDown, PitchSlideUp, PitchSlideDown, ReplayFrom, FromAndPitch, SetFromAdd, FromAdd, SetSpeed, AddPitch, AddVolume, Tremolo, Vibrato, SampleVib, PitchSlideTo, Retrig, Emphasis, AddHalfTone, CV, CVAdd, Filter = 23, DSPEcho, DSPDelay, }; enum Volume : uint8 { VolCommand = 200, StopSample = 254, ContSample = 253, StartSample = 252, // unused KeyOff = 251, SpeedDown = 250, SpeedUp = 249, SetPitch = 248, PitchUp = 247, PitchDown = 246, PitchUp2 = 245, PitchDown2 = 244, PitchUp3 = 243, PitchDown3 = 242 }; uint8be command; // See Command enum int8be note; uint8be param; // Volume if <= 100, see Volume enum otherwise uint8be inst; bool IsGlobal() const { if(command == SymEvent::SetSpeed || command == SymEvent::DSPEcho || command == SymEvent::DSPDelay) return true; if(command == SymEvent::KeyOn && (param == SymEvent::SpeedUp || param == SymEvent::SpeedDown)) return true; return false; } // used to compare DSP events for mapping them to MIDI macro numbers bool operator<(const SymEvent &other) const { return std::tie(command, note, param, inst) < std::tie(other.command, other.note, other.param, other.inst); } }; MPT_BINARY_STRUCT(SymEvent, 4) struct SymVirtualHeader { char id[4]; // "ViRT" uint8be zero; uint8be filler1; uint16be version; // 0 = regular, 1 = transwave uint16be mixInfo; // unused, but not 0 in all modules uint16be filler2; uint16be eos; // 0 uint16be numEvents; uint16be maxEvents; // always 20 uint16be eventSize; // 4 for virtual instruments, 10 for transwave instruments (number of cycles, not used) bool IsValid() const { return !memcmp(id, "ViRT", 4) && zero == 0 && version <= 1 && eos == 0 && maxEvents == 20; } bool IsVirtual() const { return IsValid() && version == 0 && numEvents <= 20 && eventSize == sizeof(SymEvent); } bool IsTranswave() const { return IsValid() && version == 1 && numEvents == 2 && eventSize == 10; } }; MPT_BINARY_STRUCT(SymVirtualHeader, 20) // Virtual instrument info // This allows instruments to be created based on a mix of other instruments. // The sample mixing is done at load time. struct SymVirtualInst { SymVirtualHeader header; SymEvent noteEvents[20]; char padding[28]; bool Render(CSoundFile &sndFile, const bool asQueue, ModSample &target, uint16 sampleBoost) const { if(header.numEvents < 1 || header.numEvents > std::size(noteEvents) || noteEvents[0].inst >= sndFile.GetNumSamples()) return false; target.Initialize(MOD_TYPE_IT); target.uFlags = CHN_16BIT; const auto events = mpt::as_span(noteEvents).subspan(0, header.numEvents); const double rateFactor = 1.0 / std::max(sndFile.GetSample(events[0].inst + 1).nC5Speed, uint32(1)); for(const auto &event : events.subspan(0, asQueue ? events.size() : 1u)) { if(event.inst >= sndFile.GetNumSamples() || event.note < 0) continue; const ModSample &sourceSmp = sndFile.GetSample(event.inst + 1); const double length = sourceSmp.nLength * std::pow(2.0, (event.note - events[0].note) / -12.0) * sourceSmp.nC5Speed * rateFactor; target.nLength += mpt::saturate_round(length); } if(!target.AllocateSample()) return false; std::vector channels(events.size()); SmpLength lastSampleOffset = 0; for(size_t ev = 0; ev < events.size(); ev++) { const SymEvent &event = events[ev]; ModChannel &chn = channels[ev]; if(event.inst >= sndFile.GetNumSamples() || event.note < 0) continue; int8 finetune = 0; if(event.param >= SymEvent::PitchDown3 && event.param <= SymEvent::PitchUp) { static constexpr int8 PitchTable[] = {-4, 4, -2, 2, -1, 1}; static_assert(mpt::array_size::size == SymEvent::PitchUp - SymEvent::PitchDown3 + 1); finetune = PitchTable[event.param - SymEvent::PitchDown3]; } const ModSample &sourceSmp = sndFile.GetSample(event.inst + 1); const double increment = std::pow(2.0, (event.note - events[0].note) / 12.0 + finetune / 96.0) * sourceSmp.nC5Speed * rateFactor; if(increment <= 0) continue; chn.increment = SamplePosition::FromDouble(increment); chn.pCurrentSample = sourceSmp.samplev(); chn.nLength = sourceSmp.nLength; chn.dwFlags = sourceSmp.uFlags & CHN_SAMPLEFLAGS; if(asQueue) { // This determines when the queued sample will be played chn.oldOffset = lastSampleOffset; lastSampleOffset += mpt::saturate_round(chn.nLength / chn.increment.ToDouble()); } int32 volume = 4096 * sampleBoost / 10000; // avoid clipping the filters if the virtual sample is later also filtered (see e.g. 303 emulator.symmod) if(!asQueue) volume /= header.numEvents; chn.leftVol = chn.rightVol = volume; } SmpLength writeOffset = 0; while(writeOffset < target.nLength) { std::array buffer{}; const SmpLength writeCount = std::min(static_cast(MIXBUFFERSIZE), target.nLength - writeOffset); for(auto &chn : channels) { if(!chn.pCurrentSample) continue; // Should queued sample be played yet? if(chn.oldOffset >= writeCount) { chn.oldOffset -= writeCount; continue; } uint32 functionNdx = MixFuncTable::ndxLinear; if(chn.dwFlags[CHN_16BIT]) functionNdx |= MixFuncTable::ndx16Bit; if(chn.dwFlags[CHN_STEREO]) functionNdx |= MixFuncTable::ndxStereo; const SmpLength procCount = std::min(writeCount - chn.oldOffset, mpt::saturate_round((chn.nLength - chn.position.ToDouble()) / chn.increment.ToDouble())); MixFuncTable::Functions[functionNdx](chn, sndFile.m_Resampler, buffer.data() + chn.oldOffset * 2, procCount); chn.oldOffset = 0; if(chn.position.GetUInt() >= chn.nLength) chn.pCurrentSample = nullptr; } CopySample, SC::DecodeIdentity>>(target.sample16() + writeOffset, writeCount, 1, buffer.data(), sizeof(buffer), 2); writeOffset += writeCount; } return true; } }; MPT_BINARY_STRUCT(SymVirtualInst, 128) // Transwave instrument info // Similar to virtual instruments, allows blending between two sample loops struct SymTranswaveInst { struct Transwave { uint16be sourceIns; uint16be volume; // According to source label - but appears to be unused uint32be loopStart; uint32be loopLen; uint32be padding; std::pair ConvertLoop(const ModSample &mptSmp) const { const double loopScale = static_cast(mptSmp.nLength) / (100 << 16); const SmpLength start = mpt::saturate_cast(loopScale * std::min(uint32(100 << 16), loopStart.get())); const SmpLength length = mpt::saturate_cast(loopScale * std::min(uint32(100 << 16), loopLen.get())); return {start, std::min(mptSmp.nLength - start, length)}; } }; SymVirtualHeader header; Transwave points[2]; char padding[76]; // Morph between two sample loops bool Render(const ModSample &smp1, const ModSample &smp2, ModSample &target) const { target.Initialize(MOD_TYPE_IT); const auto [loop1Start, loop1Len] = points[0].ConvertLoop(smp1); const auto [loop2Start, loop2Len] = points[1].ConvertLoop(smp2); if(loop1Len < 1 || loop1Len > MAX_SAMPLE_LENGTH / (4u * 80u)) return false; const SmpLength cycleLength = loop1Len * 4u; const double cycleFactor1 = loop1Len / static_cast(cycleLength); const double cycleFactor2 = loop2Len / static_cast(cycleLength); target.uFlags = CHN_16BIT; target.nLength = cycleLength * 80u; if(!target.AllocateSample()) return false; const double ampFactor = 1.0 / target.nLength; for(SmpLength i = 0; i < cycleLength; i++) { const double v1 = TranswaveInterpolate(smp1, loop1Start + i * cycleFactor1); const double v2 = TranswaveInterpolate(smp2, loop2Start + i * cycleFactor2); SmpLength writeOffset = i; for(int cycle = 0; cycle < 80; cycle++, writeOffset += cycleLength) { const double amp = writeOffset * ampFactor; target.sample16()[writeOffset] = mpt::saturate_round(v1 * (1.0 - amp) + v2 * amp); } } return true; } static MPT_FORCEINLINE double TranswaveInterpolate(const ModSample &smp, double offset) { if(!smp.HasSampleData()) return 0.0; SmpLength intOffset = static_cast(offset); const double fractOffset = offset - intOffset; const uint8 numChannels = smp.GetNumChannels(); intOffset *= numChannels; int16 v1, v2; if(smp.uFlags[CHN_16BIT]) { v1 = smp.sample16()[intOffset]; v2 = smp.sample16()[intOffset + numChannels]; } else { v1 = smp.sample8()[intOffset] * 256; v2 = smp.sample8()[intOffset + numChannels] * 256; } return (v1 * (1.0 - fractOffset) + v2 * fractOffset); } }; MPT_BINARY_STRUCT(SymTranswaveInst, 128) // Instrument definition struct SymInstrument { using SymInstrumentName = std::array; SymVirtualInst virt; // or SymInstrumentName, or SymTranswaveInst enum Type : int8 { Silent = -8, Kill = -4, Normal = 0, Loop = 4, Sustain = 8 }; enum Channel : uint8 { Mono, StereoL, StereoR, LineSrc // virtual mix instrument }; enum SampleFlags : uint8 { PlayReverse = 1, // reverse sample AsQueue = 2, // "queue" virtual instrument (rendereds samples one after another rather than simultaneously) MirrorX = 4, // invert sample phase Is16Bit = 8, // not used, we already know the bit depth of the samples NewLoopSystem = 16, // use fine loop start/len values MakeNewSample = (PlayReverse | MirrorX) }; enum InstFlags : uint8 { NoTranspose = 1, // don't apply sequence/position transpose NoDSP = 2, // don't apply DSP effects SyncPlay = 4 // play a stereo instrument pair (or two copies of the same mono instrument) on consecutive channels }; int8be type; // see Type enum uint8be loopStartHigh; uint8be loopLenHigh; uint8be numRepetitions; // for "sustain" instruments uint8be channel; // see Channel enum uint8be dummy1; // called "automaximize" (normalize?) in Amiga source, but unused uint8be volume; // 0-199 uint8be dummy2[3]; // info about "parent/child" and sample format int8be finetune; // -128..127 ~= 2 semitones int8be transpose; uint8be sampleFlags; // see SampleFlags enum int8be filter; // negative: highpass, positive: lowpass uint8be instFlags; // see InstFlags enum uint8be downsample; // downsample factor; affects sample tuning uint8be dummy3[2]; // resonance, "loadflags" (both unused) uint8be info; // bit 0 should indicate that rangeStart/rangeLen are valid, but they appear to be unused uint8be rangeStart; // ditto uint8be rangeLen; // ditto uint8be dummy4; uint16be loopStartFine; uint16be loopLenFine; uint8be dummy5[6]; uint8be filterFlags; // bit 0 = enable, bit 1 = highpass uint8be numFilterPoints; // # of filter envelope points (up to 4, possibly only 1-2 ever actually used) struct SymFilterSetting { uint8be cutoff; uint8be resonance; } filterPoint[4]; uint8be volFadeFlag; uint8be volFadeFrom; uint8be volFadeTo; uint8be padding[83]; bool IsVirtual() const { return virt.header.IsValid(); } // Valid instrument either is virtual or has a name bool IsEmpty() const { return virt.header.id[0] == 0 || type < 0; } std::string GetName() const { return mpt::String::ReadBuf(mpt::String::maybeNullTerminated, mpt::bit_cast(virt)); } SymTranswaveInst GetTranswave() const { return mpt::bit_cast(virt); } void ConvertToMPT(ModInstrument &mptIns, ModSample &mptSmp, CSoundFile &sndFile) const { if(!IsVirtual()) mptIns.name = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, mpt::bit_cast(virt)); mptSmp.uFlags.reset(CHN_LOOP | CHN_PINGPONGLOOP | CHN_SUSTAINLOOP | CHN_PANNING); // Avoid these coming in from sample files const auto [loopStart, loopLen] = GetSampleLoop(mptSmp); if(type == Loop && loopLen > 0) { mptSmp.uFlags.set(CHN_LOOP); mptSmp.nLoopStart = loopStart; mptSmp.nLoopEnd = loopStart + loopLen; } // volume (0-199, default 100) // Symphonie actually compresses the sample data if the volume is above 100 (see end of function) // We spread the volume between sample and instrument global volume if it's below 100 for the best possible resolution. // This can be simplified if instrument volume ever gets adjusted to 0...128 range like in IT. uint8 effectiveVolume = (volume > 0 && volume < 200) ? static_cast(std::min(volume.get(), uint8(100)) * 128u / 100) : 128; mptSmp.nGlobalVol = std::max(effectiveVolume, uint8(64)) / 2u; mptIns.nGlobalVol = std::min(effectiveVolume, uint8(64)); // Tuning info (we'll let our own mixer take care of the downsampling instead of doing it at load time) mptSmp.nC5Speed = 40460; mptSmp.Transpose(-downsample + (transpose / 12.0) + (finetune / (128.0 * 12.0))); // DSP settings mptIns.nMixPlug = (instFlags & NoDSP) ? 2 : 1; if(instFlags & NoDSP) { // This is not 100% correct: An instrument playing after this one should pick up previous filter settings. mptIns.SetCutoff(127, true); mptIns.SetResonance(0, true); } // Various sample processing follows if(!mptSmp.HasSampleData()) return; if(sampleFlags & PlayReverse) ctrlSmp::ReverseSample(mptSmp, 0, 0, sndFile); if(sampleFlags & MirrorX) ctrlSmp::InvertSample(mptSmp, 0, 0, sndFile); // Always use 16-bit data to help with heavily filtered 8-bit samples (like in Future_Dream.SymMOD) const bool doVolFade = (volFadeFlag == 2) && (volFadeFrom <= 100) && (volFadeTo <= 100); if(!mptSmp.uFlags[CHN_16BIT] && (filterFlags || doVolFade || filter)) { int16 *newSample = static_cast(ModSample::AllocateSample(mptSmp.nLength, 2 * mptSmp.GetNumChannels())); if(!newSample) return; CopySample, SC::DecodeIdentity>>(newSample, mptSmp.nLength * mptSmp.GetNumChannels(), 1, mptSmp.sample8(), mptSmp.GetSampleSizeInBytes(), 1); mptSmp.uFlags.set(CHN_16BIT); ctrlSmp::ReplaceSample(mptSmp, newSample, mptSmp.nLength, sndFile); } // Highpass if(filter < 0) { auto sampleData = mpt::as_span(mptSmp.sample16(), mptSmp.nLength * mptSmp.GetNumChannels()); for(int i = 0; i < -filter; i++) { int32 mix = sampleData[0]; for(auto &sample : sampleData) { mix = mpt::rshift_signed(sample - mpt::rshift_signed(mix, 1), 1); sample = static_cast(mix); } } } // Volume Fade if(doVolFade) { auto sampleData = mpt::as_span(mptSmp.sample16(), mptSmp.nLength * mptSmp.GetNumChannels()); int32 amp = volFadeFrom << 24, inc = Util::muldivr(volFadeTo - volFadeFrom, 1 << 24, static_cast(sampleData.size())); for(auto &sample : sampleData) { sample = static_cast(Util::muldivr(sample, amp, 100 << 24)); amp += inc; } } // Resonant Filter Sweep if(filterFlags != 0) { auto sampleData = mpt::as_span(mptSmp.sample16(), mptSmp.nLength * mptSmp.GetNumChannels()); int32 cutoff = filterPoint[0].cutoff << 23, resonance = filterPoint[0].resonance << 23; const int32 cutoffStep = numFilterPoints > 1 ? Util::muldivr(filterPoint[1].cutoff - filterPoint[0].cutoff, 1 << 23, static_cast(sampleData.size())) : 0; const int32 resoStep = numFilterPoints > 1 ? Util::muldivr(filterPoint[1].resonance - filterPoint[0].resonance, 1 << 23, static_cast(sampleData.size())) : 0; const uint8 highpass = filterFlags & 2; int32 filterState[3]{}; for(auto &sample : sampleData) { const int32 currentCutoff = cutoff / (1 << 23), currentReso = resonance / (1 << 23); cutoff += cutoffStep; resonance += resoStep; filterState[2] = mpt::rshift_signed(sample, 1) - filterState[0]; filterState[1] += mpt::rshift_signed(currentCutoff * filterState[2], 8); filterState[0] += mpt::rshift_signed(currentCutoff * filterState[1], 6); filterState[0] += mpt::rshift_signed(currentReso * filterState[0], 6); filterState[0] = mpt::rshift_signed(filterState[0], 2); sample = mpt::saturate_cast(filterState[highpass]); } } // Lowpass if(filter > 0) { auto sampleData = mpt::as_span(mptSmp.sample16(), mptSmp.nLength * mptSmp.GetNumChannels()); for(int i = 0; i < filter; i++) { int32 mix = sampleData[0]; for(auto &sample : sampleData) { mix = (sample + sample + mix) / 3; sample = static_cast(mix); } } } // Symphonie normalizes samples at load time (it normalizes them to the sample boost value - but we will use the full 16-bit range) // Indeed, the left and right channel instruments are normalized separately. const auto Normalize = [](auto sampleData) { const auto scale = Util::MaxValueOfType(sampleData[0]); const auto [minElem, maxElem] = std::minmax_element(sampleData.begin(), sampleData.end()); const int max = std::max(-*minElem, +*maxElem); if(max >= scale || max == 0) return; for(auto &v : sampleData) { v = static_cast::type>(static_cast(v) * scale / max); } }; if(mptSmp.uFlags[CHN_16BIT]) Normalize(mpt::as_span(mptSmp.sample16(), mptSmp.nLength * mptSmp.GetNumChannels())); else Normalize(mpt::as_span(mptSmp.sample8(), mptSmp.nLength * mptSmp.GetNumChannels())); // "Non-destructive" over-amplification with hard knee compression if(volume > 100 && volume < 200) { const auto Amplify = [](auto sampleData, const uint8 gain) { const int32 knee = 16384 * (200 - gain) / 100, kneeInv = 32768 - knee; constexpr int32 scale = 1 << (16 - (sizeof(sampleData[0]) * 8)); for(auto &sample : sampleData) { int32 v = sample * scale; if(v > knee) v = (v - knee) * knee / kneeInv + kneeInv; else if(v < -knee) v = (v + knee) * knee / kneeInv - kneeInv; else v = v * kneeInv / knee; sample = mpt::saturate_cast::type>(v / scale); } }; const auto length = mptSmp.nLength * mptSmp.GetNumChannels(); if(mptSmp.uFlags[CHN_16BIT]) Amplify(mpt::span(mptSmp.sample16(), mptSmp.sample16() + length), volume); else Amplify(mpt::span(mptSmp.sample8(), mptSmp.sample8() + length), volume); } // This must be applied last because some sample processors are time-dependent and Symphonie would be doing this during playback instead mptSmp.RemoveAllCuePoints(); if(type == Sustain && numRepetitions > 0 && loopLen > 0) { mptSmp.cues[0] = loopStart + loopLen * (numRepetitions + 1u); mptSmp.nSustainStart = loopStart; // This is of purely informative value and not used for playback mptSmp.nSustainEnd = loopStart + loopLen; if(MAX_SAMPLE_LENGTH / numRepetitions < loopLen) return; if(MAX_SAMPLE_LENGTH - numRepetitions * loopLen < mptSmp.nLength) return; const uint8 bps = mptSmp.GetBytesPerSample(); SmpLength loopEnd = loopStart + loopLen * (numRepetitions + 1); SmpLength newLength = mptSmp.nLength + loopLen * numRepetitions; std::byte *newSample = static_cast(ModSample::AllocateSample(newLength, bps)); if(!newSample) return; mptSmp.nLength = newLength; std::memcpy(newSample, mptSmp.sampleb(), (loopStart + loopLen) * bps); for(uint8 i = 0; i < numRepetitions; i++) { std::memcpy(newSample + (loopStart + loopLen * (i + 1)) * bps, mptSmp.sampleb() + loopStart * bps, loopLen * bps); } std::memcpy(newSample + loopEnd * bps, mptSmp.sampleb() + (loopStart + loopLen) * bps, (newLength - loopEnd) * bps); ctrlSmp::ReplaceSample(mptSmp, newSample, mptSmp.nLength, sndFile); } } std::pair GetSampleLoop(const ModSample &mptSmp) const { if(type != Loop && type != Sustain) return {0, 0}; SmpLength loopStart = static_cast(std::min(loopStartHigh.get(), uint8(100))); SmpLength loopLen = static_cast(std::min(loopLenHigh.get(), uint8(100))); if(sampleFlags & NewLoopSystem) { loopStart = (loopStart << 16) + loopStartFine; loopLen = (loopLen << 16) + loopLenFine; const double loopScale = static_cast(mptSmp.nLength) / (100 << 16); loopStart = mpt::saturate_cast(loopStart * loopScale); loopLen = std::min(mptSmp.nLength - loopStart, mpt::saturate_cast(loopLen * loopScale)); } else if(mptSmp.HasSampleData()) { // The order of operations here may seem weird as it reduces precision, but it's taken directly from the original assembly source (UpdateRecalcLoop) loopStart = ((loopStart << 7) / 100u) * (mptSmp.nLength >> 7); loopLen = std::min(mptSmp.nLength - loopStart, ((loopLen << 7) / 100u) * (mptSmp.nLength >> 7)); const auto FindLoopEnd = [](auto sampleData, const uint8 numChannels, SmpLength loopStart, SmpLength loopLen, const int threshold) { const auto valAtStart = sampleData.data()[loopStart * numChannels]; auto *endPtr = sampleData.data() + (loopStart + loopLen) * numChannels; while(loopLen) { if(std::abs(*endPtr - valAtStart) < threshold) return loopLen; endPtr -= numChannels; loopLen--; } return loopLen; }; if(mptSmp.uFlags[CHN_16BIT]) loopLen = FindLoopEnd(mpt::as_span(mptSmp.sample16(), mptSmp.nLength * mptSmp.GetNumChannels()), mptSmp.GetNumChannels(), loopStart, loopLen, 6 * 256); else loopLen = FindLoopEnd(mpt::as_span(mptSmp.sample8(), mptSmp.nLength * mptSmp.GetNumChannels()), mptSmp.GetNumChannels(), loopStart, loopLen, 6); } return {loopStart, loopLen}; } }; MPT_BINARY_STRUCT(SymInstrument, 256) struct SymSequence { uint16be start; uint16be length; uint16be loop; int16be info; int16be transpose; uint8be padding[6]; }; MPT_BINARY_STRUCT(SymSequence, 16) struct SymPosition { uint8be dummy[4]; uint16be loopNum; uint16be loopCount; // Only used during playback uint16be pattern; uint16be start; uint16be length; uint16be speed; int16be transpose; uint16be eventsPerLine; // Unused uint8be padding[12]; // Used to compare position entries for mapping them to OpenMPT patterns bool operator<(const SymPosition &other) const { return std::tie(pattern, start, length, transpose, speed) < std::tie(other.pattern, other.start, other.length, other.transpose, other.speed); } }; MPT_BINARY_STRUCT(SymPosition, 32) static std::vector DecodeSymChunk(FileReader &file) { std::vector data; const uint32 packedLength = file.ReadUint32BE(); if(!file.CanRead(packedLength)) { file.Skip(file.BytesLeft()); return data; } FileReader chunk = file.ReadChunk(packedLength); if(packedLength >= 10 && chunk.ReadMagic("PACK\xFF\xFF")) { // RLE-compressed chunk uint32 unpackedLength = chunk.ReadUint32BE(); // The best compression ratio can be achieved with type 1, where six bytes turn into up to 255*4 bytes, a ratio of 1:170. uint32 maxLength = packedLength - 10; if(Util::MaxValueOfType(maxLength) / 170 >= maxLength) maxLength *= 170; else maxLength = Util::MaxValueOfType(maxLength); LimitMax(unpackedLength, maxLength); data.resize(unpackedLength); bool done = false; uint32 offset = 0, remain = unpackedLength; while(!done && !chunk.EndOfFile()) { uint8 len; std::array dword; const int8 type = chunk.ReadInt8(); switch(type) { case 0: // Copy raw bytes len = chunk.ReadUint8(); if(remain >= len && chunk.CanRead(len)) { chunk.ReadRaw(mpt::as_span(data).subspan(offset, len)); offset += len; remain -= len; } else { done = true; } break; case 1: // Copy a dword multiple times len = chunk.ReadUint8(); if(remain >= (len * 4u) && chunk.ReadArray(dword)) { remain -= len * 4u; while(len--) { std::copy(dword.begin(), dword.end(), data.begin() + offset); offset += 4; } } else { done = true; } break; case 2: // Copy a dword twice if(remain >= 8 && chunk.ReadArray(dword)) { std::copy(dword.begin(), dword.end(), data.begin() + offset); std::copy(dword.begin(), dword.end(), data.begin() + offset + 4); offset += 8; remain -= 8; } else { done = true; } break; case 3: // Zero bytes len = chunk.ReadUint8(); if(remain >= len) { // vector is already initialized to zero offset += len; remain -= len; } else { done = true; } break; case -1: done = true; break; default: // error done = true; break; } } #ifndef MPT_BUILD_FUZZER // When using a fuzzer, we should not care if the decompressed buffer has the correct size. // This makes finding new interesting test cases much easier. if(remain) std::vector{}.swap(data); #endif } else { // Uncompressed chunk chunk.ReadVector(data, packedLength); } return data; } template static std::vector DecodeSymArray(FileReader &file) { const auto data = DecodeSymChunk(file); FileReader chunk(mpt::as_span(data)); std::vector retVal; chunk.ReadVector(retVal, data.size() / sizeof(T)); return retVal; } static bool ReadRawSymSample(ModSample &sample, FileReader &file) { SampleIO sampleIO(SampleIO::_16bit, SampleIO::mono, SampleIO::bigEndian, SampleIO::signedPCM); SmpLength nullBytes = 0; sample.Initialize(); file.Rewind(); if(file.ReadMagic("MAESTRO")) { file.Seek(12); if(file.ReadUint32BE() == 0) sampleIO |= SampleIO::stereoInterleaved; file.Seek(24); } else if(file.ReadMagic("16BT")) { file.Rewind(); nullBytes = 4; // In Symphonie, the anti-click would take care of those... } else { sampleIO |= SampleIO::_8bit; } sample.nLength = mpt::saturate_cast(file.BytesLeft() / (sampleIO.GetNumChannels() * sampleIO.GetBitDepth() / 8u)); const bool ok = sampleIO.ReadSample(sample, file) > 0; if(ok && nullBytes) std::memset(sample.samplev(), 0, std::min(nullBytes, sample.GetSampleSizeInBytes())); return ok; } static std::vector DecodeSample8(FileReader &file) { auto data = DecodeSymChunk(file); uint8 lastVal = 0; for(auto &val : data) { lastVal += mpt::byte_cast(val); val = mpt::byte_cast(lastVal); } return data; } static std::vector DecodeSample16(FileReader &file) { auto data = DecodeSymChunk(file); std::array buf; constexpr size_t blockSize = buf.size() / 2; // Size of block in 16-bit samples for(size_t block = 0; block < data.size() / buf.size(); block++) { const size_t offset = block * sizeof(buf); uint8 lastVal = 0; // Decode LSBs for(size_t i = 0; i < blockSize; i++) { lastVal += mpt::byte_cast(data[offset + i]); buf[i * 2 + 1] = mpt::byte_cast(lastVal); } // Decode MSBs for(size_t i = 0; i < blockSize; i++) { lastVal += mpt::byte_cast(data[offset + i + blockSize]); buf[i * 2] = mpt::byte_cast(lastVal); } std::copy(buf.begin(), buf.end(), data.begin() + offset); } return data; } static bool ConvertDSP(const SymEvent event, MIDIMacroConfigData::Macro ¯o, const CSoundFile &sndFile) { if(event.command == SymEvent::Filter) { // Symphonie practically uses the same filter for this as for the sample processing. // The cutoff and resonance are an approximation. const uint8 type = event.note % 5u; const uint8 cutoff = sndFile.FrequencyToCutOff(event.param * 10000.0 / 240.0); const uint8 reso = static_cast(std::min(127, event.inst * 127 / 185)); if(type == 1) // lowpass filter macro = MPT_AFORMAT("F0F000{} F0F001{} F0F00200")(mpt::afmt::HEX0<2>(cutoff), mpt::afmt::HEX0<2>(reso)); else if(type == 2) // highpass filter macro = MPT_AFORMAT("F0F000{} F0F001{} F0F00210")(mpt::afmt::HEX0<2>(cutoff), mpt::afmt::HEX0<2>(reso)); else // no filter or unsupported filter type macro = "F0F0007F F0F00100"; return true; } else if(event.command == SymEvent::DSPEcho) { const uint8 type = (event.note < 5) ? event.note : 0; const uint8 length = (event.param < 128) ? event.param : 127; const uint8 feedback = (event.inst < 128) ? event.inst : 127; macro = MPT_AFORMAT("F0F080{} F0F081{} F0F082{}")(mpt::afmt::HEX0<2>(type), mpt::afmt::HEX0<2>(length), mpt::afmt::HEX0<2>(feedback)); return true; } else if(event.command == SymEvent::DSPDelay) { // DSP first has to be turned on from the Symphonie GUI before it can be used in a track (unlike Echo), // so it's not implemented for now. return false; } return false; } CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderSymMOD(MemoryFileReader file, const uint64 *pfilesize) { MPT_UNREFERENCED_PARAMETER(pfilesize); SymFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) return ProbeWantMoreData; if(!fileHeader.Validate()) return ProbeFailure; if(!file.CanRead(sizeof(uint32be))) return ProbeWantMoreData; if(file.ReadInt32BE() >= 0) return ProbeFailure; return ProbeSuccess; } bool CSoundFile::ReadSymMOD(FileReader &file, ModLoadingFlags loadFlags) { file.Rewind(); SymFileHeader fileHeader; if(!file.ReadStruct(fileHeader) || !fileHeader.Validate()) return false; if(file.ReadInt32BE() >= 0) return false; else if(loadFlags == onlyVerifyHeader) return true; InitializeGlobals(MOD_TYPE_MPT); m_SongFlags.set(SONG_LINEARSLIDES | SONG_EXFILTERRANGE | SONG_IMPORTED); m_playBehaviour = GetDefaultPlaybackBehaviour(MOD_TYPE_IT); m_playBehaviour.reset(kITShortSampleRetrig); enum class ChunkType : int32 { NumChannels = -1, TrackLength = -2, PatternSize = -3, NumInstruments = -4, EventSize = -5, Tempo = -6, ExternalSamples = -7, PositionList = -10, SampleFile = -11, EmptySample = -12, PatternEvents = -13, InstrumentList = -14, Sequences = -15, InfoText = -16, SamplePacked = -17, SamplePacked16 = -18, InfoType = -19, InfoBinary = -20, InfoString = -21, SampleBoost = 10, // All samples will be normalized to this value StereoDetune = 11, // Note: Not affected by no-DSP flag in instrument! So this would need to have its own plugin... StereoPhase = 12, }; uint32 trackLen = 0; uint16 sampleBoost = 2500; bool isSymphoniePro = false; bool externalSamples = false; std::vector positions; std::vector sequences; std::vector patternData; std::vector instruments; file.SkipBack(sizeof(int32)); while(file.CanRead(sizeof(int32))) { const ChunkType chunkType = static_cast(file.ReadInt32BE()); switch(chunkType) { // Simple values case ChunkType::NumChannels: if(auto numChannels = static_cast(file.ReadUint32BE()); !m_nChannels && numChannels > 0 && numChannels <= MAX_BASECHANNELS) { m_nChannels = numChannels; m_nSamplePreAmp = Clamp(512 / m_nChannels, 16, 128); } break; case ChunkType::TrackLength: trackLen = file.ReadUint32BE(); if(trackLen > 1024) return false; break; case ChunkType::EventSize: if(auto eventSize = (file.ReadUint32BE() & 0xFFFF); eventSize != sizeof(SymEvent)) return false; break; case ChunkType::Tempo: m_nDefaultTempo = TEMPO(1.24 * std::min(file.ReadUint32BE(), uint32(800))); break; // Unused values case ChunkType::NumInstruments: // determined from # of instrument headers instead case ChunkType::PatternSize: file.Skip(4); break; case ChunkType::SampleBoost: sampleBoost = static_cast(Clamp(file.ReadUint32BE(), 0u, 10000u)); isSymphoniePro = true; break; case ChunkType::StereoDetune: case ChunkType::StereoPhase: isSymphoniePro = true; if(uint32 val = file.ReadUint32BE(); val != 0) AddToLog(LogWarning, U_("Stereo Detune / Stereo Phase is not supported")); break; case ChunkType::ExternalSamples: file.Skip(4); if(!m_nSamples) externalSamples = true; break; // Binary chunk types case ChunkType::PositionList: if((loadFlags & loadPatternData) && positions.empty()) positions = DecodeSymArray(file); else file.Skip(file.ReadUint32BE()); break; case ChunkType::SampleFile: case ChunkType::SamplePacked: case ChunkType::SamplePacked16: if(m_nSamples >= instruments.size()) break; if(!externalSamples && (loadFlags & loadSampleData) && CanAddMoreSamples()) { const SAMPLEINDEX sample = ++m_nSamples; std::vector unpackedSample; FileReader chunk; if(chunkType == ChunkType::SampleFile) { chunk = file.ReadChunk(file.ReadUint32BE()); } else if(chunkType == ChunkType::SamplePacked) { unpackedSample = DecodeSample8(file); chunk = FileReader(mpt::as_span(unpackedSample)); } else // SamplePacked16 { unpackedSample = DecodeSample16(file); chunk = FileReader(mpt::as_span(unpackedSample)); } if(!ReadIFFSample(sample, chunk) && !ReadWAVSample(sample, chunk) && !ReadAIFFSample(sample, chunk) && !ReadRawSymSample(Samples[sample], chunk)) { AddToLog(LogWarning, U_("Unknown sample format.")); } // Symphonie represents stereo instruments as two consecutive mono instruments which are // automatically played at the same time. If this one uses a stereo sample, split it // and map two OpenMPT instruments to the stereo halves to ensure correct playback if(Samples[sample].uFlags[CHN_STEREO] && CanAddMoreSamples()) { const SAMPLEINDEX sampleL = ++m_nSamples; ctrlSmp::SplitStereo(Samples[sample], Samples[sampleL], Samples[sample], *this); Samples[sampleL].filename = "Left"; Samples[sample].filename = "Right"; } else if(sample < instruments.size() && instruments[sample].channel == SymInstrument::StereoR && CanAddMoreSamples()) { // Prevent misalignment of samples in exit.symmod (see condition in MoveNextMonoInstrument in Symphonie source) m_nSamples++; } } else { // Skip sample file.Skip(file.ReadUint32BE()); } break; case ChunkType::EmptySample: if(CanAddMoreSamples()) m_nSamples++; break; case ChunkType::PatternEvents: if((loadFlags & loadPatternData) && patternData.empty()) patternData = DecodeSymArray(file); else file.Skip(file.ReadUint32BE()); break; case ChunkType::InstrumentList: if(instruments.empty()) instruments = DecodeSymArray(file); else file.Skip(file.ReadUint32BE()); break; case ChunkType::Sequences: if((loadFlags & loadPatternData) && sequences.empty()) sequences = DecodeSymArray(file); else file.Skip(file.ReadUint32BE()); break; case ChunkType::InfoText: if(const auto text = DecodeSymChunk(file); !text.empty()) m_songMessage.Read(text.data(), text.size(), SongMessage::leLF); break; // Unused binary chunks case ChunkType::InfoType: case ChunkType::InfoBinary: case ChunkType::InfoString: file.Skip(file.ReadUint32BE()); break; // Unrecognized chunk/value type default: return false; } } if(!m_nChannels || !trackLen || instruments.empty()) return false; if((loadFlags & loadPatternData) && (positions.empty() || patternData.empty() || sequences.empty())) return false; // Let's hope noone is going to use the 256th instrument ;) if(instruments.size() >= MAX_INSTRUMENTS) instruments.resize(MAX_INSTRUMENTS - 1u); m_nInstruments = static_cast(instruments.size()); static_assert(MAX_SAMPLES >= MAX_INSTRUMENTS); m_nSamples = std::max(m_nSamples, m_nInstruments); // Supporting this is probably rather useless, as the paths will always be full Amiga paths. We just take the filename without path for now. if(externalSamples) { #ifdef MPT_EXTERNAL_SAMPLES m_nSamples = m_nInstruments; for(SAMPLEINDEX sample = 1; sample <= m_nSamples; sample++) { const SymInstrument &symInst = instruments[sample - 1]; if(symInst.IsEmpty() || symInst.IsVirtual()) continue; auto filename = mpt::PathString::FromUnicode(mpt::ToUnicode(mpt::Charset::ISO8859_1, symInst.GetName())); if(file.GetOptionalFileName()) filename = file.GetOptionalFileName()->GetPath() + filename.GetFullFileName(); if(!LoadExternalSample(sample, filename)) AddToLog(LogError, MPT_UFORMAT("Unable to load sample {}: {}")(sample, filename)); else ResetSamplePath(sample); if(Samples[sample].uFlags[CHN_STEREO] && sample < m_nSamples) { const SAMPLEINDEX sampleL = sample + 1; ctrlSmp::SplitStereo(Samples[sample], Samples[sampleL], Samples[sample], *this); Samples[sampleL].filename = "Left"; Samples[sample].filename = "Right"; sample++; } } #else AddToLog(LogWarning, U_("External samples are not supported.")); #endif // MPT_EXTERNAL_SAMPLES } // Convert instruments for(int pass = 0; pass < 2; pass++) { for(INSTRUMENTINDEX ins = 1; ins <= m_nInstruments; ins++) { SymInstrument &symInst = instruments[ins - 1]; if(symInst.IsEmpty()) continue; // First load all regular instruments, and when we have the required information, render the virtual ones if(symInst.IsVirtual() != (pass == 1)) continue; SAMPLEINDEX sample = ins; if(symInst.virt.header.IsVirtual()) { const uint8 firstSource = symInst.virt.noteEvents[0].inst; ModSample &target = Samples[sample]; if(symInst.virt.Render(*this, symInst.sampleFlags & SymInstrument::AsQueue, target, sampleBoost)) { m_szNames[sample] = "Virtual"; if(firstSource < instruments.size()) symInst.downsample += instruments[firstSource].downsample; } else { sample = firstSource + 1; } } else if(symInst.virt.header.IsTranswave()) { const SymTranswaveInst transwaveInst = symInst.GetTranswave(); const auto &trans1 = transwaveInst.points[0], &trans2 = transwaveInst.points[1]; if(trans1.sourceIns < m_nSamples) { const ModSample emptySample; const ModSample &smp1 = Samples[trans1.sourceIns + 1]; const ModSample &smp2 = trans2.sourceIns < m_nSamples ? Samples[trans2.sourceIns + 1] : emptySample; ModSample &target = Samples[sample]; if(transwaveInst.Render(smp1, smp2, target)) { m_szNames[sample] = "Transwave"; // Transwave instruments play an octave lower than the original source sample, but are 4x oversampled, // so effectively they play an octave higher symInst.transpose += 12; } } } if(ModInstrument *instr = AllocateInstrument(ins, sample); instr != nullptr && sample <= m_nSamples) symInst.ConvertToMPT(*instr, Samples[sample], *this); } } // Convert patterns // map Symphonie positions to converted patterns std::map patternMap; // map DSP commands to MIDI macro numbers std::map macroMap; bool useDSP = false; const uint32 patternSize = m_nChannels * trackLen; const PATTERNINDEX numPatterns = mpt::saturate_cast(patternData.size() / patternSize); Patterns.ResizeArray(numPatterns); Order().clear(); struct ChnState { float curVolSlide = 0; // Current volume slide factor of a channel float curVolSlideAmt = 0; // Cumulative volume slide amount float curPitchSlide = 0; // Current pitch slide factor of a channel float curPitchSlideAmt = 0; // Cumulative pitch slide amount bool stopped = false; // Sample paused or not (affects volume and pitch slides) uint8 lastNote = 0; // Last note played on a channel uint8 lastInst = 0; // Last instrument played on a channel uint8 lastVol = 64; // Last specified volume of a channel (to avoid excessive Mxx commands) uint8 channelVol = 100; // Volume multiplier, 0...100 uint8 calculatedVol = 64; // Final channel volume uint8 fromAdd = 0; // Base sample offset for FROM and FR&P effects uint8 curVibrato = 0; uint8 curTremolo = 0; uint8 sampleVibSpeed = 0; uint8 sampleVibDepth = 0; uint8 tonePortaAmt = 0; uint16 sampleVibPhase = 0; uint16 retriggerRemain = 0; uint16 tonePortaRemain = 0; }; std::vector chnStates(m_nChannels); // In Symphonie, sequences represent the structure of a song, and not separate songs like in OpenMPT. Hence they will all be loaded into the same ModSequence. for(SymSequence &seq : sequences) { if(seq.info == 1) continue; if(seq.info == -1) break; if(seq.start >= positions.size() || seq.length > positions.size() || seq.length == 0 || positions.size() - seq.length < seq.start) continue; auto seqPositions = mpt::as_span(positions).subspan(seq.start, seq.length); // Sequences are all part of the same song, just add a skip index as a divider ModSequence &order = Order(); if(!order.empty()) order.push_back(ModSequence::GetIgnoreIndex()); for(auto &pos : seqPositions) { // before checking the map, apply the sequence transpose value pos.transpose += seq.transpose; // pattern already converted? PATTERNINDEX patternIndex = 0; if(patternMap.count(pos)) { patternIndex = patternMap[pos]; } else if(loadFlags & loadPatternData) { // Convert pattern now patternIndex = Patterns.InsertAny(pos.length); if(patternIndex == PATTERNINDEX_INVALID) break; patternMap[pos] = patternIndex; if(pos.pattern >= numPatterns || pos.start >= trackLen) continue; uint8 patternSpeed = static_cast(pos.speed); // This may intentionally read into the next pattern auto srcEvent = patternData.cbegin() + (pos.pattern * patternSize) + (pos.start * m_nChannels); const SymEvent emptyEvent{}; ModCommand syncPlayCommand; for(ROWINDEX row = 0; row < pos.length; row++) { ModCommand *rowBase = Patterns[patternIndex].GetpModCommand(row, 0); bool applySyncPlay = false; for(CHANNELINDEX chn = 0; chn < m_nChannels; chn++) { ModCommand &m = rowBase[chn]; const SymEvent &event = (srcEvent != patternData.cend()) ? *srcEvent : emptyEvent; if(srcEvent != patternData.cend()) srcEvent++; int8 note = (event.note >= 0 && event.note <= 84) ? event.note + 25 : -1; uint8 origInst = event.inst; uint8 mappedInst = 0; if(origInst < instruments.size()) { mappedInst = static_cast(origInst + 1); if(!(instruments[origInst].instFlags & SymInstrument::NoTranspose) && note >= 0) note = Clamp(static_cast(note + pos.transpose), NOTE_MIN, NOTE_MAX); } // If we duplicated a stereo channel to this cell but the event is non-empty, remove it again. if(m.note != NOTE_NONE && (event.command != SymEvent::KeyOn || event.note != -1 || event.inst != 0 || event.param != 0) && m.instr > 0 && m.instr <= instruments.size() && instruments[m.instr - 1].channel == SymInstrument::StereoR) { m.Clear(); } auto &chnState = chnStates[chn]; if(applySyncPlay) { applySyncPlay = false; m = syncPlayCommand; if(m.command == CMD_NONE && chnState.calculatedVol != chnStates[chn - 1].calculatedVol) { m.command = CMD_CHANNELVOLUME; m.param = chnState.calculatedVol = chnStates[chn - 1].calculatedVol; } if(!event.IsGlobal()) continue; } bool applyVolume = false; switch(static_cast(event.command.get())) { case SymEvent::KeyOn: if(event.param > SymEvent::VolCommand) { switch(event.param) { case SymEvent::StopSample: m.volcmd = VOLCMD_PLAYCONTROL; m.vol = 0; chnState.stopped = true; break; case SymEvent::ContSample: m.volcmd = VOLCMD_PLAYCONTROL; m.vol = 1; chnState.stopped = false; break; case SymEvent::KeyOff: if(m.note == NOTE_NONE) m.note = chnState.lastNote; m.volcmd = VOLCMD_OFFSET; m.vol = 1; break; case SymEvent::SpeedDown: if(patternSpeed > 1) { m.command = CMD_SPEED; m.param = --patternSpeed; } break; case SymEvent::SpeedUp: if(patternSpeed < 0xFF) { m.command = CMD_SPEED; m.param = ++patternSpeed; } break; case SymEvent::SetPitch: chnState.lastNote = note; if(mappedInst != chnState.lastInst) break; m.note = note; m.command = CMD_TONEPORTAMENTO; m.param = 0xFF; chnState.curPitchSlide = 0; chnState.tonePortaRemain = 0; break; // fine portamentos with range up to half a semitone case SymEvent::PitchUp: m.command = CMD_PORTAMENTOUP; m.param = 0xF2; break; case SymEvent::PitchDown: m.command = CMD_PORTAMENTODOWN; m.param = 0xF2; break; case SymEvent::PitchUp2: m.command = CMD_PORTAMENTOUP; m.param = 0xF4; break; case SymEvent::PitchDown2: m.command = CMD_PORTAMENTODOWN; m.param = 0xF4; break; case SymEvent::PitchUp3: m.command = CMD_PORTAMENTOUP; m.param = 0xF8; break; case SymEvent::PitchDown3: m.command = CMD_PORTAMENTODOWN; m.param = 0xF8; break; } } else { if(event.note >= 0 || event.param < 100) { if(event.note >= 0) { m.note = chnState.lastNote = note; m.instr = chnState.lastInst = mappedInst; chnState.curPitchSlide = 0; chnState.tonePortaRemain = 0; } if(event.param > 0) { chnState.lastVol = mpt::saturate_round(event.param * 0.64); if(chnState.curVolSlide != 0) applyVolume = true; chnState.curVolSlide = 0; } } } if(const uint8 newVol = static_cast(Util::muldivr_unsigned(chnState.lastVol, chnState.channelVol, 100)); applyVolume || chnState.calculatedVol != newVol) { chnState.calculatedVol = newVol; m.command = CMD_CHANNELVOLUME; m.param = newVol; } // Key-On commands with stereo instruments are played on both channels - unless there's already some sort of event if(event.note > 0 && (chn < m_nChannels - 1) && !(chn % 2u) && origInst < instruments.size() && instruments[origInst].channel == SymInstrument::StereoL) { ModCommand &next = rowBase[chn + 1]; next = m; next.instr++; chnStates[chn + 1].lastVol = chnState.lastVol; chnStates[chn + 1].curVolSlide = chnState.curVolSlide; chnStates[chn + 1].curVolSlideAmt = chnState.curVolSlideAmt; chnStates[chn + 1].curPitchSlide = chnState.curPitchSlide; chnStates[chn + 1].curPitchSlideAmt = chnState.curPitchSlideAmt; chnStates[chn + 1].retriggerRemain = chnState.retriggerRemain; } break; // volume effects // Symphonie has very fine fractional volume slides which are applied at the output sample rate, // rather than per tick or per row, so instead let's simulate it based on the pattern speed // by keeping track of the volume and using normal volume commands // the math here is an approximation which works fine for most songs case SymEvent::VolSlideUp: chnState.curVolSlideAmt = 0; chnState.curVolSlide = event.param * 0.0333f; break; case SymEvent::VolSlideDown: chnState.curVolSlideAmt = 0; chnState.curVolSlide = event.param * -0.0333f; break; case SymEvent::AddVolume: m.command = m.param = 0; break; case SymEvent::Tremolo: { // both tremolo speed and depth can go much higher than OpenMPT supports, // but modules will probably use pretty sane, supportable values anyway // TODO: handle very small nonzero params uint8 speed = std::min(15, event.inst >> 3); uint8 depth = std::min(15, event.param >> 3); chnState.curTremolo = (speed << 4) | depth; } break; // pitch effects // Pitch slides have a similar granularity to volume slides, and are approximated // the same way here based on a rough comparison against Exx/Fxx slides case SymEvent::PitchSlideUp: chnState.curPitchSlideAmt = 0; chnState.curPitchSlide = event.param * 0.0333f; chnState.tonePortaRemain = 0; break; case SymEvent::PitchSlideDown: chnState.curPitchSlideAmt = 0; chnState.curPitchSlide = event.param * -0.0333f; chnState.tonePortaRemain = 0; break; case SymEvent::PitchSlideTo: if(note >= 0 && event.param > 0) { const int distance = std::abs((note - chnState.lastNote) * 32); chnState.curPitchSlide = 0; m.note = chnState.lastNote = note; m.command = CMD_TONEPORTAMENTO; chnState.tonePortaAmt = m.param = mpt::saturate_cast(distance / (2 * event.param)); chnState.tonePortaRemain = static_cast(distance - std::min(distance, chnState.tonePortaAmt * (patternSpeed - 1))); } break; case SymEvent::AddPitch: // "The range (-128...127) is about 4 half notes." m.command = m.param = 0; break; case SymEvent::Vibrato: { // both vibrato speed and depth can go much higher than OpenMPT supports, // but modules will probably use pretty sane, supportable values anyway // TODO: handle very small nonzero params uint8 speed = std::min(15, event.inst >> 3); uint8 depth = std::min(15, event.param); chnState.curVibrato = (speed << 4) | depth; } break; case SymEvent::AddHalfTone: m.note = chnState.lastNote = Clamp(static_cast(chnState.lastNote + event.param), NOTE_MIN, NOTE_MAX); m.command = CMD_TONEPORTAMENTO; m.param = 0xFF; chnState.tonePortaRemain = 0; break; // DSP effects case SymEvent::Filter: #ifndef NO_PLUGINS case SymEvent::DSPEcho: case SymEvent::DSPDelay: #endif if(macroMap.count(event)) { m.command = CMD_MIDI; m.param = macroMap[event]; } else if(macroMap.size() < m_MidiCfg.Zxx.size()) { uint8 param = static_cast(macroMap.size()); if(ConvertDSP(event, m_MidiCfg.Zxx[param], *this)) { m.command = CMD_MIDI; m.param = macroMap[event] = 0x80 | param; if(event.command == SymEvent::DSPEcho || event.command == SymEvent::DSPDelay) useDSP = true; } } break; // other effects case SymEvent::Retrig: // This plays the note times every +1 ticks. // The effect continues on the following rows until the correct amount is reached. if(event.param < 1) break; m.command = CMD_RETRIG; m.param = static_cast(std::min(15, event.inst + 1)); chnState.retriggerRemain = event.param * (event.inst + 1u); break; case SymEvent::SetSpeed: m.command = CMD_SPEED; m.param = patternSpeed = event.param ? event.param : 4u; break; // TODO this applies a fade on the sample level case SymEvent::Emphasis: m.command = CMD_NONE; break; case SymEvent::CV: if(event.note == 0 || event.note == 4) { uint8 pan = (event.note == 4) ? event.inst : 128; uint8 vol = std::min(event.param, 100); uint8 volL = static_cast(vol * std::min(128, 256 - pan) / 128); uint8 volR = static_cast(vol * std::min(uint8(128), pan) / 128); if(volL != chnState.channelVol) { chnState.channelVol = volL; m.command = CMD_CHANNELVOLUME; m.param = chnState.calculatedVol = static_cast(Util::muldivr_unsigned(chnState.lastVol, chnState.channelVol, 100)); } if(event.note == 4 && chn < (m_nChannels - 1) && chnStates[chn + 1].channelVol != volR) { chnStates[chn + 1].channelVol = volR; ModCommand &next = rowBase[chn + 1]; next.command = CMD_CHANNELVOLUME; next.param = chnState.calculatedVol = static_cast(Util::muldivr_unsigned(chnState.lastVol, chnState.channelVol, 100)); } } break; case SymEvent::CVAdd: // Effect doesn't seem to exist in UI and code looks like a no-op m.command = CMD_NONE; break; case SymEvent::SetFromAdd: chnState.fromAdd = event.param; chnState.sampleVibSpeed = 0; chnState.sampleVibDepth = 0; break; case SymEvent::FromAdd: // TODO need to verify how signedness of this value is treated // C = -128...+127 //FORMEL: Neuer FADD := alter FADD + C* Samplelaenge/16384 chnState.fromAdd += event.param; break; case SymEvent::SampleVib: chnState.sampleVibSpeed = event.inst; chnState.sampleVibDepth = event.param; break; // sample effects case SymEvent::FromAndPitch: chnState.lastNote = note; m.instr = chnState.lastInst = mappedInst; [[fallthrough]]; case SymEvent::ReplayFrom: m.note = chnState.lastNote; if(note >= 0) m.instr = chnState.lastInst = mappedInst; if(event.command == SymEvent::ReplayFrom) { m.volcmd = VOLCMD_TONEPORTAMENTO; m.vol = 1; } // don't always add the command, because often FromAndPitch is used with offset 0 // to act as a key-on which doesn't cancel volume slides, etc if(event.param || chnState.fromAdd || chnState.sampleVibDepth) { double sampleVib = 0.0; if(chnState.sampleVibDepth) sampleVib = chnState.sampleVibDepth * (std::sin(chnState.sampleVibPhase * (mpt::numbers::pi * 2.0 / 1024.0) + 1.5 * mpt::numbers::pi) - 1.0) / 4.0; m.command = CMD_OFFSETPERCENTAGE; m.param = mpt::saturate_round(event.param + chnState.fromAdd + sampleVib); } chnState.tonePortaRemain = 0; break; } // Any event which plays a note should re-enable continuous effects if(m.note != NOTE_NONE) chnState.stopped = false; else if(chnState.stopped) continue; if(chnState.retriggerRemain) { chnState.retriggerRemain = std::max(chnState.retriggerRemain, static_cast(patternSpeed)) - patternSpeed; if(m.command == CMD_NONE) { m.command = CMD_RETRIG; m.param = 0; } } // Handle fractional volume slides if(chnState.curVolSlide != 0) { chnState.curVolSlideAmt += chnState.curVolSlide * patternSpeed; if(m.command == CMD_NONE) { if(patternSpeed > 1 && chnState.curVolSlideAmt >= (patternSpeed - 1)) { uint8 slideAmt = std::min(15, mpt::saturate_round(chnState.curVolSlideAmt / (patternSpeed - 1))); chnState.curVolSlideAmt -= slideAmt * (patternSpeed - 1); // normal slide up m.command = CMD_CHANNELVOLSLIDE; m.param = slideAmt << 4; } else if(chnState.curVolSlideAmt >= 1.0f) { uint8 slideAmt = std::min(15, mpt::saturate_round(chnState.curVolSlideAmt)); chnState.curVolSlideAmt -= slideAmt; // fine slide up m.command = CMD_CHANNELVOLSLIDE; m.param = (slideAmt << 4) | 0x0F; } else if(patternSpeed > 1 && chnState.curVolSlideAmt <= -(patternSpeed - 1)) { uint8 slideAmt = std::min(15, mpt::saturate_round(-chnState.curVolSlideAmt / (patternSpeed - 1))); chnState.curVolSlideAmt += slideAmt * (patternSpeed - 1); // normal slide down m.command = CMD_CHANNELVOLSLIDE; m.param = slideAmt; } else if(chnState.curVolSlideAmt <= -1.0f) { uint8 slideAmt = std::min(14, mpt::saturate_round(-chnState.curVolSlideAmt)); chnState.curVolSlideAmt += slideAmt; // fine slide down m.command = CMD_CHANNELVOLSLIDE; m.param = slideAmt | 0xF0; } } } // Handle fractional pitch slides if(chnState.curPitchSlide != 0) { chnState.curPitchSlideAmt += chnState.curPitchSlide * patternSpeed; if(m.command == CMD_NONE) { if(patternSpeed > 1 && chnState.curPitchSlideAmt >= (patternSpeed - 1)) { uint8 slideAmt = std::min(0xDF, mpt::saturate_round(chnState.curPitchSlideAmt / (patternSpeed - 1))); chnState.curPitchSlideAmt -= slideAmt * (patternSpeed - 1); // normal slide up m.command = CMD_PORTAMENTOUP; m.param = slideAmt; } else if(chnState.curPitchSlideAmt >= 1.0f) { uint8 slideAmt = std::min(15, mpt::saturate_round(chnState.curPitchSlideAmt)); chnState.curPitchSlideAmt -= slideAmt; // fine slide up m.command = CMD_PORTAMENTOUP; m.param = slideAmt | 0xF0; } else if(patternSpeed > 1 && chnState.curPitchSlideAmt <= -(patternSpeed - 1)) { uint8 slideAmt = std::min(0xDF, mpt::saturate_round(-chnState.curPitchSlideAmt / (patternSpeed - 1))); chnState.curPitchSlideAmt += slideAmt * (patternSpeed - 1); // normal slide down m.command = CMD_PORTAMENTODOWN; m.param = slideAmt; } else if(chnState.curPitchSlideAmt <= -1.0f) { uint8 slideAmt = std::min(14, mpt::saturate_round(-chnState.curPitchSlideAmt)); chnState.curPitchSlideAmt += slideAmt; // fine slide down m.command = CMD_PORTAMENTODOWN; m.param = slideAmt | 0xF0; } } // TODO: use volume column if effect column is occupied else if(m.volcmd == VOLCMD_NONE) { if(patternSpeed > 1 && chnState.curPitchSlideAmt / 4 >= (patternSpeed - 1)) { uint8 slideAmt = std::min(9, mpt::saturate_round(chnState.curPitchSlideAmt / (patternSpeed - 1)) / 4); chnState.curPitchSlideAmt -= slideAmt * (patternSpeed - 1) * 4; m.volcmd = VOLCMD_PORTAUP; m.vol = slideAmt; } else if(patternSpeed > 1 && chnState.curPitchSlideAmt / 4 <= -(patternSpeed - 1)) { uint8 slideAmt = std::min(9, mpt::saturate_round(-chnState.curPitchSlideAmt / (patternSpeed - 1)) / 4); chnState.curPitchSlideAmt += slideAmt * (patternSpeed - 1) * 4; m.volcmd = VOLCMD_PORTADOWN; m.vol = slideAmt; } } } // Vibrato and Tremolo if(m.command == CMD_NONE && chnState.curVibrato != 0) { m.command = CMD_VIBRATO; m.param = chnState.curVibrato; } if(m.command == CMD_NONE && chnState.curTremolo != 0) { m.command = CMD_TREMOLO; m.param = chnState.curTremolo; } // Tone Portamento if(m.command != CMD_TONEPORTAMENTO && chnState.tonePortaRemain) { if(m.command == CMD_NONE) m.command = CMD_TONEPORTAMENTO; else m.volcmd = VOLCMD_TONEPORTAMENTO; chnState.tonePortaRemain -= std::min(chnState.tonePortaRemain, static_cast(chnState.tonePortaAmt * (patternSpeed - 1))); } chnState.sampleVibPhase = (chnState.sampleVibPhase + chnState.sampleVibSpeed * patternSpeed) & 1023; if(!(chn % 2u) && chnState.lastInst && chnState.lastInst <= instruments.size() && (instruments[chnState.lastInst - 1].instFlags & SymInstrument::SyncPlay)) { syncPlayCommand = m; applySyncPlay = true; if(syncPlayCommand.instr && instruments[chnState.lastInst - 1].channel == SymInstrument::StereoL) syncPlayCommand.instr++; } } } Patterns[patternIndex].WriteEffect(EffectWriter(CMD_SPEED, static_cast(pos.speed)).Row(0).RetryNextRow()); } order.insert(order.GetLength(), std::max(pos.loopNum.get(), uint16(1)), patternIndex); // Undo transpose tweak pos.transpose -= seq.transpose; } } #ifndef NO_PLUGINS if(useDSP) { SNDMIXPLUGIN &plugin = m_MixPlugins[0]; plugin.Destroy(); memcpy(&plugin.Info.dwPluginId1, "SymM", 4); memcpy(&plugin.Info.dwPluginId2, "Echo", 4); plugin.Info.routingFlags = SNDMIXPLUGININFO::irAutoSuspend; plugin.Info.mixMode = 0; plugin.Info.gain = 10; plugin.Info.reserved = 0; plugin.Info.dwOutputRouting = 0; std::fill(plugin.Info.dwReserved, plugin.Info.dwReserved + std::size(plugin.Info.dwReserved), 0); plugin.Info.szName = "Echo"; plugin.Info.szLibraryName = "SymMOD Echo"; m_MixPlugins[1].Info.szName = "No Echo"; } #endif // NO_PLUGINS // Channel panning for(CHANNELINDEX chn = 0; chn < m_nChannels; chn++) { InitChannel(chn); ChnSettings[chn].nPan = (chn & 1) ? 256 : 0; ChnSettings[chn].nMixPlugin = useDSP ? 1 : 0; // For MIDI macros controlling the echo DSP } m_modFormat.formatName = U_("Symphonie"); m_modFormat.type = U_("symmod"); if(!isSymphoniePro) m_modFormat.madeWithTracker = U_("Symphonie"); // or Symphonie Jr else if(instruments.size() <= 128) m_modFormat.madeWithTracker = U_("Symphonie Pro"); else m_modFormat.madeWithTracker = U_("Symphonie Pro 256"); m_modFormat.charset = mpt::Charset::ISO8859_1; return true; } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Load_uax.cpp0000644000175000017500000000467714024476651020362 00000000000000/* * Load_uax.cpp * ------------ * Purpose: UAX (Unreal Sounds) module ripper * Notes : The sounds are read into module sample slots. * Authors: Johannes Schultz (inspired by code from http://wiki.beyondunreal.com/Legacy:Package_File_Format) * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Loaders.h" #include "UMXTools.h" OPENMPT_NAMESPACE_BEGIN CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderUAX(MemoryFileReader file, const uint64 *pfilesize) { return UMX::ProbeFileHeader(file, pfilesize, "sound"); } bool CSoundFile::ReadUAX(FileReader &file, ModLoadingFlags loadFlags) { file.Rewind(); UMX::FileHeader fileHeader; if(!file.ReadStruct(fileHeader) || !fileHeader.IsValid()) return false; // Note that this can be a false positive, e.g. Unreal maps will have music and sound // in their name table because they usually import such files. However, it spares us // from wildly seeking through the file, as the name table is usually right at the // start of the file, so it is hopefully a good enough heuristic for our purposes. if(!UMX::FindNameTableEntry(file, fileHeader, "sound")) return false; else if(!file.CanRead(fileHeader.GetMinimumAdditionalFileSize())) return false; else if(loadFlags == onlyVerifyHeader) return true; const std::vector names = UMX::ReadNameTable(file, fileHeader); const std::vector classes = UMX::ReadImportTable(file, fileHeader, names); InitializeGlobals(); m_modFormat.formatName = MPT_UFORMAT("Unreal Package v{}")(fileHeader.packageVersion); m_modFormat.type = U_("uax"); m_modFormat.charset = mpt::Charset::Windows1252; // Read export table file.Seek(fileHeader.exportOffset); for(uint32 i = 0; i < fileHeader.exportCount && file.CanRead(8); i++) { auto [fileChunk, objName] = UMX::ReadExportTableEntry(file, fileHeader, classes, names, "sound"); if(!fileChunk.IsValid()) continue; if(CanAddMoreSamples()) { // Read as sample if(ReadSampleFromFile(GetNumSamples() + 1, fileChunk, true)) { if(objName > 0 && static_cast(objName) < names.size()) { m_szNames[GetNumSamples()] = names[objName]; } } } } if(m_nSamples != 0) { InitializeChannels(); SetType(MOD_TYPE_MPT); m_ContainerType = MOD_CONTAINERTYPE_UAX; m_nChannels = 4; Patterns.Insert(0, 64); Order().assign(1, 0); return true; } else { return false; } } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Load_ult.cpp0000644000175000017500000002753714137245271020366 00000000000000/* * Load_ult.cpp * ------------ * Purpose: ULT (UltraTracker) module loader * Notes : (currently none) * Authors: Storlek (Original author - http://schismtracker.org/ - code ported with permission) * Johannes Schultz (OpenMPT Port, tweaks) * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Loaders.h" OPENMPT_NAMESPACE_BEGIN struct UltFileHeader { char signature[14]; // "MAS_UTrack_V00" uint8 version; // '1'...'4' char songName[32]; // Song Name, not guaranteed to be null-terminated uint8 messageLength; // Number of Lines }; MPT_BINARY_STRUCT(UltFileHeader, 48) struct UltSample { enum UltSampleFlags { ULT_16BIT = 4, ULT_LOOP = 8, ULT_PINGPONGLOOP = 16, }; char name[32]; char filename[12]; uint32le loopStart; uint32le loopEnd; uint32le sizeStart; uint32le sizeEnd; uint8le volume; // 0-255, apparently prior to 1.4 this was logarithmic? uint8le flags; // above uint16le speed; // only exists for 1.4+ int16le finetune; // Convert an ULT sample header to OpenMPT's internal sample header. void ConvertToMPT(ModSample &mptSmp) const { mptSmp.Initialize(); mptSmp.Set16BitCuePoints(); mptSmp.filename = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, filename); if(sizeEnd <= sizeStart) { return; } mptSmp.nLength = sizeEnd - sizeStart; mptSmp.nSustainStart = loopStart; mptSmp.nSustainEnd = std::min(static_cast(loopEnd), mptSmp.nLength); mptSmp.nVolume = volume; mptSmp.nC5Speed = speed; if(finetune) { mptSmp.Transpose(finetune / (12.0 * 32768.0)); } if(flags & ULT_LOOP) mptSmp.uFlags.set(CHN_SUSTAINLOOP); if(flags & ULT_PINGPONGLOOP) mptSmp.uFlags.set(CHN_PINGPONGSUSTAIN); if(flags & ULT_16BIT) { mptSmp.uFlags.set(CHN_16BIT); mptSmp.nSustainStart /= 2; mptSmp.nSustainEnd /= 2; } } }; MPT_BINARY_STRUCT(UltSample, 66) /* Unhandled effects: 5x1 - do not loop sample (x is unused) E0x - set vibrato strength (2 is normal) The logarithmic volume scale used in older format versions here, or pretty much anywhere for that matter. I don't even think Ultra Tracker tries to convert them. */ static void TranslateULTCommands(uint8 &effect, uint8 ¶m, uint8 version) { static constexpr uint8 ultEffTrans[] = { CMD_ARPEGGIO, CMD_PORTAMENTOUP, CMD_PORTAMENTODOWN, CMD_TONEPORTAMENTO, CMD_VIBRATO, CMD_NONE, CMD_NONE, CMD_TREMOLO, CMD_NONE, CMD_OFFSET, CMD_VOLUMESLIDE, CMD_PANNING8, CMD_VOLUME, CMD_PATTERNBREAK, CMD_NONE, // extended effects, processed separately CMD_SPEED, }; uint8 e = effect & 0x0F; effect = ultEffTrans[e]; switch(e) { case 0x00: if(!param || version < '3') effect = CMD_NONE; break; case 0x05: // play backwards if((param & 0x0F) == 0x02 || (param & 0xF0) == 0x20) { effect = CMD_S3MCMDEX; param = 0x9F; } if(((param & 0x0F) == 0x0C || (param & 0xF0) == 0xC0) && version >= '3') { effect = CMD_KEYOFF; param = 0; } break; case 0x07: if(version < '4') effect = CMD_NONE; break; case 0x0A: if(param & 0xF0) param &= 0xF0; break; case 0x0B: param = (param & 0x0F) * 0x11; break; case 0x0C: // volume param /= 4u; break; case 0x0D: // pattern break param = 10 * (param >> 4) + (param & 0x0F); break; case 0x0E: // special switch(param >> 4) { case 0x01: effect = CMD_PORTAMENTOUP; param = 0xF0 | (param & 0x0F); break; case 0x02: effect = CMD_PORTAMENTODOWN; param = 0xF0 | (param & 0x0F); break; case 0x08: if(version >= '4') { effect = CMD_S3MCMDEX; param = 0x60 | (param & 0x0F); } break; case 0x09: effect = CMD_RETRIG; param &= 0x0F; break; case 0x0A: effect = CMD_VOLUMESLIDE; param = ((param & 0x0F) << 4) | 0x0F; break; case 0x0B: effect = CMD_VOLUMESLIDE; param = 0xF0 | (param & 0x0F); break; case 0x0C: case 0x0D: effect = CMD_S3MCMDEX; break; } break; case 0x0F: if(param > 0x2F) effect = CMD_TEMPO; break; } } static int ReadULTEvent(ModCommand &m, FileReader &file, uint8 version) { uint8 repeat = 1; uint8 b = file.ReadUint8(); if(b == 0xFC) // repeat event { repeat = file.ReadUint8(); b = file.ReadUint8(); } m.note = (b > 0 && b < 61) ? (b + 35 + NOTE_MIN) : NOTE_NONE; const auto [instr, cmd, para1, para2] = file.ReadArray(); m.instr = instr; uint8 cmd1 = cmd & 0x0F; uint8 cmd2 = cmd >> 4; uint8 param1 = para1; uint8 param2 = para2; TranslateULTCommands(cmd1, param1, version); TranslateULTCommands(cmd2, param2, version); // sample offset -- this is even more special than digitrakker's if(cmd1 == CMD_OFFSET && cmd2 == CMD_OFFSET) { uint32 offset = ((param2 << 8) | param1) >> 6; m.command = CMD_OFFSET; m.param = static_cast(offset); if(offset > 0xFF) { m.volcmd = VOLCMD_OFFSET; m.vol = static_cast(offset >> 8); } return repeat; } else if(cmd1 == CMD_OFFSET) { uint32 offset = param1 * 4; param1 = mpt::saturate_cast(offset); if(offset > 0xFF && ModCommand::GetEffectWeight(cmd2) < ModCommand::GetEffectType(CMD_OFFSET)) { m.command = CMD_OFFSET; m.param = static_cast(offset); m.volcmd = VOLCMD_OFFSET; m.vol = static_cast(offset >> 8); return repeat; } } else if(cmd2 == CMD_OFFSET) { uint32 offset = param2 * 4; param2 = mpt::saturate_cast(offset); if(offset > 0xFF && ModCommand::GetEffectWeight(cmd1) < ModCommand::GetEffectType(CMD_OFFSET)) { m.command = CMD_OFFSET; m.param = static_cast(offset); m.volcmd = VOLCMD_OFFSET; m.vol = static_cast(offset >> 8); return repeat; } } else if(cmd1 == cmd2) { // don't try to figure out how ultratracker does this, it's quite random cmd2 = CMD_NONE; } if(cmd2 == CMD_VOLUME || (cmd2 == CMD_NONE && cmd1 != CMD_VOLUME)) { // swap commands std::swap(cmd1, cmd2); std::swap(param1, param2); } // Combine slide commands, if possible ModCommand::CombineEffects(cmd2, param2, cmd1, param1); ModCommand::TwoRegularCommandsToMPT(cmd1, param1, cmd2, param2); m.volcmd = cmd1; m.vol = param1; m.command = cmd2; m.param = param2; return repeat; } // Functor for postfixing ULT patterns (this is easier than just remembering everything WHILE we're reading the pattern events) struct PostFixUltCommands { PostFixUltCommands(CHANNELINDEX numChannels) { this->numChannels = numChannels; curChannel = 0; writeT125 = false; isPortaActive.resize(numChannels, false); } void operator()(ModCommand &m) { // Attempt to fix portamentos. // UltraTracker will slide until the destination note is reached or 300 is encountered. // Stop porta? if(m.command == CMD_TONEPORTAMENTO && m.param == 0) { isPortaActive[curChannel] = false; m.command = CMD_NONE; } if(m.volcmd == VOLCMD_TONEPORTAMENTO && m.vol == 0) { isPortaActive[curChannel] = false; m.volcmd = VOLCMD_NONE; } // Apply porta? if(m.note == NOTE_NONE && isPortaActive[curChannel]) { if(m.command == CMD_NONE && m.volcmd != VOLCMD_TONEPORTAMENTO) { m.command = CMD_TONEPORTAMENTO; m.param = 0; } else if(m.volcmd == VOLCMD_NONE && m.command != CMD_TONEPORTAMENTO) { m.volcmd = VOLCMD_TONEPORTAMENTO; m.vol = 0; } } else // new note -> stop porta (or initialize again) { isPortaActive[curChannel] = (m.command == CMD_TONEPORTAMENTO || m.volcmd == VOLCMD_TONEPORTAMENTO); } // attempt to fix F00 (reset to tempo 125, speed 6) if(writeT125 && m.command == CMD_NONE) { m.command = CMD_TEMPO; m.param = 125; } if(m.command == CMD_SPEED && m.param == 0) { m.param = 6; writeT125 = true; } if(m.command == CMD_TEMPO) // don't try to fix this anymore if the tempo has already changed. { writeT125 = false; } curChannel = (curChannel + 1) % numChannels; } std::vector isPortaActive; CHANNELINDEX numChannels, curChannel; bool writeT125; }; static bool ValidateHeader(const UltFileHeader &fileHeader) { if(fileHeader.version < '1' || fileHeader.version > '4' || std::memcmp(fileHeader.signature, "MAS_UTrack_V00", sizeof(fileHeader.signature)) ) { return false; } return true; } static uint64 GetHeaderMinimumAdditionalSize(const UltFileHeader &fileHeader) { return fileHeader.messageLength * 32u + 3u + 256u; } CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderULT(MemoryFileReader file, const uint64 *pfilesize) { UltFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return ProbeWantMoreData; } if(!ValidateHeader(fileHeader)) { return ProbeFailure; } return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); } bool CSoundFile::ReadULT(FileReader &file, ModLoadingFlags loadFlags) { file.Rewind(); UltFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return false; } if(!ValidateHeader(fileHeader)) { return false; } if(loadFlags == onlyVerifyHeader) { return true; } if(!file.CanRead(mpt::saturate_cast(GetHeaderMinimumAdditionalSize(fileHeader)))) { return false; } InitializeGlobals(MOD_TYPE_ULT); m_songName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileHeader.songName); const mpt::uchar *versions[] = {UL_("<1.4"), UL_("1.4"), UL_("1.5"), UL_("1.6")}; m_modFormat.formatName = U_("UltraTracker"); m_modFormat.type = U_("ult"); m_modFormat.madeWithTracker = U_("UltraTracker ") + versions[fileHeader.version - '1']; m_modFormat.charset = mpt::Charset::CP437; m_SongFlags = SONG_ITCOMPATGXX | SONG_ITOLDEFFECTS; // this will be converted to IT format by MPT. // Read "messageLength" lines, each containing 32 characters. m_songMessage.ReadFixedLineLength(file, fileHeader.messageLength * 32, 32, 0); if(SAMPLEINDEX numSamples = file.ReadUint8(); numSamples < MAX_SAMPLES) m_nSamples = numSamples; else return false; for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++) { UltSample sampleHeader; // Annoying: v4 added a field before the end of the struct if(fileHeader.version >= '4') { file.ReadStruct(sampleHeader); } else { file.ReadStructPartial(sampleHeader, 64); sampleHeader.finetune = sampleHeader.speed; sampleHeader.speed = 8363; } sampleHeader.ConvertToMPT(Samples[smp]); m_szNames[smp] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, sampleHeader.name); } ReadOrderFromFile(Order(), file, 256, 0xFF, 0xFE); if(CHANNELINDEX numChannels = file.ReadUint8() + 1u; numChannels <= MAX_BASECHANNELS) m_nChannels = numChannels; else return false; PATTERNINDEX numPats = file.ReadUint8() + 1; for(CHANNELINDEX chn = 0; chn < GetNumChannels(); chn++) { ChnSettings[chn].Reset(); if(fileHeader.version >= '3') ChnSettings[chn].nPan = ((file.ReadUint8() & 0x0F) << 4) + 8; else ChnSettings[chn].nPan = (chn & 1) ? 192 : 64; } Patterns.ResizeArray(numPats); for(PATTERNINDEX pat = 0; pat < numPats; pat++) { if(!Patterns.Insert(pat, 64)) return false; } for(CHANNELINDEX chn = 0; chn < m_nChannels; chn++) { ModCommand evnote; for(PATTERNINDEX pat = 0; pat < numPats && file.CanRead(5); pat++) { ModCommand *note = Patterns[pat].GetpModCommand(0, chn); ROWINDEX row = 0; while(row < 64) { int repeat = ReadULTEvent(evnote, file, fileHeader.version); if(repeat + row > 64) repeat = 64 - row; if(repeat == 0) break; while(repeat--) { *note = evnote; note += GetNumChannels(); row++; } } } } // Post-fix some effects. Patterns.ForEachModCommand(PostFixUltCommands(GetNumChannels())); if(loadFlags & loadSampleData) { for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++) { SampleIO( Samples[smp].uFlags[CHN_16BIT] ? SampleIO::_16bit : SampleIO::_8bit, SampleIO::mono, SampleIO::littleEndian, SampleIO::signedPCM) .ReadSample(Samples[smp], file); } } return true; } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Load_wav.cpp0000644000175000017500000001465414053010430020333 00000000000000/* * Load_wav.cpp * ------------ * Purpose: WAV importer * Notes : This loader converts each WAV channel into a separate mono sample. * Authors: Olivier Lapicque * OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Loaders.h" #include "WAVTools.h" #include "openmpt/soundbase/SampleConvert.hpp" #include "openmpt/soundbase/SampleDecode.hpp" #include "SampleCopy.h" OPENMPT_NAMESPACE_BEGIN ///////////////////////////////////////////////////////////// // WAV file support template static bool CopyWavChannel(ModSample &sample, const FileReader &file, size_t channelIndex, size_t numChannels, SampleConversion conv = SampleConversion()) { MPT_ASSERT(sample.GetNumChannels() == 1); MPT_ASSERT(sample.GetElementarySampleSize() == sizeof(typename SampleConversion::output_t)); const size_t offset = channelIndex * sizeof(typename SampleConversion::input_t) * SampleConversion::input_inc; if(sample.AllocateSample() == 0 || !file.CanRead(offset)) { return false; } const std::byte *inBuf = file.GetRawData().data(); CopySample(reinterpret_cast(sample.samplev()), sample.nLength, 1, inBuf + offset, file.BytesLeft() - offset, numChannels, conv); return true; } CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderWAV(MemoryFileReader file, const uint64 *pfilesize) { RIFFHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return ProbeWantMoreData; } if((fileHeader.magic != RIFFHeader::idRIFF && fileHeader.magic != RIFFHeader::idLIST) || (fileHeader.type != RIFFHeader::idWAVE && fileHeader.type != RIFFHeader::idwave)) { return ProbeFailure; } MPT_UNREFERENCED_PARAMETER(pfilesize); return ProbeSuccess; } bool CSoundFile::ReadWAV(FileReader &file, ModLoadingFlags loadFlags) { WAVReader wavFile(file); if(!wavFile.IsValid() || wavFile.GetNumChannels() == 0 || wavFile.GetNumChannels() > MAX_BASECHANNELS || wavFile.GetNumChannels() >= MAX_SAMPLES || wavFile.GetBitsPerSample() == 0 || wavFile.GetBitsPerSample() > 64 || (wavFile.GetBitsPerSample() < 32 && wavFile.GetSampleFormat() == WAVFormatChunk::fmtFloat) || (wavFile.GetSampleFormat() != WAVFormatChunk::fmtPCM && wavFile.GetSampleFormat() != WAVFormatChunk::fmtFloat)) { return false; } else if(loadFlags == onlyVerifyHeader) { return true; } InitializeGlobals(MOD_TYPE_MPT); m_ContainerType = MOD_CONTAINERTYPE_WAV; m_nChannels = std::max(wavFile.GetNumChannels(), uint16(2)); Patterns.ResizeArray(2); if(!Patterns.Insert(0, 64) || !Patterns.Insert(1, 64)) { return false; } m_modFormat.formatName = U_("RIFF WAVE"); m_modFormat.type = U_("wav"); m_modFormat.charset = mpt::Charset::Windows1252; const SmpLength sampleLength = wavFile.GetSampleLength(); // Setting up module length // Calculate sample length in ticks at tempo 125 const uint32 sampleRate = std::max(uint32(1), wavFile.GetSampleRate()); const uint32 sampleTicks = mpt::saturate_cast(((sampleLength * 50) / sampleRate) + 1); uint32 ticksPerRow = std::max((sampleTicks + 63u) / 63u, uint32(1)); Order().assign(1, 0); ORDERINDEX numOrders = 1; while(ticksPerRow >= 32 && numOrders < MAX_ORDERS) { numOrders++; ticksPerRow = (sampleTicks + (64 * numOrders - 1)) / (64 * numOrders); } Order().resize(numOrders, 1); m_nSamples = wavFile.GetNumChannels(); m_nInstruments = 0; m_nDefaultSpeed = ticksPerRow; m_nDefaultTempo.Set(125); m_SongFlags = SONG_LINEARSLIDES; for(CHANNELINDEX channel = 0; channel < m_nChannels; channel++) { ChnSettings[channel].Reset(); ChnSettings[channel].nPan = (channel % 2u) ? 256 : 0; } // Setting up pattern PatternRow pattern = Patterns[0].GetRow(0); pattern[0].note = pattern[1].note = NOTE_MIDDLEC; pattern[0].instr = pattern[1].instr = 1; const FileReader sampleChunk = wavFile.GetSampleData(); // Read every channel into its own sample lot. for(SAMPLEINDEX channel = 0; channel < GetNumSamples(); channel++) { pattern[channel].note = pattern[0].note; pattern[channel].instr = static_cast(channel + 1); ModSample &sample = Samples[channel + 1]; sample.Initialize(); sample.uFlags = CHN_PANNING; sample.nLength = sampleLength; sample.nC5Speed = wavFile.GetSampleRate(); m_szNames[channel + 1] = ""; wavFile.ApplySampleSettings(sample, GetCharsetInternal(), m_szNames[channel + 1]); if(wavFile.GetNumChannels() > 1) { // Pan all samples appropriately switch(channel) { case 0: sample.nPan = 0; break; case 1: sample.nPan = 256; break; case 2: sample.nPan = (wavFile.GetNumChannels() == 3 ? 128u : 64u); pattern[channel].command = CMD_S3MCMDEX; pattern[channel].param = 0x91; break; case 3: sample.nPan = 192; pattern[channel].command = CMD_S3MCMDEX; pattern[channel].param = 0x91; break; default: sample.nPan = 128; break; } } if(wavFile.GetBitsPerSample() > 8) { sample.uFlags.set(CHN_16BIT); } if(wavFile.GetSampleFormat() == WAVFormatChunk::fmtFloat) { if(wavFile.GetBitsPerSample() <= 32) CopyWavChannel, SC::DecodeFloat32>>(sample, sampleChunk, channel, wavFile.GetNumChannels()); else CopyWavChannel, SC::DecodeFloat64>>(sample, sampleChunk, channel, wavFile.GetNumChannels()); } else { if(wavFile.GetBitsPerSample() <= 8) CopyWavChannel(sample, sampleChunk, channel, wavFile.GetNumChannels()); else if(wavFile.GetBitsPerSample() <= 16) CopyWavChannel>(sample, sampleChunk, channel, wavFile.GetNumChannels()); else if(wavFile.GetBitsPerSample() <= 24) CopyWavChannel, SC::DecodeInt24<0, littleEndian24>>>(sample, sampleChunk, channel, wavFile.GetNumChannels()); else if(wavFile.GetBitsPerSample() <= 32) CopyWavChannel, SC::DecodeInt32<0, littleEndian32>>>(sample, sampleChunk, channel, wavFile.GetNumChannels()); else if(wavFile.GetBitsPerSample() <= 64) CopyWavChannel, SC::DecodeInt64<0, littleEndian64>>>(sample, sampleChunk, channel, wavFile.GetNumChannels()); } sample.PrecomputeLoops(*this, false); } return true; } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Load_xm.cpp0000644000175000017500000012331414145724735020202 00000000000000/* * Load_xm.cpp * ----------- * Purpose: XM (FastTracker II) module loader / saver * Notes : (currently none) * Authors: Olivier Lapicque * OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Loaders.h" #include "../common/version.h" #include "XMTools.h" #include "mod_specifications.h" #ifndef MODPLUG_NO_FILESAVE #include "mpt/io/base.hpp" #include "mpt/io/io.hpp" #include "mpt/io/io_stdstream.hpp" #include "../common/mptFileIO.h" #endif #include "OggStream.h" #include #ifdef MODPLUG_TRACKER #include "../mptrack/TrackerSettings.h" // For super smooth ramping option #endif // MODPLUG_TRACKER #include "mpt/audio/span.hpp" #if defined(MPT_WITH_VORBIS) && defined(MPT_WITH_VORBISFILE) #include #endif #if defined(MPT_WITH_VORBIS) #if MPT_COMPILER_CLANG #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wreserved-id-macro" #endif // MPT_COMPILER_CLANG #include #if MPT_COMPILER_CLANG #pragma clang diagnostic pop #endif // MPT_COMPILER_CLANG #endif #if defined(MPT_WITH_VORBISFILE) #if MPT_COMPILER_CLANG #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wreserved-id-macro" #endif // MPT_COMPILER_CLANG #include #if MPT_COMPILER_CLANG #pragma clang diagnostic pop #endif // MPT_COMPILER_CLANG #include "openmpt/soundbase/Copy.hpp" #endif #ifdef MPT_WITH_STBVORBIS #include #include "openmpt/soundbase/Copy.hpp" #endif // MPT_WITH_STBVORBIS OPENMPT_NAMESPACE_BEGIN #if defined(MPT_WITH_VORBIS) && defined(MPT_WITH_VORBISFILE) static size_t VorbisfileFilereaderRead(void *ptr, size_t size, size_t nmemb, void *datasource) { FileReader &file = *reinterpret_cast(datasource); return file.ReadRaw(mpt::span(mpt::void_cast(ptr), size * nmemb)).size() / size; } static int VorbisfileFilereaderSeek(void *datasource, ogg_int64_t offset, int whence) { FileReader &file = *reinterpret_cast(datasource); switch(whence) { case SEEK_SET: { if(!mpt::in_range(offset)) { return -1; } return file.Seek(mpt::saturate_cast(offset)) ? 0 : -1; } break; case SEEK_CUR: { if(offset < 0) { if(offset == std::numeric_limits::min()) { return -1; } if(!mpt::in_range(0-offset)) { return -1; } return file.SkipBack(mpt::saturate_cast(0 - offset)) ? 0 : -1; } else { if(!mpt::in_range(offset)) { return -1; } return file.Skip(mpt::saturate_cast(offset)) ? 0 : -1; } } break; case SEEK_END: { if(!mpt::in_range(offset)) { return -1; } if(!mpt::in_range(file.GetLength() + offset)) { return -1; } return file.Seek(mpt::saturate_cast(file.GetLength() + offset)) ? 0 : -1; } break; default: return -1; } } static long VorbisfileFilereaderTell(void *datasource) { FileReader &file = *reinterpret_cast(datasource); FileReader::off_t result = file.GetPosition(); if(!mpt::in_range(result)) { return -1; } return static_cast(result); } #endif // MPT_WITH_VORBIS && MPT_WITH_VORBISFILE // Allocate samples for an instrument static std::vector AllocateXMSamples(CSoundFile &sndFile, SAMPLEINDEX numSamples) { LimitMax(numSamples, SAMPLEINDEX(32)); std::vector foundSlots; foundSlots.reserve(numSamples); for(SAMPLEINDEX i = 0; i < numSamples; i++) { SAMPLEINDEX candidateSlot = sndFile.GetNumSamples() + 1; if(candidateSlot >= MAX_SAMPLES) { // If too many sample slots are needed, try to fill some empty slots first. for(SAMPLEINDEX j = 1; j <= sndFile.GetNumSamples(); j++) { if(sndFile.GetSample(j).HasSampleData()) { continue; } if(!mpt::contains(foundSlots, j)) { // Empty sample slot that is not occupied by the current instrument. Yay! candidateSlot = j; // Remove unused sample from instrument sample assignments for(INSTRUMENTINDEX ins = 1; ins <= sndFile.GetNumInstruments(); ins++) { if(sndFile.Instruments[ins] == nullptr) { continue; } for(auto &sample : sndFile.Instruments[ins]->Keyboard) { if(sample == candidateSlot) { sample = 0; } } } break; } } } if(candidateSlot >= MAX_SAMPLES) { // Still couldn't find any empty sample slots, so look out for existing but unused samples. std::vector usedSamples; SAMPLEINDEX unusedSampleCount = sndFile.DetectUnusedSamples(usedSamples); if(unusedSampleCount > 0) { sndFile.RemoveSelectedSamples(usedSamples); // Remove unused samples from instrument sample assignments for(INSTRUMENTINDEX ins = 1; ins <= sndFile.GetNumInstruments(); ins++) { if(sndFile.Instruments[ins] == nullptr) { continue; } for(auto &sample : sndFile.Instruments[ins]->Keyboard) { if(sample < usedSamples.size() && !usedSamples[sample]) { sample = 0; } } } // New candidate slot is first unused sample slot. candidateSlot = static_cast(std::find(usedSamples.begin() + 1, usedSamples.end(), false) - usedSamples.begin()); } else { // No unused sampel slots: Give up :( break; } } if(candidateSlot < MAX_SAMPLES) { foundSlots.push_back(candidateSlot); if(candidateSlot > sndFile.GetNumSamples()) { sndFile.m_nSamples = candidateSlot; } } } return foundSlots; } // Read .XM patterns static void ReadXMPatterns(FileReader &file, const XMFileHeader &fileHeader, CSoundFile &sndFile) { // Reading patterns sndFile.Patterns.ResizeArray(fileHeader.patterns); for(PATTERNINDEX pat = 0; pat < fileHeader.patterns; pat++) { FileReader::off_t curPos = file.GetPosition(); uint32 headerSize = file.ReadUint32LE(); file.Skip(1); // Pack method (= 0) ROWINDEX numRows = 64; if(fileHeader.version == 0x0102) { numRows = file.ReadUint8() + 1; } else { numRows = file.ReadUint16LE(); } // A packed size of 0 indicates a completely empty pattern. const uint16 packedSize = file.ReadUint16LE(); if(numRows == 0) numRows = 64; else if(numRows > MAX_PATTERN_ROWS) numRows = MAX_PATTERN_ROWS; file.Seek(curPos + headerSize); FileReader patternChunk = file.ReadChunk(packedSize); if(!sndFile.Patterns.Insert(pat, numRows) || packedSize == 0) { continue; } enum PatternFlags { isPackByte = 0x80, allFlags = 0xFF, notePresent = 0x01, instrPresent = 0x02, volPresent = 0x04, commandPresent = 0x08, paramPresent = 0x10, }; for(auto &m : sndFile.Patterns[pat]) { uint8 info = patternChunk.ReadUint8(); uint8 vol = 0; if(info & isPackByte) { // Interpret byte as flag set. if(info & notePresent) m.note = patternChunk.ReadUint8(); } else { // Interpret byte as note, read all other pattern fields as well. m.note = info; info = allFlags; } if(info & instrPresent) m.instr = patternChunk.ReadUint8(); if(info & volPresent) vol = patternChunk.ReadUint8(); if(info & commandPresent) m.command = patternChunk.ReadUint8(); if(info & paramPresent) m.param = patternChunk.ReadUint8(); if(m.note == 97) { m.note = NOTE_KEYOFF; } else if(m.note > 0 && m.note < 97) { m.note += 12; } else { m.note = NOTE_NONE; } if(m.command | m.param) { CSoundFile::ConvertModCommand(m); } else { m.command = CMD_NONE; } if(m.instr == 0xFF) { m.instr = 0; } if(vol >= 0x10 && vol <= 0x50) { m.volcmd = VOLCMD_VOLUME; m.vol = vol - 0x10; } else if (vol >= 0x60) { // Volume commands 6-F translation. static constexpr ModCommand::VOLCMD volEffTrans[] = { VOLCMD_VOLSLIDEDOWN, VOLCMD_VOLSLIDEUP, VOLCMD_FINEVOLDOWN, VOLCMD_FINEVOLUP, VOLCMD_VIBRATOSPEED, VOLCMD_VIBRATODEPTH, VOLCMD_PANNING, VOLCMD_PANSLIDELEFT, VOLCMD_PANSLIDERIGHT, VOLCMD_TONEPORTAMENTO, }; m.volcmd = volEffTrans[(vol - 0x60) >> 4]; m.vol = vol & 0x0F; if(m.volcmd == VOLCMD_PANNING) { m.vol *= 4; // FT2 does indeed not scale panning symmetrically. } } } } } enum TrackerVersions { verUnknown = 0x00, // Probably not made with MPT verOldModPlug = 0x01, // Made with MPT Alpha / Beta verNewModPlug = 0x02, // Made with MPT (not Alpha / Beta) verModPlug1_09 = 0x04, // Made with MPT 1.09 or possibly other version verOpenMPT = 0x08, // Made with OpenMPT verConfirmed = 0x10, // We are very sure that we found the correct tracker version. verFT2Generic = 0x20, // "FastTracker v2.00", but FastTracker has NOT been ruled out verOther = 0x40, // Something we don't know, testing for DigiTrakker. verFT2Clone = 0x80, // NOT FT2: itype changed between instruments, or \0 found in song title verDigiTrakker = 0x100, // Probably DigiTrakker verUNMO3 = 0x200, // TODO: UNMO3-ed XMs are detected as MPT 1.16 verEmptyOrders = 0x400, // Allow empty order list like in OpenMPT (FT2 just plays pattern 0 if the order list is empty according to the header) }; DECLARE_FLAGSET(TrackerVersions) static bool ValidateHeader(const XMFileHeader &fileHeader) { if(fileHeader.channels == 0 || fileHeader.channels > MAX_BASECHANNELS || std::memcmp(fileHeader.signature, "Extended Module: ", 17) ) { return false; } return true; } static uint64 GetHeaderMinimumAdditionalSize(const XMFileHeader &fileHeader) { return fileHeader.orders + 4 * (fileHeader.patterns + fileHeader.instruments); } CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderXM(MemoryFileReader file, const uint64 *pfilesize) { XMFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return ProbeWantMoreData; } if(!ValidateHeader(fileHeader)) { return ProbeFailure; } return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); } static bool ReadSampleData(ModSample &sample, SampleIO sampleFlags, FileReader &sampleChunk, bool &isOXM) { bool unsupportedSample = false; bool isOGG = false; if(sampleChunk.CanRead(8)) { isOGG = true; sampleChunk.Skip(4); // In order to avoid false-detecting PCM as OggVorbis as much as possible, // we parse and verify the complete sample data and only assume OggVorbis, // if all Ogg checksums are correct a no single byte of non-Ogg data exists. // The fast-path for regular PCM will only check "OggS" magic and do no other work after failing that check. while(!sampleChunk.EndOfFile()) { if(!Ogg::ReadPage(sampleChunk)) { isOGG = false; break; } } } isOXM = isOXM || isOGG; sampleChunk.Rewind(); if(isOGG) { uint32 originalSize = sampleChunk.ReadInt32LE(); FileReader sampleData = sampleChunk.ReadChunk(sampleChunk.BytesLeft()); sample.uFlags.set(CHN_16BIT, sampleFlags.GetBitDepth() >= 16); sample.uFlags.set(CHN_STEREO, sampleFlags.GetChannelFormat() != SampleIO::mono); sample.nLength = originalSize / (sample.uFlags[CHN_16BIT] ? 2 : 1) / (sample.uFlags[CHN_STEREO] ? 2 : 1); #if defined(MPT_WITH_VORBIS) && defined(MPT_WITH_VORBISFILE) ov_callbacks callbacks = { &VorbisfileFilereaderRead, &VorbisfileFilereaderSeek, NULL, &VorbisfileFilereaderTell }; OggVorbis_File vf; MemsetZero(vf); if(ov_open_callbacks(&sampleData, &vf, nullptr, 0, callbacks) == 0) { if(ov_streams(&vf) == 1) { // we do not support chained vorbis samples vorbis_info *vi = ov_info(&vf, -1); if(vi && vi->rate > 0 && vi->channels > 0) { sample.AllocateSample(); SmpLength offset = 0; int channels = vi->channels; int current_section = 0; long decodedSamples = 0; bool eof = false; while(!eof && offset < sample.nLength && sample.HasSampleData()) { float **output = nullptr; long ret = ov_read_float(&vf, &output, 1024, ¤t_section); if(ret == 0) { eof = true; } else if(ret < 0) { // stream error, just try to continue } else { decodedSamples = ret; LimitMax(decodedSamples, mpt::saturate_cast(sample.nLength - offset)); if(decodedSamples > 0 && channels == sample.GetNumChannels()) { if(sample.uFlags[CHN_16BIT]) { CopyAudio(mpt::audio_span_interleaved(sample.sample16() + (offset * sample.GetNumChannels()), sample.GetNumChannels(), decodedSamples), mpt::audio_span_planar(output, channels, decodedSamples)); } else { CopyAudio(mpt::audio_span_interleaved(sample.sample8() + (offset * sample.GetNumChannels()), sample.GetNumChannels(), decodedSamples), mpt::audio_span_planar(output, channels, decodedSamples)); } } offset += decodedSamples; } } } else { unsupportedSample = true; } } else { unsupportedSample = true; } ov_clear(&vf); } else { unsupportedSample = true; } #elif defined(MPT_WITH_STBVORBIS) // NOTE/TODO: stb_vorbis does not handle inferred negative PCM sample // position at stream start. (See // ). // This means that, for remuxed and re-aligned/cutted (at stream start) // Vorbis files, stb_vorbis will include superfluous samples at the // beginning. OXM files with this property are yet to be spotted in the // wild, thus, this behaviour is currently not problematic. int consumed = 0, error = 0; stb_vorbis *vorb = nullptr; FileReader::PinnedView sampleDataView = sampleData.GetPinnedView(); const std::byte* data = sampleDataView.data(); std::size_t dataLeft = sampleDataView.size(); vorb = stb_vorbis_open_pushdata(mpt::byte_cast(data), mpt::saturate_cast(dataLeft), &consumed, &error, nullptr); sampleData.Skip(consumed); data += consumed; dataLeft -= consumed; if(vorb) { // Header has been read, proceed to reading the sample data sample.AllocateSample(); SmpLength offset = 0; while((error == VORBIS__no_error || (error == VORBIS_need_more_data && dataLeft > 0)) && offset < sample.nLength && sample.HasSampleData()) { int channels = 0, decodedSamples = 0; float **output; consumed = stb_vorbis_decode_frame_pushdata(vorb, mpt::byte_cast(data), mpt::saturate_cast(dataLeft), &channels, &output, &decodedSamples); sampleData.Skip(consumed); data += consumed; dataLeft -= consumed; LimitMax(decodedSamples, mpt::saturate_cast(sample.nLength - offset)); if(decodedSamples > 0 && channels == sample.GetNumChannels()) { if(sample.uFlags[CHN_16BIT]) { CopyAudio(mpt::audio_span_interleaved(sample.sample16() + (offset * sample.GetNumChannels()), sample.GetNumChannels(), decodedSamples), mpt::audio_span_planar(output, channels, decodedSamples)); } else { CopyAudio(mpt::audio_span_interleaved(sample.sample8() + (offset * sample.GetNumChannels()), sample.GetNumChannels(), decodedSamples), mpt::audio_span_planar(output, channels, decodedSamples)); } } offset += decodedSamples; error = stb_vorbis_get_error(vorb); } stb_vorbis_close(vorb); } else { unsupportedSample = true; } #else // !VORBIS unsupportedSample = true; #endif // VORBIS } else { sampleFlags.ReadSample(sample, sampleChunk); } return !unsupportedSample; } bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) { file.Rewind(); XMFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return false; } if(!ValidateHeader(fileHeader)) { return false; } if(!file.CanRead(mpt::saturate_cast(GetHeaderMinimumAdditionalSize(fileHeader)))) { return false; } else if(loadFlags == onlyVerifyHeader) { return true; } InitializeGlobals(MOD_TYPE_XM); InitializeChannels(); m_nMixLevels = MixLevels::Compatible; FlagSet madeWith(verUnknown); mpt::ustring madeWithTracker; bool isMadTracker = false; if(!memcmp(fileHeader.trackerName, "FastTracker v2.00 ", 20) && fileHeader.size == 276) { if(fileHeader.version < 0x0104) madeWith = verFT2Generic | verConfirmed; else if(memchr(fileHeader.songName, '\0', 20) != nullptr) // FT2 pads the song title with spaces, some other trackers use null chars madeWith = verFT2Clone | verNewModPlug | verEmptyOrders; else madeWith = verFT2Generic | verNewModPlug; } else if(!memcmp(fileHeader.trackerName, "FastTracker v 2.00 ", 20)) { // MPT 1.0 (exact version to be determined later) madeWith = verOldModPlug; } else { // Something else! madeWith = verUnknown | verConfirmed; madeWithTracker = mpt::ToUnicode(mpt::Charset::CP437, mpt::String::ReadBuf(mpt::String::spacePadded, fileHeader.trackerName)); if(!memcmp(fileHeader.trackerName, "OpenMPT ", 8)) { madeWith = verOpenMPT | verConfirmed | verEmptyOrders; } else if(!memcmp(fileHeader.trackerName, "MilkyTracker ", 12)) { // MilkyTracker prior to version 0.90.87 doesn't set a version string. // Luckily, starting with v0.90.87, MilkyTracker also implements the FT2 panning scheme. if(memcmp(fileHeader.trackerName + 12, " ", 8)) { m_nMixLevels = MixLevels::CompatibleFT2; } } else if(!memcmp(fileHeader.trackerName, "Fasttracker II clone", 20)) { // 8bitbubsy's FT2 clone should be treated exactly like FT2 madeWith = verFT2Generic | verConfirmed; } else if(!memcmp(fileHeader.trackerName, "MadTracker 2.0\0", 15)) { // Fix channel 2 in m3_cha.xm m_playBehaviour.reset(kFT2PortaNoNote); // Fix arpeggios in kragle_-_happy_day.xm m_playBehaviour.reset(kFT2Arpeggio); isMadTracker = true; } else if(!memcmp(fileHeader.trackerName, "Skale Tracker\0", 14) || !memcmp(fileHeader.trackerName, "Sk@le Tracker\0", 14)) { m_playBehaviour.reset(kFT2ST3OffsetOutOfRange); // Fix arpeggios in KAPTENFL.XM m_playBehaviour.reset(kFT2Arpeggio); } else if(!memcmp(fileHeader.trackerName, "*Converted ", 11)) { madeWith = verDigiTrakker; } } m_songName = mpt::String::ReadBuf(mpt::String::spacePadded, fileHeader.songName); m_nMinPeriod = 1; m_nMaxPeriod = 31999; Order().SetRestartPos(fileHeader.restartPos); m_nChannels = fileHeader.channels; m_nInstruments = std::min(static_cast(fileHeader.instruments), static_cast(MAX_INSTRUMENTS - 1)); if(fileHeader.speed) m_nDefaultSpeed = fileHeader.speed; if(fileHeader.tempo) m_nDefaultTempo = Clamp(TEMPO(fileHeader.tempo, 0), ModSpecs::xmEx.GetTempoMin(), ModSpecs::xmEx.GetTempoMax()); m_SongFlags.reset(); m_SongFlags.set(SONG_LINEARSLIDES, (fileHeader.flags & XMFileHeader::linearSlides) != 0); m_SongFlags.set(SONG_EXFILTERRANGE, (fileHeader.flags & XMFileHeader::extendedFilterRange) != 0); if(m_SongFlags[SONG_EXFILTERRANGE] && madeWith == (verFT2Generic | verNewModPlug)) { madeWith = verFT2Clone | verNewModPlug | verConfirmed; } ReadOrderFromFile(Order(), file, fileHeader.orders); if(fileHeader.orders == 0 && !madeWith[verEmptyOrders]) { // Fix lamb_-_dark_lighthouse.xm, which only contains one pattern and an empty order list Order().assign(1, 0); } file.Seek(fileHeader.size + 60); if(fileHeader.version >= 0x0104) { ReadXMPatterns(file, fileHeader, *this); } bool isOXM = false; // In case of XM versions < 1.04, we need to memorize the sample flags for all samples, as they are not stored immediately after the sample headers. std::vector sampleFlags; uint8 sampleReserved = 0; int instrType = -1; bool unsupportedSamples = false; // Reading instruments for(INSTRUMENTINDEX instr = 1; instr <= m_nInstruments; instr++) { // First, try to read instrument header length... uint32 headerSize = file.ReadUint32LE(); if(headerSize == 0) { headerSize = sizeof(XMInstrumentHeader); } // Now, read the complete struct. file.SkipBack(4); XMInstrumentHeader instrHeader; file.ReadStructPartial(instrHeader, headerSize); // Time for some version detection stuff. if(madeWith == verOldModPlug) { madeWith.set(verConfirmed); if(instrHeader.size == 245) { // ModPlug Tracker Alpha m_dwLastSavedWithVersion = MPT_V("1.00.00.A5"); madeWithTracker = U_("ModPlug Tracker 1.0 alpha"); } else if(instrHeader.size == 263) { // ModPlug Tracker Beta (Beta 1 still behaves like Alpha, but Beta 3.3 does it this way) m_dwLastSavedWithVersion = MPT_V("1.00.00.B3"); madeWithTracker = U_("ModPlug Tracker 1.0 beta"); } else { // WTF? madeWith = (verUnknown | verConfirmed); } } else if(instrHeader.numSamples == 0) { // Empty instruments make tracker identification pretty easy! if(instrHeader.size == 263 && instrHeader.sampleHeaderSize == 0 && madeWith[verNewModPlug]) madeWith.set(verConfirmed); else if(instrHeader.size != 29 && madeWith[verDigiTrakker]) madeWith.reset(verDigiTrakker); else if(madeWith[verFT2Clone | verFT2Generic] && instrHeader.size != 33) { // Sure isn't FT2. // Note: FT2 NORMALLY writes shdr=40 for all samples, but sometimes it // just happens to write random garbage there instead. Surprise! // Note: 4-mat's eternity.xm has an instrument header size of 29. madeWith = verUnknown; } } if(AllocateInstrument(instr) == nullptr) { continue; } instrHeader.ConvertToMPT(*Instruments[instr]); if(instrType == -1) { instrType = instrHeader.type; } else if(instrType != instrHeader.type && madeWith[verFT2Generic]) { // FT2 writes some random junk for the instrument type field, // but it's always the SAME junk for every instrument saved. madeWith.reset(verFT2Generic); madeWith.set(verFT2Clone); } if(instrHeader.numSamples > 0) { // Yep, there are some samples associated with this instrument. if((instrHeader.instrument.midiEnabled | instrHeader.instrument.midiChannel | instrHeader.instrument.midiProgram | instrHeader.instrument.muteComputer) != 0) { // Definitely not an old MPT. madeWith.reset(verOldModPlug | verNewModPlug); } // Read sample headers std::vector sampleSlots = AllocateXMSamples(*this, instrHeader.numSamples); // Update sample assignment map for(size_t k = 0 + 12; k < 96 + 12; k++) { if(Instruments[instr]->Keyboard[k] < sampleSlots.size()) { Instruments[instr]->Keyboard[k] = sampleSlots[Instruments[instr]->Keyboard[k]]; } } if(fileHeader.version >= 0x0104) { sampleFlags.clear(); } // Need to memorize those if we're going to skip any samples... std::vector sampleSize(instrHeader.numSamples); // Early versions of Sk@le Tracker set instrHeader.sampleHeaderSize = 0 (IFULOVE.XM) // cybernostra weekend has instrHeader.sampleHeaderSize = 0x12, which would leave out the sample name, but FT2 still reads the name. MPT_ASSERT(instrHeader.sampleHeaderSize == 0 || instrHeader.sampleHeaderSize == sizeof(XMSample)); for(SAMPLEINDEX sample = 0; sample < instrHeader.numSamples; sample++) { XMSample sampleHeader; file.ReadStruct(sampleHeader); sampleFlags.push_back(sampleHeader.GetSampleFormat()); sampleSize[sample] = sampleHeader.length; sampleReserved |= sampleHeader.reserved; if(sample < sampleSlots.size()) { SAMPLEINDEX mptSample = sampleSlots[sample]; sampleHeader.ConvertToMPT(Samples[mptSample]); instrHeader.instrument.ApplyAutoVibratoToMPT(Samples[mptSample]); m_szNames[mptSample] = mpt::String::ReadBuf(mpt::String::spacePadded, sampleHeader.name); if((sampleHeader.flags & 3) == 3 && madeWith[verNewModPlug]) { // MPT 1.09 and maybe newer / older versions set both loop flags for bidi loops. madeWith.set(verModPlug1_09); } } } // Read samples if(fileHeader.version >= 0x0104) { for(SAMPLEINDEX sample = 0; sample < instrHeader.numSamples; sample++) { // Sample 15 in dirtysex.xm by J/M/T/M is a 16-bit sample with an odd size of 0x18B according to the header, while the real sample size would be 0x18A. // Always read as many bytes as specified in the header, even if the sample reader would probably read less bytes. FileReader sampleChunk = file.ReadChunk(sampleFlags[sample].GetEncoding() != SampleIO::ADPCM ? sampleSize[sample] : (16 + (sampleSize[sample] + 1) / 2)); if(sample < sampleSlots.size() && (loadFlags & loadSampleData)) { if(!ReadSampleData(Samples[sampleSlots[sample]], sampleFlags[sample], sampleChunk, isOXM)) { unsupportedSamples = true; } } } } } } if(sampleReserved == 0 && madeWith[verNewModPlug] && memchr(fileHeader.songName, '\0', sizeof(fileHeader.songName)) != nullptr) { // Null-terminated song name: Quite possibly MPT. (could really be an MPT-made file resaved in FT2, though) madeWith.set(verConfirmed); } if(fileHeader.version < 0x0104) { // Load Patterns and Samples (Version 1.02 and 1.03) if(loadFlags & (loadPatternData | loadSampleData)) { ReadXMPatterns(file, fileHeader, *this); } if(loadFlags & loadSampleData) { for(SAMPLEINDEX sample = 1; sample <= GetNumSamples(); sample++) { sampleFlags[sample - 1].ReadSample(Samples[sample], file); } } } if(unsupportedSamples) { AddToLog(LogWarning, U_("Some compressed samples could not be loaded because they use an unsupported codec.")); } // Read song comments: "text" if(file.ReadMagic("text")) { m_songMessage.Read(file, file.ReadUint32LE(), SongMessage::leCR); madeWith.set(verConfirmed); } // Read midi config: "MIDI" bool hasMidiConfig = false; if(file.ReadMagic("MIDI")) { file.ReadStructPartial(m_MidiCfg, file.ReadUint32LE()); m_MidiCfg.Sanitize(); hasMidiConfig = true; madeWith.set(verConfirmed); } // Read pattern names: "PNAM" if(file.ReadMagic("PNAM")) { const PATTERNINDEX namedPats = std::min(static_cast(file.ReadUint32LE() / MAX_PATTERNNAME), Patterns.Size()); for(PATTERNINDEX pat = 0; pat < namedPats; pat++) { char patName[MAX_PATTERNNAME]; file.ReadString(patName, MAX_PATTERNNAME); Patterns[pat].SetName(patName); } madeWith.set(verConfirmed); } // Read channel names: "CNAM" if(file.ReadMagic("CNAM")) { const CHANNELINDEX namedChans = std::min(static_cast(file.ReadUint32LE() / MAX_CHANNELNAME), GetNumChannels()); for(CHANNELINDEX chn = 0; chn < namedChans; chn++) { file.ReadString(ChnSettings[chn].szName, MAX_CHANNELNAME); } madeWith.set(verConfirmed); } // Read mix plugins information if(file.CanRead(8)) { FileReader::off_t oldPos = file.GetPosition(); LoadMixPlugins(file); if(file.GetPosition() != oldPos) { madeWith.set(verConfirmed); } } if(madeWith[verConfirmed]) { if(madeWith[verModPlug1_09]) { m_dwLastSavedWithVersion = MPT_V("1.09.00.00"); madeWithTracker = U_("ModPlug Tracker 1.09"); } else if(madeWith[verNewModPlug]) { m_dwLastSavedWithVersion = MPT_V("1.16.00.00"); madeWithTracker = U_("ModPlug Tracker 1.10 - 1.16"); } } if(!memcmp(fileHeader.trackerName, "OpenMPT ", 8)) { // Hey, I know this tracker! std::string mptVersion(fileHeader.trackerName + 8, 12); m_dwLastSavedWithVersion = Version::Parse(mpt::ToUnicode(mpt::Charset::ASCII, mptVersion)); madeWith = verOpenMPT | verConfirmed; if(m_dwLastSavedWithVersion < MPT_V("1.22.07.19")) m_nMixLevels = MixLevels::Compatible; else m_nMixLevels = MixLevels::CompatibleFT2; } if(m_dwLastSavedWithVersion && !madeWith[verOpenMPT]) { m_nMixLevels = MixLevels::Original; m_playBehaviour.reset(); } if(madeWith[verFT2Generic]) { m_nMixLevels = MixLevels::CompatibleFT2; if(!hasMidiConfig) { // FT2 allows typing in arbitrary unsupported effect letters such as Zxx. // Prevent these commands from being interpreted as filter commands by erasing the default MIDI Config. m_MidiCfg.ClearZxxMacros(); } if(fileHeader.version >= 0x0104 // Old versions of FT2 didn't have (smooth) ramping. Disable it for those versions where we can be sure that there should be no ramping. #ifdef MODPLUG_TRACKER && TrackerSettings::Instance().autoApplySmoothFT2Ramping #endif // MODPLUG_TRACKER ) { // apply FT2-style super-soft volume ramping m_playBehaviour.set(kFT2VolumeRamping); } } if(madeWithTracker.empty()) { if(madeWith[verDigiTrakker] && sampleReserved == 0 && (instrType ? instrType : -1) == -1) { madeWithTracker = U_("DigiTrakker"); } else if(madeWith[verFT2Generic]) { madeWithTracker = U_("FastTracker 2 or compatible"); } else { madeWithTracker = U_("Unknown"); } } bool isOpenMPTMade = false; // specific for OpenMPT 1.17+ if(GetNumInstruments()) { isOpenMPTMade = LoadExtendedInstrumentProperties(file); } LoadExtendedSongProperties(file, true, &isOpenMPTMade); if(isOpenMPTMade && m_dwLastSavedWithVersion < MPT_V("1.17.00.00")) { // Up to OpenMPT 1.17.02.45 (r165), it was possible that the "last saved with" field was 0 // when saving a file in OpenMPT for the first time. m_dwLastSavedWithVersion = MPT_V("1.17.00.00"); } if(m_dwLastSavedWithVersion >= MPT_V("1.17.00.00")) { madeWithTracker = U_("OpenMPT ") + m_dwLastSavedWithVersion.ToUString(); } // We no longer allow any --- or +++ items in the order list now. if(m_dwLastSavedWithVersion && m_dwLastSavedWithVersion < MPT_V("1.22.02.02")) { if(!Patterns.IsValidPat(0xFE)) Order().RemovePattern(0xFE); if(!Patterns.IsValidPat(0xFF)) Order().Replace(0xFF, Order.GetInvalidPatIndex()); } m_modFormat.formatName = MPT_UFORMAT("FastTracker 2 v{}.{}")(fileHeader.version >> 8, mpt::ufmt::hex0<2>(fileHeader.version & 0xFF)); m_modFormat.madeWithTracker = std::move(madeWithTracker); m_modFormat.charset = (m_dwLastSavedWithVersion || isMadTracker) ? mpt::Charset::Windows1252 : mpt::Charset::CP437; if(isOXM) { m_modFormat.originalFormatName = std::move(m_modFormat.formatName); m_modFormat.formatName = U_("OggMod FastTracker 2"); m_modFormat.type = U_("oxm"); m_modFormat.originalType = U_("xm"); } else { m_modFormat.type = U_("xm"); } return true; } #ifndef MODPLUG_NO_FILESAVE bool CSoundFile::SaveXM(std::ostream &f, bool compatibilityExport) { bool addChannel = false; // avoid odd channel count for FT2 compatibility XMFileHeader fileHeader; MemsetZero(fileHeader); memcpy(fileHeader.signature, "Extended Module: ", 17); mpt::String::WriteBuf(mpt::String::spacePadded, fileHeader.songName) = m_songName; fileHeader.eof = 0x1A; const std::string openMptTrackerName = mpt::ToCharset(GetCharsetFile(), Version::Current().GetOpenMPTVersionString()); mpt::String::WriteBuf(mpt::String::spacePadded, fileHeader.trackerName) = openMptTrackerName; // Writing song header fileHeader.version = 0x0104; // XM Format v1.04 fileHeader.size = sizeof(XMFileHeader) - 60; // minus everything before this field fileHeader.restartPos = Order().GetRestartPos(); fileHeader.channels = m_nChannels; if((m_nChannels % 2u) && m_nChannels < 32) { // Avoid odd channel count for FT2 compatibility fileHeader.channels++; addChannel = true; } else if(compatibilityExport && fileHeader.channels > 32) { fileHeader.channels = 32; } // Find out number of orders and patterns used. // +++ and --- patterns are not taken into consideration as FastTracker does not support them. const ORDERINDEX trimmedLength = Order().GetLengthTailTrimmed(); std::vector orderList(trimmedLength); const ORDERINDEX orderLimit = compatibilityExport ? 256 : uint16_max; ORDERINDEX numOrders = 0; PATTERNINDEX numPatterns = Patterns.GetNumPatterns(); bool changeOrderList = false; for(ORDERINDEX ord = 0; ord < trimmedLength; ord++) { PATTERNINDEX pat = Order()[ord]; if(pat == Order.GetIgnoreIndex() || pat == Order.GetInvalidPatIndex() || pat > uint8_max) { changeOrderList = true; } else if(numOrders < orderLimit) { orderList[numOrders++] = static_cast(pat); if(pat >= numPatterns) numPatterns = pat + 1; } } if(changeOrderList) { AddToLog(LogWarning, U_("Skip and stop order list items (+++ and ---) are not saved in XM files.")); } orderList.resize(compatibilityExport ? 256 : numOrders); fileHeader.orders = numOrders; fileHeader.patterns = numPatterns; fileHeader.size += static_cast(orderList.size()); uint16 writeInstruments; if(m_nInstruments > 0) fileHeader.instruments = writeInstruments = m_nInstruments; else fileHeader.instruments = writeInstruments = m_nSamples; if(m_SongFlags[SONG_LINEARSLIDES]) fileHeader.flags |= XMFileHeader::linearSlides; if(m_SongFlags[SONG_EXFILTERRANGE] && !compatibilityExport) fileHeader.flags |= XMFileHeader::extendedFilterRange; fileHeader.flags = fileHeader.flags; // Fasttracker 2 will happily accept any tempo faster than 255 BPM. XMPlay does also support this, great! fileHeader.tempo = mpt::saturate_cast(m_nDefaultTempo.GetInt()); fileHeader.speed = static_cast(Clamp(m_nDefaultSpeed, 1u, 31u)); mpt::IO::Write(f, fileHeader); // Write processed order list mpt::IO::Write(f, orderList); // Writing patterns #define ASSERT_CAN_WRITE(x) \ if(len > s.size() - x) /*Buffer running out? Make it larger.*/ \ s.resize(s.size() + 10 * 1024, 0); std::vector s(64 * 64 * 5, 0); for(PATTERNINDEX pat = 0; pat < numPatterns; pat++) { uint8 patHead[9] = { 0 }; patHead[0] = 9; if(!Patterns.IsValidPat(pat)) { // There's nothing to write... chicken out. patHead[5] = 64; mpt::IO::Write(f, patHead); continue; } const uint16 numRows = mpt::saturate_cast(Patterns[pat].GetNumRows()); patHead[5] = static_cast(numRows & 0xFF); patHead[6] = static_cast(numRows >> 8); auto p = Patterns[pat].cbegin(); size_t len = 0; // Empty patterns are always loaded as 64-row patterns in FT2, regardless of their real size... bool emptyPattern = true; for(size_t j = m_nChannels * numRows; j > 0; j--, p++) { // Don't write more than 32 channels if(compatibilityExport && m_nChannels - ((j - 1) % m_nChannels) > 32) continue; uint8 note = p->note; uint8 command = p->command, param = p->param; ModSaveCommand(command, param, true, compatibilityExport); if (note >= NOTE_MIN_SPECIAL) note = 97; else if ((note <= 12) || (note > 96+12)) note = 0; else note -= 12; uint8 vol = 0; if (p->volcmd != VOLCMD_NONE) { switch(p->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_VIBRATODEPTH: vol = 0xB0 + (p->vol & 0x0F); break; case VOLCMD_PANNING: vol = 0xC0 + (p->vol / 4); 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; } // Those values are ignored in FT2. Don't save them, also to avoid possible problems with other trackers (or MPT itself) if(compatibilityExport && p->vol == 0) { switch(p->volcmd) { case VOLCMD_VOLUME: case VOLCMD_PANNING: case VOLCMD_VIBRATODEPTH: case VOLCMD_TONEPORTAMENTO: case VOLCMD_PANSLIDELEFT: // Doesn't have memory, but does weird things with zero param. break; default: // no memory here. vol = 0; } } } // no need to fix non-empty patterns if(!p->IsEmpty()) emptyPattern = false; // Apparently, completely empty patterns are loaded as empty 64-row patterns in FT2, regardless of their original size. // We have to avoid this, so we add a "break to row 0" command in the last row. if(j == 1 && emptyPattern && numRows != 64) { command = 0x0D; param = 0; } 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 { uint8 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(addChannel && (j % m_nChannels == 1 || m_nChannels == 1)) { ASSERT_CAN_WRITE(1); s[len++] = 0x80; } ASSERT_CAN_WRITE(5); } if(emptyPattern && numRows == 64) { // Be smart when saving empty patterns! len = 0; } // Reaching the limits of file format? if(len > uint16_max) { AddToLog(LogWarning, MPT_UFORMAT("Warning: File format limit was reached. Some pattern data may not get written to file. (pattern {})")(pat)); len = uint16_max; } patHead[7] = static_cast(len & 0xFF); patHead[8] = static_cast(len >> 8); mpt::IO::Write(f, patHead); if(len) mpt::IO::WriteRaw(f, s.data(), len); } #undef ASSERT_CAN_WRITE // Check which samples are referenced by which instruments (for assigning unreferenced samples to instruments) std::vector sampleAssigned(GetNumSamples() + 1, false); for(INSTRUMENTINDEX ins = 1; ins <= GetNumInstruments(); ins++) { if(Instruments[ins] != nullptr) { Instruments[ins]->GetSamples(sampleAssigned); } } // Writing instruments for(INSTRUMENTINDEX ins = 1; ins <= writeInstruments; ins++) { XMInstrumentHeader insHeader; std::vector samples; if(GetNumInstruments()) { if(Instruments[ins] != nullptr) { // Convert instrument insHeader.ConvertToXM(*Instruments[ins], compatibilityExport); samples = insHeader.instrument.GetSampleList(*Instruments[ins], compatibilityExport); if(samples.size() > 0 && samples[0] <= GetNumSamples()) { // Copy over auto-vibrato settings of first sample insHeader.instrument.ApplyAutoVibratoToXM(Samples[samples[0]], GetType()); } std::vector additionalSamples; // Try to save "instrument-less" samples as well by adding those after the "normal" samples of our sample. // We look for unassigned samples directly after the samples assigned to our current instrument, so if // e.g. sample 1 is assigned to instrument 1 and samples 2 to 10 aren't assigned to any instrument, // we will assign those to sample 1. Any samples before the first referenced sample are going to be lost, // but hey, I wrote this mostly for preserving instrument texts in existing modules, where we shouldn't encounter this situation... for(auto smp : samples) { while(++smp <= GetNumSamples() && !sampleAssigned[smp] && insHeader.numSamples < (compatibilityExport ? 16 : 32)) { sampleAssigned[smp] = true; // Don't want to add this sample again. additionalSamples.push_back(smp); insHeader.numSamples++; } } samples.insert(samples.end(), additionalSamples.begin(), additionalSamples.end()); } else { MemsetZero(insHeader); } } else { // Convert samples to instruments MemsetZero(insHeader); insHeader.numSamples = 1; insHeader.instrument.ApplyAutoVibratoToXM(Samples[ins], GetType()); samples.push_back(ins); } insHeader.Finalise(); size_t insHeaderSize = insHeader.size; mpt::IO::WritePartial(f, insHeader, insHeaderSize); std::vector sampleFlags(samples.size()); // Write Sample Headers for(SAMPLEINDEX smp = 0; smp < samples.size(); smp++) { XMSample xmSample; if(samples[smp] <= GetNumSamples()) { xmSample.ConvertToXM(Samples[samples[smp]], GetType(), compatibilityExport); } else { MemsetZero(xmSample); } sampleFlags[smp] = xmSample.GetSampleFormat(); mpt::String::WriteBuf(mpt::String::spacePadded, xmSample.name) = m_szNames[samples[smp]]; mpt::IO::Write(f, xmSample); } // Write Sample Data for(SAMPLEINDEX smp = 0; smp < samples.size(); smp++) { if(samples[smp] <= GetNumSamples()) { sampleFlags[smp].WriteSample(f, Samples[samples[smp]]); } } } if(!compatibilityExport) { // Writing song comments if(!m_songMessage.empty()) { uint32 size = mpt::saturate_cast(m_songMessage.length()); mpt::IO::WriteRaw(f, "text", 4); mpt::IO::WriteIntLE(f, size); mpt::IO::WriteRaw(f, m_songMessage.c_str(), size); } // Writing midi cfg if(!m_MidiCfg.IsMacroDefaultSetupUsed()) { mpt::IO::WriteRaw(f, "MIDI", 4); mpt::IO::WriteIntLE(f, sizeof(MIDIMacroConfigData)); mpt::IO::Write(f, static_cast(m_MidiCfg)); } // Writing Pattern Names const PATTERNINDEX numNamedPats = Patterns.GetNumNamedPatterns(); if(numNamedPats > 0) { mpt::IO::WriteRaw(f, "PNAM", 4); mpt::IO::WriteIntLE(f, numNamedPats * MAX_PATTERNNAME); for(PATTERNINDEX pat = 0; pat < numNamedPats; pat++) { char name[MAX_PATTERNNAME]; mpt::String::WriteBuf(mpt::String::maybeNullTerminated, name) = Patterns[pat].GetName(); mpt::IO::Write(f, name); } } // Writing Channel Names { CHANNELINDEX numNamedChannels = 0; for(CHANNELINDEX chn = 0; chn < m_nChannels; chn++) { if (ChnSettings[chn].szName[0]) numNamedChannels = chn + 1; } // Do it! if(numNamedChannels) { mpt::IO::WriteRaw(f, "CNAM", 4); mpt::IO::WriteIntLE(f, numNamedChannels * MAX_CHANNELNAME); for(CHANNELINDEX chn = 0; chn < numNamedChannels; chn++) { char name[MAX_CHANNELNAME]; mpt::String::WriteBuf(mpt::String::maybeNullTerminated, name) = ChnSettings[chn].szName; mpt::IO::Write(f, name); } } } //Save hacked-on extra info SaveMixPlugins(&f); if(GetNumInstruments()) { SaveExtendedInstrumentProperties(writeInstruments, f); } SaveExtendedSongProperties(f); } return true; } #endif // MODPLUG_NO_FILESAVE OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Message.cpp0000644000175000017500000001356714102020347020170 00000000000000/* * Message.cpp * ----------- * Purpose: Various functions for processing song messages (allocating, reading from file...) * Notes : Those functions should offer a rather high level of abstraction compared to * previous ways of reading the song messages. There are still many things to do, * though. Future versions of ReadMessage() could e.g. offer charset conversion * and the code is not yet ready for unicode. * Some functions for preparing the message text to be written to a file would * also be handy. * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Message.h" #include "../common/FileReader.h" OPENMPT_NAMESPACE_BEGIN // Read song message from a mapped file. // [in] data: pointer to the data in memory that is going to be read // [in] length: number of characters that should be read. Trailing null characters are automatically removed. // [in] lineEnding: line ending formatting of the text in memory. // [out] returns true on success. bool SongMessage::Read(const std::byte *data, size_t length, LineEnding lineEnding) { const char *str = mpt::byte_cast(data); while(length != 0 && str[length - 1] == '\0') { // Ignore trailing null character. length--; } // Simple line-ending detection algorithm. VERY simple. if(lineEnding == leAutodetect) { size_t nCR = 0, nLF = 0, nCRLF = 0; // find CRs, LFs and CRLFs for(size_t i = 0; i < length; i++) { char c = str[i]; if(c == '\r') nCR++; else if(c == '\n') nLF++; if(i && str[i - 1] == '\r' && c == '\n') nCRLF++; } // evaluate findings if(nCR == nLF && nCR == nCRLF) lineEnding = leCRLF; else if(nCR && !nLF) lineEnding = leCR; else if(!nCR && nLF) lineEnding = leLF; else lineEnding = leMixed; } size_t finalLength = 0; // calculate the final amount of characters to be allocated. for(size_t i = 0; i < length; i++) { finalLength++; if(str[i] == '\r' && lineEnding == leCRLF) i++; // skip the LF } clear(); reserve(finalLength); for(size_t i = 0; i < length; i++) { char c = str[i]; switch(c) { case '\r': if(lineEnding != leLF) c = InternalLineEnding; else c = ' '; if(lineEnding == leCRLF) i++; // skip the LF break; case '\n': if(lineEnding != leCR && lineEnding != leCRLF) c = InternalLineEnding; else c = ' '; break; case '\0': c = ' '; break; default: break; } push_back(c); } return true; } bool SongMessage::Read(FileReader &file, const size_t length, LineEnding lineEnding) { FileReader::off_t readLength = std::min(static_cast(length), file.BytesLeft()); FileReader::PinnedView fileView = file.ReadPinnedView(readLength); bool success = Read(fileView.data(), fileView.size(), lineEnding); return success; } // Read comments with fixed line length from a mapped file. // [in] data: pointer to the data in memory that is going to be read // [in] length: number of characters that should be read, not including a possible trailing null terminator (it is automatically appended). // [in] lineLength: The fixed length of a line. // [in] lineEndingLength: The padding space between two fixed lines. (there could for example be a null char after every line) // [out] returns true on success. bool SongMessage::ReadFixedLineLength(const std::byte *data, const size_t length, const size_t lineLength, const size_t lineEndingLength) { if(lineLength == 0) return false; clear(); reserve(length); size_t readPos = 0, writePos = 0; while(readPos < length) { size_t thisLineLength = std::min(lineLength, length - readPos); append(mpt::byte_cast(data) + readPos, thisLineLength); append(1, InternalLineEnding); // Fix weird chars for(size_t pos = writePos; pos < writePos + thisLineLength; pos++) { switch(operator[](pos)) { case '\0': case '\n': case '\r': operator[](pos) = ' '; break; } } readPos += thisLineLength + std::min(lineEndingLength, length - readPos - thisLineLength); writePos += thisLineLength + 1; } return true; } bool SongMessage::ReadFixedLineLength(FileReader &file, const size_t length, const size_t lineLength, const size_t lineEndingLength) { FileReader::off_t readLength = std::min(static_cast(length), file.BytesLeft()); FileReader::PinnedView fileView = file.ReadPinnedView(readLength); bool success = ReadFixedLineLength(fileView.data(), fileView.size(), lineLength, lineEndingLength); return success; } // Retrieve song message. // [in] lineEnding: line ending formatting of the text in memory. // [out] returns formatted song message. std::string SongMessage::GetFormatted(const LineEnding lineEnding) const { std::string comments; comments.reserve(length()); for(auto c : *this) { if(c == InternalLineEnding) { switch(lineEnding) { case leCR: comments.push_back('\r'); break; case leCRLF: comments.push_back('\r'); comments.push_back('\n'); break; case leLF: comments.push_back('\n'); break; default: comments.push_back('\r'); break; } } else { comments.push_back(c); } } return comments; } bool SongMessage::SetFormatted(std::string message, LineEnding lineEnding) { MPT_ASSERT(lineEnding == leLF || lineEnding == leCR || lineEnding == leCRLF); switch(lineEnding) { case leLF: message = mpt::replace(message, std::string("\n"), std::string(1, InternalLineEnding)); break; case leCR: message = mpt::replace(message, std::string("\r"), std::string(1, InternalLineEnding)); break; case leCRLF: message = mpt::replace(message, std::string("\r\n"), std::string(1, InternalLineEnding)); break; default: MPT_ASSERT_NOTREACHED(); break; } if(message == *this) { return false; } assign(std::move(message)); return true; } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Message.h0000644000175000017500000000544214052666041017641 00000000000000/* * Message.h * --------- * Purpose: Various functions for processing song messages (allocating, reading from file...) * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include #include "../common/FileReaderFwd.h" OPENMPT_NAMESPACE_BEGIN class SongMessage : public std::string { public: // Line ending types (for reading song messages from module files) enum LineEnding { leCR, // Carriage Return (0x0D, \r) leLF, // Line Feed (0x0A \n) leCRLF, // Carriage Return, Line Feed (0x0D0A, \r\n) leMixed, // It is not defined whether Carriage Return or Line Feed is the actual line ending. Both are accepted. leAutodetect, // Detect suitable line ending }; enum { InternalLineEnding = '\r', // The character that represents line endings internally }; // Read song message from a mapped file. // [in] data: pointer to the data in memory that is going to be read // [in] length: number of characters that should be read, not including a possible trailing null terminator (it is automatically appended). // [in] lineEnding: line ending formatting of the text in memory. // [out] returns true on success. bool Read(const std::byte *data, const size_t length, LineEnding lineEnding); bool Read(FileReader &file, const size_t length, LineEnding lineEnding); // Read comments with fixed line length from a mapped file. // [in] data: pointer to the data in memory that is going to be read // [in] length: number of characters that should be read, not including a possible trailing null terminator (it is automatically appended). // [in] lineLength: The fixed length of a line. // [in] lineEndingLength: The padding space between two fixed lines. (there could for example be a null char after every line) // [out] returns true on success. bool ReadFixedLineLength(const std::byte *data, const size_t length, const size_t lineLength, const size_t lineEndingLength); bool ReadFixedLineLength(FileReader &file, const size_t length, const size_t lineLength, const size_t lineEndingLength); // Retrieve song message. // [in] lineEnding: line ending formatting of the text in memory. // [out] returns formatted song message. std::string GetFormatted(const LineEnding lineEnding) const; // Set song message. // [in] lineEnding: line ending formatting of the text in memory. Must be leCR or leLF or leCRLF, // [out] returns true if the message has been changed. bool SetFormatted(std::string message, LineEnding lineEnding); // Sets the song message. Expects the provided string to already use the internal line ending character. void SetRaw(std::string message) noexcept { assign(std::move(message)); } }; OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/MIDIEvents.cpp0000644000175000017500000001303713161656666020532 00000000000000/* * MIDIEvents.cpp * -------------- * Purpose: MIDI event handling, event lists, ... * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "MIDIEvents.h" OPENMPT_NAMESPACE_BEGIN namespace MIDIEvents { // Build a generic MIDI event uint32 Event(EventType eventType, uint8 midiChannel, uint8 dataByte1, uint8 dataByte2) { return (eventType << 4) | (midiChannel & 0x0F) | (dataByte1 << 8) | (dataByte2 << 16); } // Build a MIDI CC event uint32 CC(MidiCC midiCC, uint8 midiChannel, uint8 param) { return Event(evControllerChange, midiChannel, static_cast(midiCC), param); } // Build a MIDI Pitchbend event uint32 PitchBend(uint8 midiChannel, uint16 bendAmount) { return Event(evPitchBend, midiChannel, static_cast(bendAmount & 0x7F), static_cast(bendAmount >> 7)); } // Build a MIDI Program Change event uint32 ProgramChange(uint8 midiChannel, uint8 program) { return Event(evProgramChange, midiChannel, program, 0); } // Build a MIDI Note Off event uint32 NoteOff(uint8 midiChannel, uint8 note, uint8 velocity) { return Event(evNoteOff, midiChannel, note, velocity); } // Build a MIDI Note On event uint32 NoteOn(uint8 midiChannel, uint8 note, uint8 velocity) { return Event(evNoteOn, midiChannel, note, velocity); } // Build a MIDI System Event uint8 System(SystemEvent eventType) { return static_cast((evSystem << 4) | eventType); } // Get MIDI channel from a MIDI event uint8 GetChannelFromEvent(uint32 midiMsg) { return static_cast((midiMsg & 0xF)); } // Get MIDI Event type from a MIDI event EventType GetTypeFromEvent(uint32 midiMsg) { return static_cast(((midiMsg >> 4) & 0xF)); } // Get first data byte from a MIDI event uint8 GetDataByte1FromEvent(uint32 midiMsg) { return static_cast(((midiMsg >> 8) & 0xFF)); } // Get second data byte from a MIDI event uint8 GetDataByte2FromEvent(uint32 midiMsg) { return static_cast(((midiMsg >> 16) & 0xFF)); } // Get the length of a MIDI event in bytes uint8 GetEventLength(uint8 firstByte) { uint8 msgSize = 3; switch(firstByte & 0xF0) { case 0xC0: case 0xD0: msgSize = 2; break; case 0xF0: switch(firstByte) { case 0xF1: case 0xF3: msgSize = 2; break; case 0xF2: msgSize = 3; break; default: msgSize = 1; break; } break; } return msgSize; } // MIDI CC Names const char* const MidiCCNames[MIDICC_end + 1] = { "BankSelect [Coarse]", //0 "ModulationWheel [Coarse]", //1 "Breathcontroller [Coarse]", //2 "", //3 "FootPedal [Coarse]", //4 "PortamentoTime [Coarse]", //5 "DataEntry [Coarse]", //6 "Volume [Coarse]", //7 "Balance [Coarse]", //8 "", //9 "Panposition [Coarse]", //10 "Expression [Coarse]", //11 "EffectControl1 [Coarse]", //12 "EffectControl2 [Coarse]", //13 "", //14 "", //15 "GeneralPurposeSlider1", //16 "GeneralPurposeSlider2", //17 "GeneralPurposeSlider3", //18 "GeneralPurposeSlider4", //19 "", //20 "", //21 "", //22 "", //23 "", //24 "", //25 "", //26 "", //27 "", //28 "", //29 "", //30 "", //31 "BankSelect [Fine]", //32 "ModulationWheel [Fine]", //33 "Breathcontroller [Fine]", //34 "", //35 "FootPedal [Fine]", //36 "PortamentoTime [Fine]", //37 "DataEntry [Fine]", //38 "Volume [Fine]", //39 "Balance [Fine]", //40 "", //41 "Panposition [Fine]", //42 "Expression [Fine]", //43 "EffectControl1 [Fine]", //44 "EffectControl2 [Fine]", //45 "", //46 "", //47 "", //48 "", //49 "", //50 "", //51 "", //52 "", //53 "", //54 "", //55 "", //56 "", //57 "", //58 "", //59 "", //60 "", //61 "", //62 "", //63 "HoldPedal [OnOff]", //64 "Portamento [OnOff]", //65 "SustenutoPedal [OnOff]", //66 "SoftPedal [OnOff]", //67 "LegatoPedal [OnOff]", //68 "Hold2Pedal [OnOff]", //69 "SoundVariation", //70 "SoundTimbre", //71 "SoundReleaseTime", //72 "SoundAttackTime", //73 "SoundBrightness", //74 "SoundControl6", //75 "SoundControl7", //76 "SoundControl8", //77 "SoundControl9", //78 "SoundControl10", //79 "GeneralPurposeButton1 [OnOff]",//80 "GeneralPurposeButton2 [OnOff]",//81 "GeneralPurposeButton3 [OnOff]",//82 "GeneralPurposeButton4 [OnOff]",//83 "", //84 "", //85 "", //86 "", //87 "", //88 "", //89 "", //90 "EffectsLevel", //91 "TremoloLevel", //92 "ChorusLevel", //93 "CelesteLevel", //94 "PhaserLevel", //95 "DataButtonIncrement", //96 "DataButtonDecrement", //97 "NonRegisteredParameter [Fine]",//98 "NonRegisteredParameter [Coarse]",//99 "RegisteredParameter [Fine]", //100 "RegisteredParameter [Coarse]", //101 "", //102 "", //103 "", //104 "", //105 "", //106 "", //107 "", //108 "", //109 "", //110 "", //111 "", //112 "", //113 "", //114 "", //115 "", //116 "", //117 "", //118 "", //119 "AllSoundOff", //120 "AllControllersOff", //121 "LocalKeyboard [OnOff]", //122 "AllNotesOff", //123 "OmniModeOff", //124 "OmniModeOn", //125 "MonoOperation", //126 "PolyOperation", //127 }; } // End namespace OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/MIDIEvents.h0000644000175000017500000001217014052666041020160 00000000000000/* * MIDIEvents.h * ------------ * Purpose: MIDI event handling, event lists, ... * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" OPENMPT_NAMESPACE_BEGIN // MIDI related enums and helper functions namespace MIDIEvents { // MIDI Event Types enum EventType { evNoteOff = 0x8, // Note Off event evNoteOn = 0x9, // Note On event evPolyAftertouch = 0xA, // Poly Aftertouch / Poly Pressure event evControllerChange = 0xB, // Controller Change (see MidiCC enum) evProgramChange = 0xC, // Program Change evChannelAftertouch = 0xD, // Channel Aftertouch evPitchBend = 0xE, // Pitchbend event (see PitchBend enum) evSystem = 0xF, // System event (see SystemEvent enum) }; // System Events (Fx ...) enum SystemEvent { sysExStart = 0x0, // Begin of System Exclusive message sysQuarterFrame = 0x1, // Quarter Frame Message sysPositionPointer = 0x2, // Song Position Pointer sysSongSelect = 0x3, // Song Select sysTuneRequest = 0x6, // Tune Request sysExEnd = 0x7, // End of System Exclusive message sysMIDIClock = 0x8, // MIDI Clock event sysMIDITick = 0x9, // MIDI Tick event sysStart = 0xA, // Start Song sysContinue = 0xB, // Continue Song sysStop = 0xC, // Stop Song sysActiveSense = 0xE, // Active Sense Message sysReset = 0xF, // Reset Device }; // MIDI Pitchbend Constants enum PitchBend { pitchBendMin = 0x00, pitchBendCentre = 0x2000, pitchBendMax = 0x3FFF }; // MIDI Continuous Controller Codes // http://home.roadrunner.com/~jgglatt/tech/midispec/ctllist.htm enum MidiCC { MIDICC_start = 0, MIDICC_BankSelect_Coarse = MIDICC_start, MIDICC_ModulationWheel_Coarse = 1, MIDICC_Breathcontroller_Coarse = 2, MIDICC_FootPedal_Coarse = 4, MIDICC_PortamentoTime_Coarse = 5, MIDICC_DataEntry_Coarse = 6, MIDICC_Volume_Coarse = 7, MIDICC_Balance_Coarse = 8, MIDICC_Panposition_Coarse = 10, MIDICC_Expression_Coarse = 11, MIDICC_EffectControl1_Coarse = 12, MIDICC_EffectControl2_Coarse = 13, MIDICC_GeneralPurposeSlider1 = 16, MIDICC_GeneralPurposeSlider2 = 17, MIDICC_GeneralPurposeSlider3 = 18, MIDICC_GeneralPurposeSlider4 = 19, MIDICC_BankSelect_Fine = 32, MIDICC_ModulationWheel_Fine = 33, MIDICC_Breathcontroller_Fine = 34, MIDICC_FootPedal_Fine = 36, MIDICC_PortamentoTime_Fine = 37, MIDICC_DataEntry_Fine = 38, MIDICC_Volume_Fine = 39, MIDICC_Balance_Fine = 40, MIDICC_Panposition_Fine = 42, MIDICC_Expression_Fine = 43, MIDICC_EffectControl1_Fine = 44, MIDICC_EffectControl2_Fine = 45, MIDICC_HoldPedal_OnOff = 64, MIDICC_Portamento_OnOff = 65, MIDICC_SustenutoPedal_OnOff = 66, MIDICC_SoftPedal_OnOff = 67, MIDICC_LegatoPedal_OnOff = 68, MIDICC_Hold2Pedal_OnOff = 69, MIDICC_SoundVariation = 70, MIDICC_SoundTimbre = 71, MIDICC_SoundReleaseTime = 72, MIDICC_SoundAttackTime = 73, MIDICC_SoundBrightness = 74, MIDICC_SoundControl6 = 75, MIDICC_SoundControl7 = 76, MIDICC_SoundControl8 = 77, MIDICC_SoundControl9 = 78, MIDICC_SoundControl10 = 79, MIDICC_GeneralPurposeButton1_OnOff = 80, MIDICC_GeneralPurposeButton2_OnOff = 81, MIDICC_GeneralPurposeButton3_OnOff = 82, MIDICC_GeneralPurposeButton4_OnOff = 83, MIDICC_EffectsLevel = 91, MIDICC_TremoloLevel = 92, MIDICC_ChorusLevel = 93, MIDICC_CelesteLevel = 94, MIDICC_PhaserLevel = 95, MIDICC_DataButtonincrement = 96, MIDICC_DataButtondecrement = 97, MIDICC_NonRegisteredParameter_Fine = 98, MIDICC_NonRegisteredParameter_Coarse = 99, MIDICC_RegisteredParameter_Fine = 100, MIDICC_RegisteredParameter_Coarse = 101, MIDICC_AllSoundOff = 120, MIDICC_AllControllersOff = 121, MIDICC_LocalKeyboard_OnOff = 122, MIDICC_AllNotesOff = 123, MIDICC_OmniModeOff = 124, MIDICC_OmniModeOn = 125, MIDICC_MonoOperation = 126, MIDICC_PolyOperation = 127, MIDICC_end = MIDICC_PolyOperation, }; // MIDI CC Names extern const char* const MidiCCNames[MIDICC_end + 1]; // Charset::UTF8 // Build a generic MIDI event uint32 Event(EventType eventType, uint8 midiChannel, uint8 dataByte1, uint8 dataByte2); // Build a MIDI CC event uint32 CC(MidiCC midiCC, uint8 midiChannel, uint8 param); // Build a MIDI Pitchbend event uint32 PitchBend(uint8 midiChannel, uint16 bendAmount); // Build a MIDI Program Change event uint32 ProgramChange(uint8 midiChannel, uint8 program); // Build a MIDI Note Off event uint32 NoteOff(uint8 midiChannel, uint8 note, uint8 velocity); // Build a MIDI Note On event uint32 NoteOn(uint8 midiChannel, uint8 note, uint8 velocity); // Build a MIDI System Event uint8 System(SystemEvent eventType); // Get MIDI channel from a MIDI event uint8 GetChannelFromEvent(uint32 midiMsg); // Get MIDI Event type from a MIDI event EventType GetTypeFromEvent(uint32 midiMsg); // Get first data byte from a MIDI event uint8 GetDataByte1FromEvent(uint32 midiMsg); // Get second data byte from a MIDI event uint8 GetDataByte2FromEvent(uint32 midiMsg); // Get the length of a MIDI event in bytes uint8 GetEventLength(uint8 firstByte); } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/MIDIMacros.cpp0000644000175000017500000002542714175042045020502 00000000000000/* * MIDIMacros.cpp * -------------- * Purpose: Helper functions / classes for MIDI Macro functionality. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "MIDIMacros.h" #include "../soundlib/MIDIEvents.h" #ifdef MODPLUG_TRACKER #include "Sndfile.h" #include "plugins/PlugInterface.h" #endif // MODPLUG_TRACKER OPENMPT_NAMESPACE_BEGIN ParameteredMacro MIDIMacroConfig::GetParameteredMacroType(uint32 macroIndex) const { const std::string macro = SFx[macroIndex].NormalizedString(); for(uint32 i = 0; i < kSFxMax; i++) { ParameteredMacro sfx = static_cast(i); if(sfx != kSFxCustom) { if(macro == CreateParameteredMacro(sfx)) return sfx; } } // Special macros with additional "parameter": if(macro.size() == 5 && macro.compare(CreateParameteredMacro(kSFxCC, MIDIEvents::MIDICC_start)) >= 0 && macro.compare(CreateParameteredMacro(kSFxCC, MIDIEvents::MIDICC_end)) <= 0) return kSFxCC; if(macro.size() == 7 && macro.compare(CreateParameteredMacro(kSFxPlugParam, 0)) >= 0 && macro.compare(CreateParameteredMacro(kSFxPlugParam, 0x17F)) <= 0) return kSFxPlugParam; return kSFxCustom; // custom / unknown } // Retrieve Zxx (Z80-ZFF) type from current macro configuration FixedMacro MIDIMacroConfig::GetFixedMacroType() const { // Compare with all possible preset patterns for(uint32 i = 0; i < kZxxMax; i++) { FixedMacro zxx = static_cast(i); if(zxx != kZxxCustom) { // Prepare macro pattern to compare decltype(Zxx) fixedMacros{}; CreateFixedMacro(fixedMacros, zxx); if(fixedMacros == Zxx) return zxx; } } return kZxxCustom; // Custom setup } void MIDIMacroConfig::CreateParameteredMacro(Macro ¶meteredMacro, ParameteredMacro macroType, int subType) const { switch(macroType) { case kSFxUnused: parameteredMacro = ""; break; case kSFxCutoff: parameteredMacro = "F0F000z"; break; case kSFxReso: parameteredMacro = "F0F001z"; break; case kSFxFltMode: parameteredMacro = "F0F002z"; break; case kSFxDryWet: parameteredMacro = "F0F003z"; break; case kSFxCC: parameteredMacro = MPT_AFORMAT("Bc{}z")(mpt::afmt::HEX0<2>(subType & 0x7F)); break; case kSFxPlugParam: parameteredMacro = MPT_AFORMAT("F0F{}z")(mpt::afmt::HEX0<3>(std::min(subType, 0x17F) + 0x80)); break; case kSFxChannelAT: parameteredMacro = "Dcz"; break; case kSFxPolyAT: parameteredMacro = "Acnz"; break; case kSFxPitch: parameteredMacro = "Ec00z"; break; case kSFxProgChange: parameteredMacro = "Ccz"; break; case kSFxCustom: default: MPT_ASSERT_NOTREACHED(); break; } } std::string MIDIMacroConfig::CreateParameteredMacro(ParameteredMacro macroType, int subType) const { Macro parameteredMacro{}; CreateParameteredMacro(parameteredMacro, macroType, subType); return parameteredMacro; } // Create Zxx (Z80 - ZFF) from preset void MIDIMacroConfig::CreateFixedMacro(std::array &fixedMacros, FixedMacro macroType) const { for(uint32 i = 0; i < kZxxMacros; i++) { uint32 param = i; switch(macroType) { case kZxxUnused: fixedMacros[i] = ""; break; case kZxxReso4Bit: param = i * 8; if(i < 16) fixedMacros[i] = MPT_AFORMAT("F0F001{}")(mpt::afmt::HEX0<2>(param)); else fixedMacros[i] = ""; break; case kZxxReso7Bit: fixedMacros[i] = MPT_AFORMAT("F0F001{}")(mpt::afmt::HEX0<2>(param)); break; case kZxxCutoff: fixedMacros[i] = MPT_AFORMAT("F0F000{}")(mpt::afmt::HEX0<2>(param)); break; case kZxxFltMode: fixedMacros[i] = MPT_AFORMAT("F0F002{}")(mpt::afmt::HEX0<2>(param)); break; case kZxxResoFltMode: param = (i & 0x0F) * 8; if(i < 16) fixedMacros[i] = MPT_AFORMAT("F0F001{}")(mpt::afmt::HEX0<2>(param)); else if(i < 32) fixedMacros[i] = MPT_AFORMAT("F0F002{}")(mpt::afmt::HEX0<2>(param)); else fixedMacros[i] = ""; break; case kZxxChannelAT: fixedMacros[i] = MPT_AFORMAT("Dc{}")(mpt::afmt::HEX0<2>(param)); break; case kZxxPolyAT: fixedMacros[i] = MPT_AFORMAT("Acn{}")(mpt::afmt::HEX0<2>(param)); break; case kZxxPitch: fixedMacros[i] = MPT_AFORMAT("Ec00{}")(mpt::afmt::HEX0<2>(param)); break; case kZxxProgChange: fixedMacros[i] = MPT_AFORMAT("Cc{}")(mpt::afmt::HEX0<2>(param)); break; case kZxxCustom: default: MPT_ASSERT_NOTREACHED(); continue; } } } bool MIDIMacroConfig::operator== (const MIDIMacroConfig &other) const { return std::equal(begin(), end(), other.begin()); } #ifdef MODPLUG_TRACKER // Returns macro description including plugin parameter / MIDI CC information CString MIDIMacroConfig::GetParameteredMacroName(uint32 macroIndex, IMixPlugin *plugin) const { const ParameteredMacro macroType = GetParameteredMacroType(macroIndex); switch(macroType) { case kSFxPlugParam: { const int param = MacroToPlugParam(macroIndex); CString formattedName; formattedName.Format(_T("Param %d"), param); #ifndef NO_PLUGINS if(plugin != nullptr) { CString paramName = plugin->GetParamName(param); if(!paramName.IsEmpty()) { formattedName += _T(" (") + paramName + _T(")"); } } else #else MPT_UNREFERENCED_PARAMETER(plugin); #endif // NO_PLUGINS { formattedName += _T(" (N/A)"); } return formattedName; } case kSFxCC: { CString formattedCC; formattedCC.Format(_T("MIDI CC %d"), MacroToMidiCC(macroIndex)); return formattedCC; } default: return GetParameteredMacroName(macroType); } } // Returns generic macro description. CString MIDIMacroConfig::GetParameteredMacroName(ParameteredMacro macroType) const { switch(macroType) { case kSFxUnused: return _T("Unused"); case kSFxCutoff: return _T("Set Filter Cutoff"); case kSFxReso: return _T("Set Filter Resonance"); case kSFxFltMode: return _T("Set Filter Mode"); case kSFxDryWet: return _T("Set Plugin Dry/Wet Ratio"); case kSFxPlugParam: return _T("Control Plugin Parameter..."); case kSFxCC: return _T("MIDI CC..."); case kSFxChannelAT: return _T("Channel Aftertouch"); case kSFxPolyAT: return _T("Polyphonic Aftertouch"); case kSFxPitch: return _T("Pitch Bend"); case kSFxProgChange: return _T("MIDI Program Change"); case kSFxCustom: default: return _T("Custom"); } } // Returns generic macro description. CString MIDIMacroConfig::GetFixedMacroName(FixedMacro macroType) const { switch(macroType) { case kZxxUnused: return _T("Unused"); case kZxxReso4Bit: return _T("Z80 - Z8F controls Resonant Filter Resonance"); case kZxxReso7Bit: return _T("Z80 - ZFF controls Resonant Filter Resonance"); case kZxxCutoff: return _T("Z80 - ZFF controls Resonant Filter Cutoff"); case kZxxFltMode: return _T("Z80 - ZFF controls Resonant Filter Mode"); case kZxxResoFltMode: return _T("Z80 - Z9F controls Resonance + Filter Mode"); case kZxxChannelAT: return _T("Z80 - ZFF controls Channel Aftertouch"); case kZxxPolyAT: return _T("Z80 - ZFF controls Polyphonic Aftertouch"); case kZxxPitch: return _T("Z80 - ZFF controls Pitch Bend"); case kZxxProgChange: return _T("Z80 - ZFF controls MIDI Program Change"); case kZxxCustom: default: return _T("Custom"); } } int MIDIMacroConfig::MacroToPlugParam(uint32 macroIndex) const { const std::string macro = SFx[macroIndex].NormalizedString(); int code = 0; const char *param = macro.c_str(); param += 4; if ((param[0] >= '0') && (param[0] <= '9')) code = (param[0] - '0') << 4; else if ((param[0] >= 'A') && (param[0] <= 'F')) code = (param[0] - 'A' + 0x0A) << 4; if ((param[1] >= '0') && (param[1] <= '9')) code += (param[1] - '0'); else if ((param[1] >= 'A') && (param[1] <= 'F')) code += (param[1] - 'A' + 0x0A); if (macro.size() >= 4 && macro[3] == '0') return (code - 128); else return (code + 128); } int MIDIMacroConfig::MacroToMidiCC(uint32 macroIndex) const { const std::string macro = SFx[macroIndex].NormalizedString(); int code = 0; const char *param = macro.c_str(); param += 2; if ((param[0] >= '0') && (param[0] <= '9')) code = (param[0] - '0') << 4; else if ((param[0] >= 'A') && (param[0] <= 'F')) code = (param[0] - 'A' + 0x0A) << 4; if ((param[1] >= '0') && (param[1] <= '9')) code += (param[1] - '0'); else if ((param[1] >= 'A') && (param[1] <= 'F')) code += (param[1] - 'A' + 0x0A); return code; } int MIDIMacroConfig::FindMacroForParam(PlugParamIndex param) const { for(int macroIndex = 0; macroIndex < kSFxMacros; macroIndex++) { if(GetParameteredMacroType(macroIndex) == kSFxPlugParam && MacroToPlugParam(macroIndex) == param) { return macroIndex; } } return -1; } #endif // MODPLUG_TRACKER // Check if the MIDI Macro configuration used is the default one, // i.e. the configuration that is assumed when loading a file that has no macros embedded. bool MIDIMacroConfig::IsMacroDefaultSetupUsed() const { return *this == MIDIMacroConfig{}; } // Reset MIDI macro config to default values. void MIDIMacroConfig::Reset() { std::fill(begin(), end(), Macro{}); Global[MIDIOUT_START] = "FF"; Global[MIDIOUT_STOP] = "FC"; Global[MIDIOUT_NOTEON] = "9c n v"; Global[MIDIOUT_NOTEOFF] = "9c n 0"; Global[MIDIOUT_PROGRAM] = "Cc p"; // SF0: Z00-Z7F controls cutoff CreateParameteredMacro(0, kSFxCutoff); // Z80-Z8F controls resonance CreateFixedMacro(kZxxReso4Bit); } // Clear all Zxx macros so that they do nothing. void MIDIMacroConfig::ClearZxxMacros() { std::fill(SFx.begin(), SFx.end(), Macro{}); std::fill(Zxx.begin(), Zxx.end(), Macro{}); } // Sanitize all macro config strings. void MIDIMacroConfig::Sanitize() { for(auto ¯o : *this) { macro.Sanitize(); } } // Fix old-format (not conforming to IT's MIDI macro definitions) MIDI config strings. void MIDIMacroConfig::UpgradeMacros() { for(auto ¯o : *this) { macro.UpgradeLegacyMacro(); } } // Normalize by removing blanks and other unwanted characters from macro strings for internal usage. std::string MIDIMacroConfig::Macro::NormalizedString() const { std::string sanitizedMacro = *this; std::string::size_type pos; while((pos = sanitizedMacro.find_first_not_of("0123456789ABCDEFabchmnopsuvxyz")) != std::string::npos) { sanitizedMacro.erase(pos, 1); } return sanitizedMacro; } void MIDIMacroConfig::Macro::Sanitize() noexcept { m_data.back() = '\0'; const auto length = Length(); std::fill(m_data.begin() + length, m_data.end(), '\0'); for(size_t i = 0; i < length; i++) { if(m_data[i] < 32 || m_data[i] >= 127) m_data[i] = ' '; } } void MIDIMacroConfig::Macro::UpgradeLegacyMacro() noexcept { for(auto &c : m_data) { if(c >= 'a' && c <= 'f') // Both A-F and a-f were treated as hex constants { c = c - 'a' + 'A'; } else if(c == 'K' || c == 'k') // Channel was K or k { c = 'c'; } else if(c == 'X' || c == 'x' || c == 'Y' || c == 'y') // Those were pointless { c = 'z'; } } } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/MIDIMacros.h0000644000175000017500000001460714175042045020145 00000000000000/* * MIDIMacros.h * ------------ * Purpose: Helper functions / classes for MIDI Macro functionality. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "openmpt/base/Endian.hpp" OPENMPT_NAMESPACE_BEGIN enum { kGlobalMacros = 9, // Number of global macros kSFxMacros = 16, // Number of parametered macros kZxxMacros = 128, // Number of fixed macros kMacroLength = 32, // Max number of chars per macro }; OPENMPT_NAMESPACE_END #ifdef MODPLUG_TRACKER #include "plugins/PluginStructs.h" #endif // MODPLUG_TRACKER OPENMPT_NAMESPACE_BEGIN // Parametered macro presets enum ParameteredMacro { kSFxUnused = 0, kSFxCutoff, // Z00 - Z7F controls resonant filter cutoff kSFxReso, // Z00 - Z7F controls resonant filter resonance kSFxFltMode, // Z00 - Z7F controls resonant filter mode (lowpass / highpass) kSFxDryWet, // Z00 - Z7F controls plugin Dry / Wet ratio kSFxPlugParam, // Z00 - Z7F controls a plugin parameter kSFxCC, // Z00 - Z7F controls MIDI CC kSFxChannelAT, // Z00 - Z7F controls Channel Aftertouch kSFxPolyAT, // Z00 - Z7F controls Poly Aftertouch kSFxPitch, // Z00 - Z7F controls Pitch Bend kSFxProgChange, // Z00 - Z7F controls MIDI Program Change kSFxCustom, kSFxMax }; // Fixed macro presets enum FixedMacro { kZxxUnused = 0, kZxxReso4Bit, // Z80 - Z8F controls resonant filter resonance kZxxReso7Bit, // Z80 - ZFF controls resonant filter resonance kZxxCutoff, // Z80 - ZFF controls resonant filter cutoff kZxxFltMode, // Z80 - ZFF controls resonant filter mode (lowpass / highpass) kZxxResoFltMode, // Z80 - Z9F controls resonance + filter mode kZxxChannelAT, // Z80 - ZFF controls Channel Aftertouch kZxxPolyAT, // Z80 - ZFF controls Poly Aftertouch kZxxPitch, // Z80 - ZFF controls Pitch Bend kZxxProgChange, // Z80 - ZFF controls MIDI Program Change kZxxCustom, kZxxMax }; // Global macro types enum GlobalMacro { MIDIOUT_START = 0, MIDIOUT_STOP, MIDIOUT_TICK, MIDIOUT_NOTEON, MIDIOUT_NOTEOFF, MIDIOUT_VOLUME, MIDIOUT_PAN, MIDIOUT_BANKSEL, MIDIOUT_PROGRAM, }; struct MIDIMacroConfigData { struct Macro { public: Macro &operator=(const Macro &other) = default; Macro &operator=(const std::string_view other) noexcept { const size_t copyLength = std::min({m_data.size() - 1u, other.size(), other.find('\0')}); std::copy(other.begin(), other.begin() + copyLength, m_data.begin()); m_data[copyLength] = '\0'; Sanitize(); return *this; } bool operator==(const Macro &other) const noexcept { return m_data == other.m_data; // Don't care about data past null-terminator as operator= and Sanitize() ensure there is no data behind it. } bool operator!=(const Macro &other) const noexcept { return !(*this == other); } operator mpt::span() const noexcept { return {m_data.data(), Length()}; } operator std::string_view() const noexcept { return {m_data.data(), Length()}; } operator std::string() const { return {m_data.data(), Length()}; } MPT_CONSTEXPR20_FUN size_t Length() const noexcept { return static_cast(std::distance(m_data.begin(), std::find(m_data.begin(), m_data.end(), '\0'))); } MPT_CONSTEXPR20_FUN void Clear() noexcept { m_data.fill('\0'); } // Remove blanks and other unwanted characters from macro strings for internal usage. std::string NormalizedString() const; void Sanitize() noexcept; void UpgradeLegacyMacro() noexcept; private: std::array m_data; }; std::array Global; std::array SFx; // Parametered macros for Z00...Z7F std::array Zxx; // Fixed macros Z80...ZFF constexpr Macro *begin() noexcept {return Global.data(); } constexpr const Macro *begin() const noexcept { return Global.data(); } constexpr Macro *end() noexcept { return Zxx.data() + Zxx.size(); } constexpr const Macro *end() const noexcept { return Zxx.data() + Zxx.size(); } }; // This is directly written to files, so the size must be correct! MPT_BINARY_STRUCT(MIDIMacroConfigData::Macro, 32) MPT_BINARY_STRUCT(MIDIMacroConfigData, 4896) class MIDIMacroConfig : public MIDIMacroConfigData { public: MIDIMacroConfig() { Reset(); } // Get macro type from a macro string ParameteredMacro GetParameteredMacroType(uint32 macroIndex) const; FixedMacro GetFixedMacroType() const; // Create a new macro protected: void CreateParameteredMacro(Macro ¶meteredMacro, ParameteredMacro macroType, int subType) const; public: void CreateParameteredMacro(uint32 macroIndex, ParameteredMacro macroType, int subType = 0) { if(macroIndex < std::size(SFx)) CreateParameteredMacro(SFx[macroIndex], macroType, subType); } std::string CreateParameteredMacro(ParameteredMacro macroType, int subType = 0) const; protected: void CreateFixedMacro(std::array &fixedMacros, FixedMacro macroType) const; public: void CreateFixedMacro(FixedMacro macroType) { CreateFixedMacro(Zxx, macroType); } bool operator==(const MIDIMacroConfig &other) const; bool operator!=(const MIDIMacroConfig &other) const { return !(*this == other); } #ifdef MODPLUG_TRACKER // Translate macro type or macro string to macro name CString GetParameteredMacroName(uint32 macroIndex, IMixPlugin *plugin = nullptr) const; CString GetParameteredMacroName(ParameteredMacro macroType) const; CString GetFixedMacroName(FixedMacro macroType) const; // Extract information from a parametered macro string. int MacroToPlugParam(uint32 macroIndex) const; int MacroToMidiCC(uint32 macroIndex) const; // Check if any macro can automate a given plugin parameter int FindMacroForParam(PlugParamIndex param) const; #endif // MODPLUG_TRACKER // Check if a given set of macros is the default IT macro set. bool IsMacroDefaultSetupUsed() const; // Reset MIDI macro config to default values. void Reset(); // Clear all Zxx macros so that they do nothing. void ClearZxxMacros(); // Sanitize all macro config strings. void Sanitize(); // Fix old-format (not conforming to IT's MIDI macro definitions) MIDI config strings. void UpgradeMacros(); }; static_assert(sizeof(MIDIMacroConfig) == sizeof(MIDIMacroConfigData)); // this is directly written to files, so the size must be correct! OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Mixer.h0000644000175000017500000000444714144043206017337 00000000000000/* * Mixer.h * ------- * Purpose: Basic mixer constants * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "openmpt/soundbase/MixSample.hpp" OPENMPT_NAMESPACE_BEGIN #define MPT_INTMIXER #ifdef MPT_INTMIXER using mixsample_t = MixSampleIntTraits::sample_type; enum { MIXING_FILTER_PRECISION = MixSampleIntTraits::filter_precision_bits }; // Fixed point resonant filter bits #else using mixsample_t = MixSampleFloat; #endif enum { MIXING_ATTENUATION = MixSampleIntTraits::mix_headroom_bits }; enum { MIXING_FRACTIONAL_BITS = MixSampleIntTraits::mix_fractional_bits }; inline constexpr float MIXING_SCALEF = MixSampleIntTraits::mix_scale; #ifdef MPT_INTMIXER static_assert(sizeof(mixsample_t) == 4); static_assert(MIXING_FILTER_PRECISION == 24); static_assert(MIXING_ATTENUATION == 4); static_assert(MIXING_FRACTIONAL_BITS == 27); static_assert(MixSampleIntTraits::mix_clip_max == int32(0x7FFFFFF)); static_assert(MixSampleIntTraits::mix_clip_min == (0 - int32(0x7FFFFFF))); static_assert(MIXING_SCALEF == 134217728.0f); #else static_assert(sizeof(mixsample_t) == 4); #endif #define MIXBUFFERSIZE 512 #define NUMMIXINPUTBUFFERS 4 #define VOLUMERAMPPRECISION 12 // Fractional bits in volume ramp variables // The absolute maximum number of sampling points any interpolation algorithm is going to look at in any direction from the current sampling point // Currently, the maximum is 4 sampling points forwards and 3 sampling points backwards (Polyphase / FIR algorithms). // Hence, this value must be at least 4. inline constexpr uint8 InterpolationMaxLookahead = 4; // While we could directly use the previous value in various places such as the interpolation wrap-around handling at loop points, // choosing a higher value (e.g. 16) will reduce CPU usage when using many extremely short (length < 16) samples. inline constexpr uint8 InterpolationLookaheadBufferSize = 16; static_assert(InterpolationLookaheadBufferSize >= InterpolationMaxLookahead); // Maximum size of a sampling point of a sample, in bytes. // The biggest sampling point size is currently 16-bit stereo = 2 * 2 bytes. inline constexpr uint8 MaxSamplingPointSize = 4; OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/MixerInterface.h0000644000175000017500000000713214052666041021160 00000000000000/* * MixerInterface.h * ---------------- * Purpose: The basic mixer interface and main mixer loop, completely agnostic of the actual sample input / output formats. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "Snd_defs.h" #include "ModChannel.h" OPENMPT_NAMESPACE_BEGIN class CResampler; ////////////////////////////////////////////////////////////////////////// // Sample conversion traits template struct MixerTraits { enum : int { numChannelsIn = channelsIn }; // Number of channels in sample enum : int { numChannelsOut = channelsOut }; // Number of mixer output channels typedef out output_t; // Output buffer sample type typedef in input_t; // Input buffer sample type typedef out outbuf_t[channelsOut]; // Output buffer sampling point type // To perform sample conversion, add a function with the following signature to your derived classes: // static MPT_CONSTEXPRINLINE output_t Convert(const input_t x) }; ////////////////////////////////////////////////////////////////////////// // Interpolation templates template struct NoInterpolation { MPT_FORCEINLINE void Start(const ModChannel &, const CResampler &) { } MPT_FORCEINLINE void End(const ModChannel &) { } MPT_FORCEINLINE void operator() (typename Traits::outbuf_t &outSample, const typename Traits::input_t * const inBuffer, const int32) { static_assert(static_cast(Traits::numChannelsIn) <= static_cast(Traits::numChannelsOut), "Too many input channels"); for(int i = 0; i < Traits::numChannelsIn; i++) { outSample[i] = Traits::Convert(inBuffer[i]); } } }; // Other interpolation algorithms depend on the input format type (integer / float) and can thus be found in FloatMixer.h and IntMixer.h ////////////////////////////////////////////////////////////////////////// // Main sample render loop template // Template parameters: // Traits: A class derived from MixerTraits that defines the number of channels, sample buffer types, etc.. // InterpolationFunc: Functor for reading the sample data and doing the SRC // FilterFunc: Functor for applying the resonant filter // MixFunc: Functor for mixing the computed sample data into the output buffer template static void SampleLoop(ModChannel &chn, const CResampler &resampler, typename Traits::output_t * MPT_RESTRICT outBuffer, unsigned int numSamples) { ModChannel &c = chn; const typename Traits::input_t * MPT_RESTRICT inSample = static_cast(c.pCurrentSample); InterpolationFunc interpolate; FilterFunc filter; MixFunc mix; // Do initialisation if necessary interpolate.Start(c, resampler); filter.Start(c); mix.Start(c); unsigned int samples = numSamples; SamplePosition smpPos = c.position; // Fixed-point sample position const SamplePosition increment = c.increment; // Fixed-point sample increment while(samples--) { typename Traits::outbuf_t outSample; interpolate(outSample, inSample + smpPos.GetInt() * Traits::numChannelsIn, smpPos.GetFract()); filter(outSample, c); mix(outSample, c, outBuffer); outBuffer += Traits::numChannelsOut; smpPos += increment; } mix.End(c); filter.End(c); interpolate.End(c); c.position = smpPos; } // Type of the SampleLoop function above typedef void (*MixFuncInterface)(ModChannel &, const CResampler &, mixsample_t *, unsigned int); OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/MixerLoops.cpp0000644000175000017500000001033314047662056020712 00000000000000/* * MixerLoops.cpp * -------------- * Purpose: Utility inner loops for mixer-related functionality. * Notes : This file contains performance-critical loops. * Authors: Olivier Lapicque * OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "MixerLoops.h" #include "Snd_defs.h" #include "ModChannel.h" OPENMPT_NAMESPACE_BEGIN void FloatToStereoMix(const float *pIn1, const float *pIn2, int32 *pOut, uint32 nCount, const float _f2ic) { for(uint32 i=0; i=0; i--) { pFrontBuf[i*4+3] = pRearBuf[i*2+1]; pFrontBuf[i*4+2] = pRearBuf[i*2+0]; pFrontBuf[i*4+1] = pFrontBuf[i*2+1]; pFrontBuf[i*4+0] = pFrontBuf[i*2+0]; } } void MonoFromStereo(mixsample_t *pMixBuf, uint32 nSamples) { for(uint32 i=0; i(1.0 / (1 << 20)) // Decay threshold for floating point mixer void StereoFill(mixsample_t *pBuffer, uint32 nSamples, mixsample_t &rofs, mixsample_t &lofs) { if((!rofs) && (!lofs)) { InitMixBuffer(pBuffer, nSamples*2); return; } for(uint32 i=0; i 0 ? 255 : -255)) / 256; const mixsample_t x_r = mpt::rshift_signed(rofs + (mpt::rshift_signed(-rofs, sizeof(mixsample_t) * 8 - 1) & OFSDECAYMASK), OFSDECAYSHIFT); const mixsample_t x_l = mpt::rshift_signed(lofs + (mpt::rshift_signed(-lofs, sizeof(mixsample_t) * 8 - 1) & OFSDECAYMASK), OFSDECAYSHIFT); #else const mixsample_t x_r = rofs * (1.0f / (1 << OFSDECAYSHIFT)); const mixsample_t x_l = lofs * (1.0f / (1 << OFSDECAYSHIFT)); #endif rofs -= x_r; lofs -= x_l; pBuffer[i*2] = rofs; pBuffer[i*2+1] = lofs; } #ifndef MPT_INTMIXER if(fabs(rofs) < OFSTHRESHOLD) rofs = 0; if(fabs(lofs) < OFSTHRESHOLD) lofs = 0; #endif } void EndChannelOfs(ModChannel &chn, mixsample_t *pBuffer, uint32 nSamples) { mixsample_t rofs = chn.nROfs; mixsample_t lofs = chn.nLOfs; if((!rofs) && (!lofs)) { return; } for(uint32 i=0; i 0) && (gnChannels == 1 || gnChannels == 2 || gnChannels == 4) && (NumInputChannels == 0 || NumInputChannels == 1 || NumInputChannels == 2 || NumInputChannels == 4); } MixerSettings(); }; OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/MixFuncTable.cpp0000644000175000017500000000567713547440534021150 00000000000000/* * MixFuncTable.cpp * ---------------- * Purpose: Table containing all mixer functions. * Notes : The Visual Studio project settings for this file have been adjusted * to force function inlining, so that the mixer has a somewhat acceptable * performance in debug mode. If you need to debug anything here, be sure * to disable those optimizations if needed. * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Mixer.h" #include "Snd_defs.h" #include "ModChannel.h" #include "MixFuncTable.h" #ifdef MPT_INTMIXER #include "IntMixer.h" #else #include "FloatMixer.h" #endif // MPT_INTMIXER OPENMPT_NAMESPACE_BEGIN namespace MixFuncTable { #ifdef MPT_INTMIXER using I8M = Int8MToIntS; using I16M = Int16MToIntS; using I8S = Int8SToIntS; using I16S = Int16SToIntS; #else using I8M = Int8MToFloatS; using I16M = Int16MToFloatS; using I8S = Int8SToFloatS; using I16S = Int16SToFloatS; #endif // MPT_INTMIXER // Build mix function table for given resampling, filter and ramping settings: One function each for 8-Bit / 16-Bit Mono / Stereo #define BuildMixFuncTableRamp(resampling, filter, ramp) \ SampleLoop, filter, MixMono ## ramp >, \ SampleLoop, filter, MixMono ## ramp >, \ SampleLoop, filter, MixStereo ## ramp >, \ SampleLoop, filter, MixStereo ## ramp > // Build mix function table for given resampling, filter settings: With and without ramping #define BuildMixFuncTableFilter(resampling, filter) \ BuildMixFuncTableRamp(resampling, filter, NoRamp), \ BuildMixFuncTableRamp(resampling, filter, Ramp) // Build mix function table for given resampling settings: With and without filter #define BuildMixFuncTable(resampling) \ BuildMixFuncTableFilter(resampling, NoFilter), \ BuildMixFuncTableFilter(resampling, ResonantFilter) const MixFuncInterface Functions[6 * 16] = { BuildMixFuncTable(NoInterpolation), // No SRC BuildMixFuncTable(LinearInterpolation), // Linear SRC BuildMixFuncTable(FastSincInterpolation), // Fast Sinc (Cubic Spline) SRC BuildMixFuncTable(PolyphaseInterpolation), // Kaiser SRC BuildMixFuncTable(FIRFilterInterpolation), // FIR SRC BuildMixFuncTable(AmigaBlepInterpolation), // Amiga emulation }; #undef BuildMixFuncTableRamp #undef BuildMixFuncTableFilter #undef BuildMixFuncTable ResamplingIndex ResamplingModeToMixFlags(ResamplingMode resamplingMode) { switch(resamplingMode) { case SRCMODE_NEAREST: return ndxNoInterpolation; case SRCMODE_LINEAR: return ndxLinear; case SRCMODE_CUBIC: return ndxFastSinc; case SRCMODE_SINC8LP: return ndxKaiser; case SRCMODE_SINC8: return ndxFIRFilter; case SRCMODE_AMIGA: return ndxAmigaBlep; default: MPT_ASSERT_NOTREACHED(); } return ndxNoInterpolation; } } // namespace MixFuncTable OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/MixFuncTable.h0000644000175000017500000000202714052666041020572 00000000000000/* * MixFuncTable.h * -------------- * Purpose: Table containing all mixer functions. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "MixerInterface.h" OPENMPT_NAMESPACE_BEGIN namespace MixFuncTable { // Table index bits: // [b1-b0] format (8-bit-mono, 16-bit-mono, 8-bit-stereo, 16-bit-stereo) // [b2] ramp // [b3] filter // [b6-b4] src type // Sample type / processing type index enum FunctionIndex { ndx16Bit = 0x01, ndxStereo = 0x02, ndxRamp = 0x04, ndxFilter = 0x08, }; // SRC index enum ResamplingIndex { ndxNoInterpolation = 0x00, ndxLinear = 0x10, ndxFastSinc = 0x20, ndxKaiser = 0x30, ndxFIRFilter = 0x40, ndxAmigaBlep = 0x50, }; extern const MixFuncInterface Functions[6 * 16]; ResamplingIndex ResamplingModeToMixFlags(ResamplingMode resamplingMode); } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/ModChannel.cpp0000644000175000017500000001243714154475105020624 00000000000000/* * ModChannel.cpp * -------------- * Purpose: Module Channel header class and helpers * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Sndfile.h" #include "ModChannel.h" #include "tuning.h" OPENMPT_NAMESPACE_BEGIN void ModChannel::Reset(ResetFlags resetMask, const CSoundFile &sndFile, CHANNELINDEX sourceChannel, ChannelFlags muteFlag) { if(resetMask & resetSetPosBasic) { nNote = nNewNote = NOTE_NONE; nNewIns = nOldIns = 0; pModSample = nullptr; pModInstrument = nullptr; nPortamentoDest = 0; nCommand = CMD_NONE; nPatternLoopCount = 0; nPatternLoop = 0; nFadeOutVol = 0; dwFlags.set(CHN_KEYOFF | CHN_NOTEFADE); dwOldFlags.reset(); //IT compatibility 15. Retrigger if(sndFile.m_playBehaviour[kITRetrigger]) { nRetrigParam = 1; nRetrigCount = 0; } microTuning = 0; nTremorCount = 0; nEFxSpeed = 0; prevNoteOffset = 0; lastZxxParam = 0xFF; isFirstTick = false; triggerNote = false; isPreviewNote = false; isPaused = false; portaTargetReached = false; rowCommand.Clear(); } if(resetMask & resetSetPosAdvanced) { increment = SamplePosition(0); nPeriod = 0; position.Set(0); nLength = 0; nLoopStart = 0; nLoopEnd = 0; nROfs = nLOfs = 0; pModSample = nullptr; pModInstrument = nullptr; nCutOff = 0x7F; nResonance = 0; nFilterMode = FilterMode::LowPass; rightVol = leftVol = 0; newRightVol = newLeftVol = 0; rightRamp = leftRamp = 0; nVolume = 0; // Needs to be 0 for SMP_NODEFAULTVOLUME flag nVibratoPos = nTremoloPos = nPanbrelloPos = 0; nOldHiOffset = 0; nLeftVU = nRightVU = 0; // Custom tuning related m_ReCalculateFreqOnFirstTick = false; m_CalculateFreq = false; m_PortamentoFineSteps = 0; m_PortamentoTickSlide = 0; } if(resetMask & resetChannelSettings) { if(sourceChannel < MAX_BASECHANNELS) { dwFlags = sndFile.ChnSettings[sourceChannel].dwFlags; nPan = sndFile.ChnSettings[sourceChannel].nPan; nGlobalVol = sndFile.ChnSettings[sourceChannel].nVolume; if(dwFlags[CHN_MUTE]) { dwFlags.reset(CHN_MUTE); dwFlags.set(muteFlag); } } else { dwFlags.reset(); nPan = 128; nGlobalVol = 64; } nRestorePanOnNewNote = 0; nRestoreCutoffOnNewNote = 0; nRestoreResonanceOnNewNote = 0; } } void ModChannel::Stop() { nPeriod = 0; increment.Set(0); position.Set(0); nLeftVU = nRightVU = 0; nVolume = 0; pCurrentSample = nullptr; } void ModChannel::UpdateInstrumentVolume(const ModSample *smp, const ModInstrument *ins) { nInsVol = 64; if(smp != nullptr) nInsVol = smp->nGlobalVol; if(ins != nullptr) nInsVol = (nInsVol * ins->nGlobalVol) / 64; } ModCommand::NOTE ModChannel::GetPluginNote(bool realNoteMapping) const { if(nArpeggioLastNote != NOTE_NONE) { // If an arpeggio is playing, this definitely the last playing note, which may be different from the arpeggio base note stored in nNote. return nArpeggioLastNote; } ModCommand::NOTE plugNote = mpt::saturate_cast(nNote - nTranspose); // Caution: When in compatible mode, ModChannel::nNote stores the "real" note, not the mapped note! if(realNoteMapping && pModInstrument != nullptr && plugNote >= NOTE_MIN && plugNote < (std::size(pModInstrument->NoteMap) + NOTE_MIN)) { plugNote = pModInstrument->NoteMap[plugNote - NOTE_MIN]; } return plugNote; } void ModChannel::SetInstrumentPan(int32 pan, const CSoundFile &sndFile) { // IT compatibility: Instrument and sample panning does not override channel panning // Test case: PanResetInstr.it if(sndFile.m_playBehaviour[kITDoNotOverrideChannelPan]) { nRestorePanOnNewNote = static_cast(nPan + 1); if(dwFlags[CHN_SURROUND]) nRestorePanOnNewNote |= 0x8000; } nPan = pan; } void ModChannel::RecalcTuningFreq(Tuning::RATIOTYPE vibratoFactor, Tuning::NOTEINDEXTYPE arpeggioSteps, const CSoundFile &sndFile) { if(!HasCustomTuning()) return; ModCommand::NOTE note = ModCommand::IsNote(nNote) ? nNote : nLastNote; if(sndFile.m_playBehaviour[kITRealNoteMapping] && note >= NOTE_MIN && note <= NOTE_MAX) note = pModInstrument->NoteMap[note - NOTE_MIN]; nPeriod = mpt::saturate_round(nC5Speed * vibratoFactor * pModInstrument->pTuning->GetRatio(note - NOTE_MIDDLEC + arpeggioSteps, nFineTune + m_PortamentoFineSteps) * (1 << FREQ_FRACBITS)); } // IT command S73-S7E void ModChannel::InstrumentControl(uint8 param, const CSoundFile &sndFile) { param &= 0x0F; switch(param) { case 0x3: nNNA = NewNoteAction::NoteCut; break; case 0x4: nNNA = NewNoteAction::Continue; break; case 0x5: nNNA = NewNoteAction::NoteOff; break; case 0x6: nNNA = NewNoteAction::NoteFade; break; case 0x7: VolEnv.flags.reset(ENV_ENABLED); break; case 0x8: VolEnv.flags.set(ENV_ENABLED); break; case 0x9: PanEnv.flags.reset(ENV_ENABLED); break; case 0xA: PanEnv.flags.set(ENV_ENABLED); break; case 0xB: PitchEnv.flags.reset(ENV_ENABLED); break; case 0xC: PitchEnv.flags.set(ENV_ENABLED); break; case 0xD: // S7D: Enable pitch envelope, force to play as pitch envelope case 0xE: // S7E: Enable pitch envelope, force to play as filter envelope if(sndFile.GetType() == MOD_TYPE_MPT) { PitchEnv.flags.set(ENV_ENABLED); PitchEnv.flags.set(ENV_FILTER, param != 0xD); } break; } } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/ModChannel.h0000644000175000017500000002141114154475105020261 00000000000000/* * ModChannel.h * ------------ * Purpose: Module Channel header class and helpers * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "ModSample.h" #include "ModInstrument.h" #include "modcommand.h" #include "Paula.h" #include "tuningbase.h" OPENMPT_NAMESPACE_BEGIN class CSoundFile; // Mix Channel Struct struct ModChannel { // Envelope playback info struct EnvInfo { uint32 nEnvPosition = 0; int16 nEnvValueAtReleaseJump = NOT_YET_RELEASED; FlagSet flags; void Reset() { nEnvPosition = 0; nEnvValueAtReleaseJump = NOT_YET_RELEASED; } }; // Information used in the mixer (should be kept tight for better caching) SamplePosition position; // Current play position (fixed point) SamplePosition increment; // Sample speed relative to mixing frequency (fixed point) const void *pCurrentSample; // Currently playing sample (nullptr if no sample is playing) int32 leftVol; // 0...4096 (12 bits, since 16 bits + 12 bits = 28 bits = 0dB in integer mixer, see MIXING_ATTENUATION) int32 rightVol; // Ditto int32 leftRamp; // Ramping delta, 20.12 fixed point (see VOLUMERAMPPRECISION) int32 rightRamp; // Ditto int32 rampLeftVol; // Current ramping volume, 20.12 fixed point (see VOLUMERAMPPRECISION) int32 rampRightVol; // Ditto mixsample_t nFilter_Y[2][2]; // Filter memory - two history items per sample channel mixsample_t nFilter_A0, nFilter_B0, nFilter_B1; // Filter coeffs mixsample_t nFilter_HP; SmpLength nLength; SmpLength nLoopStart; SmpLength nLoopEnd; FlagSet dwFlags; mixsample_t nROfs, nLOfs; uint32 nRampLength; const ModSample *pModSample; // Currently assigned sample slot (may already be stopped) Paula::State paulaState; // Information not used in the mixer const ModInstrument *pModInstrument; // Currently assigned instrument slot SmpLength prevNoteOffset; // Offset for instrument-less notes for ProTracker/ScreamTracker SmpLength oldOffset; // Offset command memory FlagSet dwOldFlags; // Flags from previous tick int32 newLeftVol, newRightVol; int32 nRealVolume, nRealPan; int32 nVolume, nPan, nFadeOutVol; int32 nPeriod; // Frequency in Hz if CSoundFile::PeriodsAreFrequencies() or using custom tuning, 4x Amiga periods otherwise int32 nC5Speed, nPortamentoDest; int32 cachedPeriod, glissandoPeriod; int32 nCalcVolume; // Calculated channel volume, 14-Bit (without global volume, pre-amp etc applied) - for MIDI macros EnvInfo VolEnv, PanEnv, PitchEnv; // Envelope playback info int32 nGlobalVol; // Channel volume (CV in ITTECH.TXT) 0...64 int32 nInsVol; // Sample / Instrument volume (SV * IV in ITTECH.TXT) 0...64 int32 nAutoVibDepth; uint32 nEFxOffset; // Offset memory for Invert Loop (EFx, .MOD only) ROWINDEX nPatternLoop; uint16 portamentoSlide; int16 nTranspose; int16 nFineTune; int16 microTuning; // Micro-tuning / MIDI pitch wheel command int16 nVolSwing, nPanSwing; int16 nCutSwing, nResSwing; uint16 nRestorePanOnNewNote; // If > 0, nPan should be set to nRestorePanOnNewNote - 1 on new note. Used to recover from pan swing and IT sample / instrument panning. High bit set = surround CHANNELINDEX nMasterChn; ModCommand rowCommand; // 8-bit members ResamplingMode resamplingMode; uint8 nRestoreResonanceOnNewNote; // See nRestorePanOnNewNote uint8 nRestoreCutoffOnNewNote; // ditto uint8 nNote; NewNoteAction nNNA; uint8 nLastNote; // Last note, ignoring note offs and cuts - for MIDI macros uint8 nArpeggioLastNote, nArpeggioBaseNote; // For plugin arpeggio uint8 nNewNote, nNewIns, nOldIns, nCommand, nArpeggio; uint8 nRetrigParam, nRetrigCount; uint8 nOldVolumeSlide, nOldFineVolUpDown; uint8 nOldPortaUp, nOldPortaDown, nOldFinePortaUpDown, nOldExtraFinePortaUpDown; uint8 nOldPanSlide, nOldChnVolSlide; uint8 nOldGlobalVolSlide; uint8 nAutoVibPos, nVibratoPos, nTremoloPos, nPanbrelloPos; uint8 nVibratoType, nVibratoSpeed, nVibratoDepth; uint8 nTremoloType, nTremoloSpeed, nTremoloDepth; uint8 nPanbrelloType, nPanbrelloSpeed, nPanbrelloDepth; int8 nPanbrelloOffset, nPanbrelloRandomMemory; uint8 nOldCmdEx, nOldVolParam, nOldTempo; uint8 nOldHiOffset; uint8 nCutOff, nResonance; uint8 nTremorCount, nTremorParam; uint8 nPatternLoopCount; uint8 nLeftVU, nRightVU; uint8 nActiveMacro; FilterMode nFilterMode; uint8 nEFxSpeed, nEFxDelay; // memory for Invert Loop (EFx, .MOD only) uint8 noteSlideParam, noteSlideCounter; // IMF / PTM Note Slide uint8 lastZxxParam; // Memory for \xx slides bool isFirstTick : 1; // Execute tick-0 effects on this channel? (condition differs between formats due to Pattern Delay commands) bool triggerNote : 1; // Trigger note on this tick on this channel if there is one? bool isPreviewNote : 1; // Notes preview in editor bool isPaused : 1; // Don't mix or increment channel position, but keep the note alive bool portaTargetReached : 1; // Tone portamento is finished //-->Variables used to make user-definable tuning modes work with pattern effects. //If true, freq should be recalculated in ReadNote() on first tick. //Currently used only for vibrato things - using in other context might be //problematic. bool m_ReCalculateFreqOnFirstTick : 1; //To tell whether to calculate frequency. bool m_CalculateFreq : 1; int32 m_PortamentoFineSteps, m_PortamentoTickSlide; //NOTE_PCs memory. float m_plugParamValueStep, m_plugParamTargetValue; uint16 m_RowPlugParam; PLUGINDEX m_RowPlug; void ClearRowCmd() { rowCommand = ModCommand(); } // Get a reference to a specific envelope of this channel const EnvInfo &GetEnvelope(EnvelopeType envType) const { switch(envType) { case ENV_VOLUME: default: return VolEnv; case ENV_PANNING: return PanEnv; case ENV_PITCH: return PitchEnv; } } EnvInfo &GetEnvelope(EnvelopeType envType) { return const_cast(static_cast(this)->GetEnvelope(envType)); } void ResetEnvelopes() { VolEnv.Reset(); PanEnv.Reset(); PitchEnv.Reset(); } enum ResetFlags { resetChannelSettings = 1, // Reload initial channel settings resetSetPosBasic = 2, // Reset basic runtime channel attributes resetSetPosAdvanced = 4, // Reset more runtime channel attributes resetSetPosFull = resetSetPosBasic | resetSetPosAdvanced | resetChannelSettings, // Reset all runtime channel attributes resetTotal = resetSetPosFull, }; void Reset(ResetFlags resetMask, const CSoundFile &sndFile, CHANNELINDEX sourceChannel, ChannelFlags muteFlag); void Stop(); bool IsSamplePlaying() const noexcept { return !increment.IsZero(); } uint32 GetVSTVolume() const noexcept { return (pModInstrument) ? pModInstrument->nGlobalVol * 4 : nVolume; } ModCommand::NOTE GetPluginNote(bool realNoteMapping) const; // Check if the channel has a valid MIDI output. A return value of true implies that pModInstrument != nullptr. bool HasMIDIOutput() const noexcept { return pModInstrument != nullptr && pModInstrument->HasValidMIDIChannel(); } // Check if the channel uses custom tuning. A return value of true implies that pModInstrument != nullptr. bool HasCustomTuning() const noexcept { return pModInstrument != nullptr && pModInstrument->pTuning != nullptr; } // Check if currently processed loop is a sustain loop. pModSample is not checked for validity! bool InSustainLoop() const noexcept { return (dwFlags & (CHN_LOOP | CHN_KEYOFF)) == CHN_LOOP && pModSample->uFlags[CHN_SUSTAINLOOP]; } void UpdateInstrumentVolume(const ModSample *smp, const ModInstrument *ins); void SetInstrumentPan(int32 pan, const CSoundFile &sndFile); void RecalcTuningFreq(Tuning::RATIOTYPE vibratoFactor, Tuning::NOTEINDEXTYPE arpeggioSteps, const CSoundFile &sndFile); // IT command S73-S7E void InstrumentControl(uint8 param, const CSoundFile &sndFile); int32 GetMIDIPitchBend() const noexcept { return (static_cast(microTuning) + 0x8000) / 4; } }; // Default pattern channel settings struct ModChannelSettings { #ifdef MODPLUG_TRACKER static constexpr uint32 INVALID_COLOR = 0xFFFFFFFF; uint32 color = INVALID_COLOR; // For pattern editor #endif // MODPLUG_TRACKER FlagSet dwFlags; // Channel flags uint16 nPan = 128; // Initial pan (0...256) uint16 nVolume = 64; // Initial channel volume (0...64) PLUGINDEX nMixPlugin = 0; // Assigned plugin mpt::charbuf szName; // Channel name void Reset() { *this = {}; } }; OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/modcommand.cpp0000644000175000017500000007573214175042210020727 00000000000000/* * modcommand.cpp * -------------- * Purpose: Various functions for writing effects to patterns, converting ModCommands, etc. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Sndfile.h" #include "mod_specifications.h" #include "Tables.h" OPENMPT_NAMESPACE_BEGIN const EffectType effectTypes[] = { EFFECT_TYPE_NORMAL, EFFECT_TYPE_NORMAL, EFFECT_TYPE_PITCH, EFFECT_TYPE_PITCH, EFFECT_TYPE_PITCH, EFFECT_TYPE_PITCH, EFFECT_TYPE_VOLUME, EFFECT_TYPE_VOLUME, EFFECT_TYPE_VOLUME, EFFECT_TYPE_PANNING, EFFECT_TYPE_NORMAL, EFFECT_TYPE_VOLUME, EFFECT_TYPE_GLOBAL, EFFECT_TYPE_VOLUME, EFFECT_TYPE_GLOBAL, EFFECT_TYPE_NORMAL, EFFECT_TYPE_GLOBAL, EFFECT_TYPE_GLOBAL, EFFECT_TYPE_NORMAL, EFFECT_TYPE_NORMAL, EFFECT_TYPE_NORMAL, EFFECT_TYPE_VOLUME, EFFECT_TYPE_VOLUME, EFFECT_TYPE_GLOBAL, EFFECT_TYPE_GLOBAL, EFFECT_TYPE_NORMAL, EFFECT_TYPE_PITCH, EFFECT_TYPE_PANNING, EFFECT_TYPE_PITCH, EFFECT_TYPE_PANNING, EFFECT_TYPE_NORMAL, EFFECT_TYPE_NORMAL, EFFECT_TYPE_NORMAL, EFFECT_TYPE_NORMAL, EFFECT_TYPE_NORMAL, EFFECT_TYPE_PITCH, EFFECT_TYPE_PITCH, EFFECT_TYPE_NORMAL, EFFECT_TYPE_PITCH, EFFECT_TYPE_PITCH, EFFECT_TYPE_PITCH, EFFECT_TYPE_PITCH, EFFECT_TYPE_NORMAL, EFFECT_TYPE_NORMAL, EFFECT_TYPE_NORMAL, EFFECT_TYPE_NORMAL, }; static_assert(std::size(effectTypes) == MAX_EFFECTS); const EffectType volumeEffectTypes[] = { EFFECT_TYPE_NORMAL, EFFECT_TYPE_VOLUME, EFFECT_TYPE_PANNING, EFFECT_TYPE_VOLUME, EFFECT_TYPE_VOLUME, EFFECT_TYPE_VOLUME, EFFECT_TYPE_VOLUME, EFFECT_TYPE_PITCH, EFFECT_TYPE_PITCH, EFFECT_TYPE_PANNING, EFFECT_TYPE_PANNING, EFFECT_TYPE_PITCH, EFFECT_TYPE_PITCH, EFFECT_TYPE_PITCH, EFFECT_TYPE_NORMAL, EFFECT_TYPE_NORMAL, }; static_assert(std::size(volumeEffectTypes) == MAX_VOLCMDS); EffectType ModCommand::GetEffectType(COMMAND cmd) { if(cmd < std::size(effectTypes)) return effectTypes[cmd]; else return EFFECT_TYPE_NORMAL; } EffectType ModCommand::GetVolumeEffectType(VOLCMD volcmd) { if(volcmd < std::size(volumeEffectTypes)) return volumeEffectTypes[volcmd]; else return EFFECT_TYPE_NORMAL; } // Convert an Exx command (MOD) to Sxx command (S3M) void ModCommand::ExtendedMODtoS3MEffect() { if(command != CMD_MODCMDEX) return; command = CMD_S3MCMDEX; switch(param & 0xF0) { case 0x00: command = CMD_NONE; break; // No filter control 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 & 0x03) | 0x30; break; case 0x50: param = (param & 0x0F) | 0x20; break; case 0x60: param = (param & 0x0F) | 0xB0; break; case 0x70: param = (param & 0x03) | 0x40; break; case 0x90: command = CMD_RETRIG; param = (param & 0x0F); break; case 0xA0: if(param & 0x0F) { command = CMD_VOLUMESLIDE; param = (param << 4) | 0x0F; } else command = CMD_NONE; break; case 0xB0: if(param & 0x0F) { command = CMD_VOLUMESLIDE; param = 0xF0 | std::min(param, PARAM(0x0E)); } else command = CMD_NONE; break; case 0xC0: if(param == 0xC0) { command = CMD_NONE; note = NOTE_NOTECUT; } break; // this does different things in IT and ST3 case 0xD0: if(param == 0xD0) { command = CMD_NONE; } break; // ditto // rest are the same or handled elsewhere } } // Convert an Sxx command (S3M) to Exx command (MOD) void ModCommand::ExtendedS3MtoMODEffect() { if(command != CMD_S3MCMDEX) return; command = CMD_MODCMDEX; switch(param & 0xF0) { case 0x10: param = (param & 0x0F) | 0x30; break; case 0x20: param = (param & 0x0F) | 0x50; break; case 0x30: param = (param & 0x0F) | 0x40; break; case 0x40: param = (param & 0x0F) | 0x70; break; case 0x50: command = CMD_XFINEPORTAUPDOWN; break; // map to unused X5x case 0x60: command = CMD_XFINEPORTAUPDOWN; break; // map to unused X6x case 0x80: command = CMD_PANNING8; param = (param & 0x0F) * 0x11; break; // FT2 does actually not support E8x case 0x90: command = CMD_XFINEPORTAUPDOWN; break; // map to unused X9x case 0xA0: command = CMD_XFINEPORTAUPDOWN; break; // map to unused XAx case 0xB0: param = (param & 0x0F) | 0x60; break; case 0x70: command = CMD_NONE; break; // No NNA / envelope control in MOD/XM format // rest are the same or handled elsewhere } } // Convert a mod command from one format to another. void ModCommand::Convert(MODTYPE fromType, MODTYPE toType, const CSoundFile &sndFile) { if(fromType == toType) { return; } if(fromType == MOD_TYPE_MTM) { // Special MTM fixups. // Retrigger with param 0 if(command == CMD_MODCMDEX && param == 0x90) { command = CMD_NONE; } else if(command == CMD_VIBRATO) { // Vibrato is approximately half as deep compared to MOD/S3M. uint8 speed = (param & 0xF0); uint8 depth = (param & 0x0F) >> 1; param = speed | depth; } // Apart from these special fixups, do a regular conversion from MOD. fromType = MOD_TYPE_MOD; } if(command == CMD_DIGIREVERSESAMPLE && toType != MOD_TYPE_DIGI) { command = CMD_S3MCMDEX; param = 0x9F; } // helper variables const bool oldTypeIsMOD = (fromType == MOD_TYPE_MOD), oldTypeIsXM = (fromType == MOD_TYPE_XM), oldTypeIsS3M = (fromType == MOD_TYPE_S3M), oldTypeIsIT = (fromType == MOD_TYPE_IT), oldTypeIsMPT = (fromType == MOD_TYPE_MPT), oldTypeIsMOD_XM = (oldTypeIsMOD || oldTypeIsXM), oldTypeIsS3M_IT_MPT = (oldTypeIsS3M || oldTypeIsIT || oldTypeIsMPT), oldTypeIsIT_MPT = (oldTypeIsIT || oldTypeIsMPT); const bool newTypeIsMOD = (toType == MOD_TYPE_MOD), newTypeIsXM = (toType == MOD_TYPE_XM), newTypeIsS3M = (toType == MOD_TYPE_S3M), newTypeIsIT = (toType == MOD_TYPE_IT), newTypeIsMPT = (toType == MOD_TYPE_MPT), newTypeIsMOD_XM = (newTypeIsMOD || newTypeIsXM), newTypeIsS3M_IT_MPT = (newTypeIsS3M || newTypeIsIT || newTypeIsMPT), newTypeIsIT_MPT = (newTypeIsIT || newTypeIsMPT); const CModSpecifications &newSpecs = CSoundFile::GetModSpecifications(toType); ////////////////////////// // Convert 8-bit Panning if(command == CMD_PANNING8) { if(newTypeIsS3M) { param = (param + 1) >> 1; } else if(oldTypeIsS3M) { if(param == 0xA4) { // surround remap command = static_cast((toType & (MOD_TYPE_IT | MOD_TYPE_MPT)) ? CMD_S3MCMDEX : CMD_XFINEPORTAUPDOWN); param = 0x91; } else { param = mpt::saturate_cast(param * 2u); } } } // End if(command == CMD_PANNING8) // Re-map \xx to Zxx if the new format only knows the latter command. if(command == CMD_SMOOTHMIDI && !newSpecs.HasCommand(CMD_SMOOTHMIDI) && newSpecs.HasCommand(CMD_MIDI)) { command = CMD_MIDI; } /////////////////////////////////////////////////////////////////////////////////////// // MPTM to anything: Convert param control, extended envelope control, note delay+cut if(oldTypeIsMPT) { if(IsPcNote()) { COMMAND newCmd = static_cast(note == NOTE_PC ? CMD_MIDI : CMD_SMOOTHMIDI); if(!newSpecs.HasCommand(newCmd)) { newCmd = CMD_MIDI; // assuming that this was CMD_SMOOTHMIDI if(!newSpecs.HasCommand(newCmd)) { newCmd = CMD_NONE; } } param = static_cast(std::min(static_cast(maxColumnValue), GetValueEffectCol()) * 0x7F / maxColumnValue); command = newCmd; // might be removed later volcmd = VOLCMD_NONE; note = NOTE_NONE; instr = 0; } if((command == CMD_S3MCMDEX) && ((param & 0xF0) == 0x70) && ((param & 0x0F) > 0x0C)) { // Extended pitch envelope control commands param = 0x7C; } else if(command == CMD_DELAYCUT) { command = CMD_S3MCMDEX; // When converting to MOD/XM, this will be converted to CMD_MODCMDEX later param = 0xD0 | (param >> 4); // Preserve delay nibble } else if(command == CMD_FINETUNE || command == CMD_FINETUNE_SMOOTH) { // Convert finetune from +/-128th of a semitone to (extra-)fine portamento (assumes linear slides, plus we're missing the actual pitch wheel depth of the instrument) if(param < 0x80) { command = CMD_PORTAMENTODOWN; param = 0x80 - param; } else if(param > 0x80) { command = CMD_PORTAMENTOUP; param -= 0x80; } if(param <= 30) param = 0xE0 | ((param + 1u) / 2u); else param = 0xF0 | std::min(static_cast((param + 7u) / 8u), PARAM(15)); } } // End if(oldTypeIsMPT) ///////////////////////////////////////// // Convert MOD / XM to S3M / IT / MPTM if(oldTypeIsMOD_XM && newTypeIsS3M_IT_MPT) { switch(command) { case CMD_ARPEGGIO: if(!param) command = CMD_NONE; // 000 does nothing in MOD/XM break; case CMD_MODCMDEX: ExtendedMODtoS3MEffect(); break; case CMD_VOLUME: // Effect column volume command overrides the volume column in XM. if(volcmd == VOLCMD_NONE || volcmd == VOLCMD_VOLUME) { volcmd = VOLCMD_VOLUME; vol = param; if(vol > 64) vol = 64; command = CMD_NONE; param = 0; } else if(volcmd == VOLCMD_PANNING) { std::swap(vol, param); volcmd = VOLCMD_VOLUME; if(vol > 64) vol = 64; command = CMD_S3MCMDEX; param = 0x80 | (param / 4); // XM volcol panning is actually 4-Bit, so we can use 4-Bit panning here. } break; case CMD_PORTAMENTOUP: if(param > 0xDF) param = 0xDF; break; case CMD_PORTAMENTODOWN: if(param > 0xDF) param = 0xDF; break; case CMD_XFINEPORTAUPDOWN: switch(param & 0xF0) { case 0x10: command = CMD_PORTAMENTOUP; param = (param & 0x0F) | 0xE0; break; case 0x20: command = CMD_PORTAMENTODOWN; param = (param & 0x0F) | 0xE0; break; case 0x50: case 0x60: case 0x70: case 0x90: case 0xA0: command = CMD_S3MCMDEX; // Surround remap (this is the "official" command) if(toType & MOD_TYPE_S3M && param == 0x91) { command = CMD_PANNING8; param = 0xA4; } break; } break; case CMD_KEYOFF: if(note == NOTE_NONE) { note = newTypeIsS3M ? NOTE_NOTECUT : NOTE_KEYOFF; command = CMD_S3MCMDEX; if(param == 0) instr = 0; param = 0xD0 | (param & 0x0F); } break; case CMD_PANNINGSLIDE: // swap L/R, convert to fine slide if(param & 0xF0) { param = 0xF0 | std::min(PARAM(0x0E), static_cast(param >> 4)); } else { param = 0x0F | (std::min(PARAM(0x0E), static_cast(param & 0x0F)) << 4); } break; default: break; } } // End if(oldTypeIsMOD_XM && newTypeIsS3M_IT_MPT) ///////////////////////////////////////// // Convert S3M / IT / MPTM to MOD / XM else if(oldTypeIsS3M_IT_MPT && newTypeIsMOD_XM) { if(note == NOTE_NOTECUT) { // convert note cut to C00 if possible or volume command otherwise (MOD/XM has no real way of cutting notes that cannot be "undone" by volume commands) note = NOTE_NONE; if(command == CMD_NONE || !newTypeIsXM) { command = CMD_VOLUME; param = 0; } else { volcmd = VOLCMD_VOLUME; vol = 0; } } else if(note == NOTE_FADE) { // convert note fade to note off note = NOTE_KEYOFF; } switch(command) { case CMD_S3MCMDEX: ExtendedS3MtoMODEffect(); break; case CMD_TONEPORTAVOL: // Can't do fine slides and portamento/vibrato at the same time :( case CMD_VIBRATOVOL: // ditto if(volcmd == VOLCMD_NONE && (((param & 0xF0) && ((param & 0x0F) == 0x0F)) || ((param & 0x0F) && ((param & 0xF0) == 0xF0)))) { // Try to salvage portamento/vibrato if(command == CMD_TONEPORTAVOL) volcmd = VOLCMD_TONEPORTAMENTO; else if(command == CMD_VIBRATOVOL) volcmd = VOLCMD_VIBRATODEPTH; vol = 0; } [[fallthrough]]; case CMD_VOLUMESLIDE: if((param & 0xF0) && ((param & 0x0F) == 0x0F)) { command = CMD_MODCMDEX; param = (param >> 4) | 0xA0; } else if((param & 0x0F) && ((param & 0xF0) == 0xF0)) { command = CMD_MODCMDEX; param = (param & 0x0F) | 0xB0; } break; case CMD_PORTAMENTOUP: if(param >= 0xF0) { command = CMD_MODCMDEX; param = (param & 0x0F) | 0x10; } else if(param >= 0xE0) { if(newTypeIsXM) { command = CMD_XFINEPORTAUPDOWN; param = 0x10 | (param & 0x0F); } else { command = CMD_MODCMDEX; param = (((param & 0x0F) + 3) >> 2) | 0x10; } } else { command = CMD_PORTAMENTOUP; } break; case CMD_PORTAMENTODOWN: if(param >= 0xF0) { command = CMD_MODCMDEX; param = (param & 0x0F) | 0x20; } else if(param >= 0xE0) { if(newTypeIsXM) { command = CMD_XFINEPORTAUPDOWN; param = 0x20 | (param & 0x0F); } else { command = CMD_MODCMDEX; param = (((param & 0x0F) + 3) >> 2) | 0x20; } } else { command = CMD_PORTAMENTODOWN; } break; case CMD_TEMPO: if(param < 0x20) command = CMD_NONE; // no tempo slides break; case CMD_PANNINGSLIDE: // swap L/R, convert fine slides to normal slides if((param & 0x0F) == 0x0F && (param & 0xF0)) { param = (param >> 4); } else if((param & 0xF0) == 0xF0 && (param & 0x0F)) { param = (param & 0x0F) << 4; } else if(param & 0x0F) { param = 0xF0; } else if(param & 0xF0) { param = 0x0F; } else { param = 0; } break; case CMD_RETRIG: // Retrig: Q0y doesn't change volume in IT/S3M, but R0y in XM takes the last x parameter if(param != 0 && (param & 0xF0) == 0) { param |= 0x80; } break; default: break; } } // End if(oldTypeIsS3M_IT_MPT && newTypeIsMOD_XM) /////////////////////// // Convert IT to S3M else if(oldTypeIsIT_MPT && newTypeIsS3M) { if(note == NOTE_KEYOFF || note == NOTE_FADE) note = NOTE_NOTECUT; switch(command) { case CMD_S3MCMDEX: switch(param & 0xF0) { case 0x70: command = CMD_NONE; break; // No NNA / envelope control in S3M format case 0x90: if(param == 0x91) { // surround remap (this is the "official" command) command = CMD_PANNING8; param = 0xA4; } else if(param == 0x90) { command = CMD_PANNING8; param = 0x40; } break; } break; case CMD_GLOBALVOLUME: param = (std::min(PARAM(0x80), param) + 1) / 2u; break; default: break; } } // End if(oldTypeIsIT_MPT && newTypeIsS3M) ////////////////////// // Convert IT to XM if(oldTypeIsIT_MPT && newTypeIsXM) { switch(command) { case CMD_VIBRATO: // With linear slides, strength is roughly doubled. param = (param & 0xF0) | (((param & 0x0F) + 1) / 2u); break; case CMD_GLOBALVOLUME: param = (std::min(PARAM(0x80), param) + 1) / 2u; break; } } // End if(oldTypeIsIT_MPT && newTypeIsXM) ////////////////////// // Convert XM to IT if(oldTypeIsXM && newTypeIsIT_MPT) { switch(command) { case CMD_VIBRATO: // With linear slides, strength is roughly halved. param = (param & 0xF0) | std::min(static_cast((param & 0x0F) * 2u), PARAM(15)); break; case CMD_GLOBALVOLUME: param = std::min(PARAM(0x40), param) * 2u; break; } } // End if(oldTypeIsIT_MPT && newTypeIsXM) /////////////////////////////////// // MOD / XM Speed/Tempo limits if(newTypeIsMOD_XM) { switch(command) { case CMD_SPEED: param = std::min(param, PARAM(0x1F)); break; break; case CMD_TEMPO: param = std::max(param, PARAM(0x20)); break; } } /////////////////////////////////////////////////////////////////////// // Convert MOD to anything - adjust effect memory, remove Invert Loop if(oldTypeIsMOD) { switch(command) { case CMD_TONEPORTAVOL: // lacks memory -> 500 is the same as 300 if(param == 0x00) command = CMD_TONEPORTAMENTO; break; case CMD_VIBRATOVOL: // lacks memory -> 600 is the same as 400 if(param == 0x00) command = CMD_VIBRATO; break; case CMD_MODCMDEX: // This would turn into "Set Active Macro", so let's better remove it case CMD_S3MCMDEX: if((param & 0xF0) == 0xF0) command = CMD_NONE; break; } } // End if(oldTypeIsMOD && newTypeIsXM) ///////////////////////////////////////////////////////////////////// // Convert anything to MOD - remove volume column, remove Set Macro if(newTypeIsMOD) { // convert note off events if(IsSpecialNote()) { note = NOTE_NONE; // no effect present, so just convert note off to volume 0 if(command == CMD_NONE) { command = CMD_VOLUME; param = 0; // EDx effect present, so convert it to ECx } else if((command == CMD_MODCMDEX) && ((param & 0xF0) == 0xD0)) { param = 0xC0 | (param & 0x0F); } } if(command != CMD_NONE) switch(command) { case CMD_RETRIG: // MOD only has E9x command = CMD_MODCMDEX; param = 0x90 | (param & 0x0F); break; case CMD_MODCMDEX: // This would turn into "Invert Loop", so let's better remove it if((param & 0xF0) == 0xF0) command = CMD_NONE; break; } if(command == CMD_NONE) switch(volcmd) { case VOLCMD_VOLUME: command = CMD_VOLUME; param = vol; break; case VOLCMD_PANNING: command = CMD_PANNING8; param = vol < 64 ? vol << 2 : 255; break; case VOLCMD_VOLSLIDEDOWN: command = CMD_VOLUMESLIDE; param = vol; break; case VOLCMD_VOLSLIDEUP: command = CMD_VOLUMESLIDE; param = vol << 4; break; case VOLCMD_FINEVOLDOWN: command = CMD_MODCMDEX; param = 0xB0 | vol; break; case VOLCMD_FINEVOLUP: command = CMD_MODCMDEX; param = 0xA0 | vol; break; case VOLCMD_PORTADOWN: command = CMD_PORTAMENTODOWN; param = vol << 2; break; case VOLCMD_PORTAUP: command = CMD_PORTAMENTOUP; param = vol << 2; break; case VOLCMD_TONEPORTAMENTO: command = CMD_TONEPORTAMENTO; param = vol << 2; break; case VOLCMD_VIBRATODEPTH: command = CMD_VIBRATO; param = vol; break; case VOLCMD_VIBRATOSPEED: command = CMD_VIBRATO; param = vol << 4; break; } volcmd = VOLCMD_NONE; } // End if(newTypeIsMOD) /////////////////////////////////////////////////// // Convert anything to S3M - adjust volume column if(newTypeIsS3M) { if(command == CMD_NONE) switch(volcmd) { case VOLCMD_VOLSLIDEDOWN: command = CMD_VOLUMESLIDE; param = vol; volcmd = VOLCMD_NONE; break; case VOLCMD_VOLSLIDEUP: command = CMD_VOLUMESLIDE; param = vol << 4; volcmd = VOLCMD_NONE; break; case VOLCMD_FINEVOLDOWN: command = CMD_VOLUMESLIDE; param = 0xF0 | vol; volcmd = VOLCMD_NONE; break; case VOLCMD_FINEVOLUP: command = CMD_VOLUMESLIDE; param = (vol << 4) | 0x0F; volcmd = VOLCMD_NONE; break; case VOLCMD_PORTADOWN: command = CMD_PORTAMENTODOWN; param = vol << 2; volcmd = VOLCMD_NONE; break; case VOLCMD_PORTAUP: command = CMD_PORTAMENTOUP; param = vol << 2; volcmd = VOLCMD_NONE; break; case VOLCMD_TONEPORTAMENTO: command = CMD_TONEPORTAMENTO; param = vol << 2; volcmd = VOLCMD_NONE; break; case VOLCMD_VIBRATODEPTH: command = CMD_VIBRATO; param = vol; volcmd = VOLCMD_NONE; break; case VOLCMD_VIBRATOSPEED: command = CMD_VIBRATO; param = vol << 4; volcmd = VOLCMD_NONE; break; case VOLCMD_PANSLIDELEFT: command = CMD_PANNINGSLIDE; param = vol << 4; volcmd = VOLCMD_NONE; break; case VOLCMD_PANSLIDERIGHT: command = CMD_PANNINGSLIDE; param = vol; volcmd = VOLCMD_NONE; break; } } // End if(newTypeIsS3M) //////////////////////////////////////////////////////////////////////// // Convert anything to XM - adjust volume column, breaking EDx command if(newTypeIsXM) { // remove EDx if no note is next to it, or it will retrigger the note in FT2 mode if(command == CMD_MODCMDEX && (param & 0xF0) == 0xD0 && note == NOTE_NONE) { command = CMD_NONE; param = 0; } if(IsSpecialNote()) { // Instrument numbers next to Note Off reset instrument settings instr = 0; if(command == CMD_MODCMDEX && (param & 0xF0) == 0xD0) { // Note Off + Note Delay does nothing when using envelopes. note = NOTE_NONE; command = CMD_KEYOFF; param &= 0x0F; } } // Convert some commands which behave differently or don't exist if(command == CMD_NONE) switch(volcmd) { case VOLCMD_PORTADOWN: command = CMD_PORTAMENTODOWN; param = vol << 2; volcmd = VOLCMD_NONE; break; case VOLCMD_PORTAUP: command = CMD_PORTAMENTOUP; param = vol << 2; volcmd = VOLCMD_NONE; break; case VOLCMD_TONEPORTAMENTO: command = CMD_TONEPORTAMENTO; param = ImpulseTrackerPortaVolCmd[vol & 0x0F]; volcmd = VOLCMD_NONE; break; } } // End if(newTypeIsXM) /////////////////////////////////////////////////// // Convert anything to IT - adjust volume column if(newTypeIsIT_MPT) { // Convert some commands which behave differently or don't exist if(!oldTypeIsIT_MPT && command == CMD_NONE) switch(volcmd) { case VOLCMD_PANSLIDELEFT: command = CMD_PANNINGSLIDE; param = vol << 4; volcmd = VOLCMD_NONE; break; case VOLCMD_PANSLIDERIGHT: command = CMD_PANNINGSLIDE; param = vol; volcmd = VOLCMD_NONE; break; case VOLCMD_VIBRATOSPEED: command = CMD_VIBRATO; param = vol << 4; volcmd = VOLCMD_NONE; break; case VOLCMD_TONEPORTAMENTO: command = CMD_TONEPORTAMENTO; param = vol << 4; volcmd = VOLCMD_NONE; break; } switch(volcmd) { case VOLCMD_VOLSLIDEDOWN: case VOLCMD_VOLSLIDEUP: case VOLCMD_FINEVOLDOWN: case VOLCMD_FINEVOLUP: case VOLCMD_PORTADOWN: case VOLCMD_PORTAUP: case VOLCMD_TONEPORTAMENTO: case VOLCMD_VIBRATODEPTH: // OpenMPT-specific commands case VOLCMD_OFFSET: vol = std::min(vol, VOL(9)); break; } } // End if(newTypeIsIT_MPT) // Fix volume column offset for formats that don't have it. if(volcmd == VOLCMD_OFFSET && !newSpecs.HasVolCommand(VOLCMD_OFFSET) && (command == CMD_NONE || command == CMD_OFFSET || !newSpecs.HasCommand(command))) { const ModCommand::PARAM oldOffset = (command == CMD_OFFSET) ? param : 0; command = CMD_OFFSET; volcmd = VOLCMD_NONE; SAMPLEINDEX smp = instr; if(smp > 0 && smp <= sndFile.GetNumInstruments() && IsNote() && sndFile.Instruments[smp] != nullptr) smp = sndFile.Instruments[smp]->Keyboard[note - NOTE_MIN]; if(smp > 0 && smp <= sndFile.GetNumSamples() && vol <= std::size(ModSample().cues)) { const ModSample &sample = sndFile.GetSample(smp); if(vol == 0) param = mpt::saturate_cast(Util::muldivr_unsigned(sample.nLength, oldOffset, 65536u)); else param = mpt::saturate_cast((sample.cues[vol - 1] + (oldOffset * 256u) + 128u) / 256u); } else { param = vol << 3; } } if((command == CMD_REVERSEOFFSET || command == CMD_OFFSETPERCENTAGE) && !newSpecs.HasCommand(command)) { command = CMD_OFFSET; } if(!newSpecs.HasNote(note)) note = NOTE_NONE; // ensure the commands really exist in this format if(!newSpecs.HasCommand(command)) command = CMD_NONE; if(!newSpecs.HasVolCommand(volcmd)) volcmd = VOLCMD_NONE; } bool ModCommand::IsContinousCommand(const CSoundFile &sndFile) const { switch(command) { case CMD_ARPEGGIO: case CMD_TONEPORTAMENTO: case CMD_VIBRATO: case CMD_TREMOLO: case CMD_RETRIG: case CMD_TREMOR: case CMD_FINEVIBRATO: case CMD_PANBRELLO: case CMD_SMOOTHMIDI: case CMD_NOTESLIDEUP: case CMD_NOTESLIDEDOWN: case CMD_NOTESLIDEUPRETRIG: case CMD_NOTESLIDEDOWNRETRIG: return true; case CMD_PORTAMENTOUP: case CMD_PORTAMENTODOWN: if(!param && sndFile.GetType() == MOD_TYPE_MOD) return false; if(sndFile.GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MED | MOD_TYPE_AMF0 | MOD_TYPE_DIGI | MOD_TYPE_STP | MOD_TYPE_DTM)) return true; if(param >= 0xF0) return false; if(param >= 0xE0 && sndFile.GetType() != MOD_TYPE_DBM) return false; return true; case CMD_VOLUMESLIDE: case CMD_TONEPORTAVOL: case CMD_VIBRATOVOL: case CMD_GLOBALVOLSLIDE: case CMD_CHANNELVOLSLIDE: case CMD_PANNINGSLIDE: if(!param && sndFile.GetType() == MOD_TYPE_MOD) return false; if(sndFile.GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM | MOD_TYPE_AMF0 | MOD_TYPE_MED | MOD_TYPE_DIGI)) return true; if((param & 0xF0) == 0xF0 && (param & 0x0F)) return false; if((param & 0x0F) == 0x0F && (param & 0xF0)) return false; return true; case CMD_TEMPO: return (param < 0x20); default: return false; } } bool ModCommand::IsContinousVolColCommand() const { switch(volcmd) { case VOLCMD_VOLSLIDEUP: case VOLCMD_VOLSLIDEDOWN: case VOLCMD_VIBRATOSPEED: case VOLCMD_VIBRATODEPTH: case VOLCMD_PANSLIDELEFT: case VOLCMD_PANSLIDERIGHT: case VOLCMD_TONEPORTAMENTO: case VOLCMD_PORTAUP: case VOLCMD_PORTADOWN: return true; default: return false; } } bool ModCommand::IsSlideUpDownCommand() const { switch(command) { case CMD_VOLUMESLIDE: case CMD_TONEPORTAVOL: case CMD_VIBRATOVOL: case CMD_GLOBALVOLSLIDE: case CMD_CHANNELVOLSLIDE: case CMD_PANNINGSLIDE: return true; default: return false; } } bool ModCommand::IsGlobalCommand(COMMAND command, PARAM param) { switch(command) { case CMD_POSITIONJUMP: case CMD_PATTERNBREAK: case CMD_SPEED: case CMD_TEMPO: case CMD_GLOBALVOLUME: case CMD_GLOBALVOLSLIDE: case CMD_MIDI: case CMD_SMOOTHMIDI: case CMD_DBMECHO: return true; case CMD_MODCMDEX: switch(param & 0xF0) { case 0x00: // LED Filter case 0x60: // Pattern Loop case 0xE0: // Row Delay return true; default: return false; } case CMD_XFINEPORTAUPDOWN: case CMD_S3MCMDEX: switch(param & 0xF0) { case 0x60: // Tick Delay case 0x90: // Sound Control case 0xB0: // Pattern Loop case 0xE0: // Row Delay return true; default: return false; } default: return false; } } // "Importance" of every FX command. Table is used for importing from formats with multiple effect colums // and is approximately the same as in SchismTracker. size_t ModCommand::GetEffectWeight(COMMAND cmd) { // Effect weights, sorted from lowest to highest weight. static constexpr COMMAND weights[] = { CMD_NONE, CMD_DUMMY, CMD_XPARAM, CMD_SETENVPOSITION, CMD_KEYOFF, CMD_TREMOLO, CMD_FINEVIBRATO, CMD_VIBRATO, CMD_XFINEPORTAUPDOWN, CMD_FINETUNE, CMD_FINETUNE_SMOOTH, CMD_PANBRELLO, CMD_S3MCMDEX, CMD_MODCMDEX, CMD_DELAYCUT, CMD_MIDI, CMD_SMOOTHMIDI, CMD_PANNINGSLIDE, CMD_PANNING8, CMD_NOTESLIDEUPRETRIG, CMD_NOTESLIDEUP, CMD_NOTESLIDEDOWNRETRIG, CMD_NOTESLIDEDOWN, CMD_PORTAMENTOUP, CMD_PORTAMENTODOWN, CMD_VOLUMESLIDE, CMD_VIBRATOVOL, CMD_VOLUME, CMD_DIGIREVERSESAMPLE, CMD_REVERSEOFFSET, CMD_OFFSETPERCENTAGE, CMD_OFFSET, CMD_TREMOR, CMD_RETRIG, CMD_ARPEGGIO, CMD_TONEPORTAMENTO, CMD_TONEPORTAVOL, CMD_DBMECHO, CMD_GLOBALVOLSLIDE, CMD_CHANNELVOLUME, CMD_GLOBALVOLSLIDE, CMD_GLOBALVOLUME, CMD_TEMPO, CMD_SPEED, CMD_POSITIONJUMP, CMD_PATTERNBREAK, }; static_assert(std::size(weights) == MAX_EFFECTS); for(size_t i = 0; i < std::size(weights); i++) { if(weights[i] == cmd) { return i; } } // Invalid / unknown command. return 0; } // Try to convert a fx column command (&effect) into a volume column command. // Returns true if successful. // Some commands can only be converted by losing some precision. // If moving the command into the volume column is more important than accuracy, use force = true. // (Code translated from SchismTracker and mainly supposed to be used with loaders ported from this tracker) bool ModCommand::ConvertVolEffect(uint8 &effect, uint8 ¶m, bool force) { switch(effect) { case CMD_NONE: effect = VOLCMD_NONE; return true; case CMD_VOLUME: effect = VOLCMD_VOLUME; param = std::min(param, PARAM(64)); break; case CMD_PORTAMENTOUP: // if not force, reject when dividing causes loss of data in LSB, or if the final value is too // large to fit. (volume column Ex/Fx are four times stronger than effect column) if(!force && ((param & 3) || param >= 0xE0)) return false; param /= 4; effect = VOLCMD_PORTAUP; break; case CMD_PORTAMENTODOWN: if(!force && ((param & 3) || param >= 0xE0)) return false; param /= 4; effect = VOLCMD_PORTADOWN; break; case CMD_TONEPORTAMENTO: if(param >= 0xF0) { // hack for people who can't type F twice :) effect = VOLCMD_TONEPORTAMENTO; param = 9; return true; } for(uint8 n = 0; n < 10; n++) { if(force ? (param <= ImpulseTrackerPortaVolCmd[n]) : (param == ImpulseTrackerPortaVolCmd[n])) { effect = VOLCMD_TONEPORTAMENTO; param = n; return true; } } return false; case CMD_VIBRATO: if(force) param = std::min(static_cast(param & 0x0F), PARAM(9)); else if((param & 0x0F) > 9 || (param & 0xF0) != 0) return false; param &= 0x0F; effect = VOLCMD_VIBRATODEPTH; break; case CMD_FINEVIBRATO: if(force) param = 0; else if(param) return false; effect = VOLCMD_VIBRATODEPTH; break; case CMD_PANNING8: if(param == 255) param = 64; else param /= 4; effect = VOLCMD_PANNING; break; case CMD_VOLUMESLIDE: if(param == 0) return false; if((param & 0xF) == 0) // Dx0 / Cx { param >>= 4; effect = VOLCMD_VOLSLIDEUP; } else if((param & 0xF0) == 0) // D0x / Dx { effect = VOLCMD_VOLSLIDEDOWN; } else if((param & 0xF) == 0xF) // DxF / Ax { param >>= 4; effect = VOLCMD_FINEVOLUP; } else if((param & 0xF0) == 0xF0) // DFx / Bx { param &= 0xF; effect = VOLCMD_FINEVOLDOWN; } else // ??? { return false; } break; case CMD_S3MCMDEX: switch (param >> 4) { case 8: effect = VOLCMD_PANNING; param = ((param & 0xF) << 2) + 2; return true; case 0: case 1: case 2: case 0xF: if(force) { effect = param = 0; return true; } break; default: break; } return false; default: return false; } return true; } // Try to combine two commands into one. Returns true on success and the combined command is placed in eff1 / param1. bool ModCommand::CombineEffects(uint8 &eff1, uint8 ¶m1, uint8 &eff2, uint8 ¶m2) { if(eff1 == CMD_VOLUMESLIDE && (eff2 == CMD_VIBRATO || eff2 == CMD_TONEPORTAVOL) && param2 == 0) { // Merge commands if(eff2 == CMD_VIBRATO) { eff1 = CMD_VIBRATOVOL; } else { eff1 = CMD_TONEPORTAVOL; } eff2 = CMD_NONE; return true; } else if(eff2 == CMD_VOLUMESLIDE && (eff1 == CMD_VIBRATO || eff1 == CMD_TONEPORTAVOL) && param1 == 0) { // Merge commands if(eff1 == CMD_VIBRATO) { eff1 = CMD_VIBRATOVOL; } else { eff1 = CMD_TONEPORTAVOL; } param1 = param2; eff2 = CMD_NONE; return true; } else if(eff1 == CMD_OFFSET && eff2 == CMD_S3MCMDEX && param2 == 0x9F) { // Reverse offset eff1 = CMD_REVERSEOFFSET; eff2 = CMD_NONE; return true; } else if(eff1 == CMD_S3MCMDEX && param1 == 0x9F && eff2 == CMD_OFFSET) { // Reverse offset eff1 = CMD_REVERSEOFFSET; param1 = param2; eff2 = CMD_NONE; return true; } else { return false; } } std::pair ModCommand::TwoRegularCommandsToMPT(uint8 &effect1, uint8 ¶m1, uint8 &effect2, uint8 ¶m2) { for(uint8 n = 0; n < 4; n++) { if(ModCommand::ConvertVolEffect(effect1, param1, (n > 1))) { return {CMD_NONE, ModCommand::PARAM(0)}; } std::swap(effect1, effect2); std::swap(param1, param2); } // Can only keep one command :( if(GetEffectWeight(static_cast(effect1)) > GetEffectWeight(static_cast(effect2))) { std::swap(effect1, effect2); std::swap(param1, param2); } std::pair lostCommand = {static_cast(effect1), param1}; effect1 = VOLCMD_NONE; param1 = 0; return lostCommand; } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/modcommand.h0000644000175000017500000002167214163335272020400 00000000000000/* * modcommand.h * ------------ * Purpose: ModCommand declarations and helpers. One ModCommand corresponds to one pattern cell. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "Snd_defs.h" #include "../common/misc_util.h" OPENMPT_NAMESPACE_BEGIN class CSoundFile; // Note definitions enum : uint8 // ModCommand::NOTE { NOTE_NONE = 0, // Empty note cell NOTE_MIN = 1, // Minimum note value NOTE_MAX = 120, // Maximum note value NOTE_MIDDLEC = (5 * 12 + NOTE_MIN), NOTE_KEYOFF = 0xFF, // === (Note Off, releases envelope / fades samples, stops plugin note) NOTE_NOTECUT = 0xFE, // ^^^ (Cuts sample / stops all plugin notes) NOTE_FADE = 0xFD, // ~~~ (Fades samples, stops plugin note) NOTE_PC = 0xFC, // Param Control 'note'. Changes param value on first tick. NOTE_PCS = 0xFB, // Param Control (Smooth) 'note'. Interpolates param value during the whole row. NOTE_MIN_SPECIAL = NOTE_PCS, NOTE_MAX_SPECIAL = NOTE_KEYOFF, }; // Volume Column commands enum VolumeCommand : uint8 { VOLCMD_NONE = 0, VOLCMD_VOLUME = 1, VOLCMD_PANNING = 2, VOLCMD_VOLSLIDEUP = 3, VOLCMD_VOLSLIDEDOWN = 4, VOLCMD_FINEVOLUP = 5, VOLCMD_FINEVOLDOWN = 6, VOLCMD_VIBRATOSPEED = 7, VOLCMD_VIBRATODEPTH = 8, VOLCMD_PANSLIDELEFT = 9, VOLCMD_PANSLIDERIGHT = 10, VOLCMD_TONEPORTAMENTO = 11, VOLCMD_PORTAUP = 12, VOLCMD_PORTADOWN = 13, VOLCMD_PLAYCONTROL = 14, VOLCMD_OFFSET = 15, MAX_VOLCMDS }; // Effect column commands enum EffectCommand : uint8 { CMD_NONE = 0, CMD_ARPEGGIO = 1, CMD_PORTAMENTOUP = 2, CMD_PORTAMENTODOWN = 3, CMD_TONEPORTAMENTO = 4, CMD_VIBRATO = 5, CMD_TONEPORTAVOL = 6, CMD_VIBRATOVOL = 7, CMD_TREMOLO = 8, CMD_PANNING8 = 9, CMD_OFFSET = 10, CMD_VOLUMESLIDE = 11, CMD_POSITIONJUMP = 12, CMD_VOLUME = 13, CMD_PATTERNBREAK = 14, CMD_RETRIG = 15, CMD_SPEED = 16, CMD_TEMPO = 17, CMD_TREMOR = 18, CMD_MODCMDEX = 19, CMD_S3MCMDEX = 20, CMD_CHANNELVOLUME = 21, CMD_CHANNELVOLSLIDE = 22, CMD_GLOBALVOLUME = 23, CMD_GLOBALVOLSLIDE = 24, CMD_KEYOFF = 25, CMD_FINEVIBRATO = 26, CMD_PANBRELLO = 27, CMD_XFINEPORTAUPDOWN = 28, CMD_PANNINGSLIDE = 29, CMD_SETENVPOSITION = 30, CMD_MIDI = 31, CMD_SMOOTHMIDI = 32, CMD_DELAYCUT = 33, CMD_XPARAM = 34, CMD_FINETUNE = 35, CMD_FINETUNE_SMOOTH = 36, CMD_DUMMY = 37, CMD_NOTESLIDEUP = 38, // IMF Gxy / PTM Jxy (Slide y notes up every x ticks) CMD_NOTESLIDEDOWN = 39, // IMF Hxy / PTM Kxy (Slide y notes down every x ticks) CMD_NOTESLIDEUPRETRIG = 40, // PTM Lxy (Slide y notes up every x ticks + retrigger note) CMD_NOTESLIDEDOWNRETRIG = 41, // PTM Mxy (Slide y notes down every x ticks + retrigger note) CMD_REVERSEOFFSET = 42, // PTM Nxx Revert sample + offset CMD_DBMECHO = 43, // DBM enable/disable echo CMD_OFFSETPERCENTAGE = 44, // PLM Percentage Offset CMD_DIGIREVERSESAMPLE = 45, // DIGI reverse sample MAX_EFFECTS }; enum EffectType : uint8 { EFFECT_TYPE_NORMAL = 0, EFFECT_TYPE_GLOBAL = 1, EFFECT_TYPE_VOLUME = 2, EFFECT_TYPE_PANNING = 3, EFFECT_TYPE_PITCH = 4, MAX_EFFECT_TYPE = 5 }; class ModCommand { public: using NOTE = uint8; using INSTR = uint8; using VOL = uint8; using VOLCMD = uint8; using COMMAND = uint8; using PARAM = uint8; // Defines the maximum value for column data when interpreted as 2-byte value // (for example volcmd and vol). The valid value range is [0, maxColumnValue]. static constexpr int maxColumnValue = 999; // Returns empty modcommand. static ModCommand Empty() { return ModCommand(); } bool operator==(const ModCommand &mc) const { return (note == mc.note) && (instr == mc.instr) && (volcmd == mc.volcmd) && (command == mc.command) && ((volcmd == VOLCMD_NONE && !IsPcNote()) || vol == mc.vol) && ((command == CMD_NONE && !IsPcNote()) || param == mc.param); } bool operator!=(const ModCommand& mc) const { return !(*this == mc); } void Set(NOTE n, INSTR ins, uint16 volcol, uint16 effectcol) { note = n; instr = ins; SetValueVolCol(volcol); SetValueEffectCol(effectcol); } uint16 GetValueVolCol() const { return GetValueVolCol(volcmd, vol); } static uint16 GetValueVolCol(uint8 volcmd, uint8 vol) { return (volcmd << 8) + vol; } void SetValueVolCol(const uint16 val) { volcmd = static_cast(val >> 8); vol = static_cast(val & 0xFF); } uint16 GetValueEffectCol() const { return GetValueEffectCol(command, param); } static uint16 GetValueEffectCol(uint8 command, uint8 param) { return (command << 8) + param; } void SetValueEffectCol(const uint16 val) { command = static_cast(val >> 8); param = static_cast(val & 0xFF); } // Clears modcommand. void Clear() { memset(this, 0, sizeof(ModCommand)); } // Returns true if modcommand is empty, false otherwise. bool IsEmpty() const { return (note == NOTE_NONE && instr == 0 && volcmd == VOLCMD_NONE && command == CMD_NONE); } // Returns true if instrument column represents plugin index. bool IsInstrPlug() const { return IsPcNote(); } // Returns true if and only if note is NOTE_PC or NOTE_PCS. bool IsPcNote() const { return IsPcNote(note); } static bool IsPcNote(NOTE note) { return note == NOTE_PC || note == NOTE_PCS; } // Returns true if and only if note is a valid musical note. bool IsNote() const { return mpt::is_in_range(note, NOTE_MIN, NOTE_MAX); } static bool IsNote(NOTE note) { return mpt::is_in_range(note, NOTE_MIN, NOTE_MAX); } // Returns true if and only if note is a valid special note. bool IsSpecialNote() const { return mpt::is_in_range(note, NOTE_MIN_SPECIAL, NOTE_MAX_SPECIAL); } static bool IsSpecialNote(NOTE note) { return mpt::is_in_range(note, NOTE_MIN_SPECIAL, NOTE_MAX_SPECIAL); } // Returns true if and only if note is a valid musical note or the note entry is empty. bool IsNoteOrEmpty() const { return note == NOTE_NONE || IsNote(); } static bool IsNoteOrEmpty(NOTE note) { return note == NOTE_NONE || IsNote(note); } // Returns true if any of the commands in this cell trigger a tone portamento. bool IsPortamento() const { return command == CMD_TONEPORTAMENTO || command == CMD_TONEPORTAVOL || volcmd == VOLCMD_TONEPORTAMENTO; } // Returns true if the cell contains a sliding or otherwise continuous effect command. bool IsContinousCommand(const CSoundFile &sndFile) const; bool IsContinousVolColCommand() const; // Returns true if the cell contains a sliding command with separate up/down nibbles. bool IsSlideUpDownCommand() const; // Returns true if the cell contains an effect command that may affect the global state of the module. bool IsGlobalCommand() const { return IsGlobalCommand(command, param); } static bool IsGlobalCommand(COMMAND command, PARAM param); // Returns true if the note is inside the Amiga frequency range bool IsAmigaNote() const { return IsAmigaNote(note); } static bool IsAmigaNote(NOTE note) { return !IsNote(note) || (note >= NOTE_MIDDLEC - 12 && note < NOTE_MIDDLEC + 24); } static EffectType GetEffectType(COMMAND cmd); EffectType GetEffectType() const { return GetEffectType(command); } static EffectType GetVolumeEffectType(VOLCMD volcmd); EffectType GetVolumeEffectType() const { return GetVolumeEffectType(volcmd); } // Convert a complete ModCommand item from one format to another void Convert(MODTYPE fromType, MODTYPE toType, const CSoundFile &sndFile); // Convert MOD/XM Exx to S3M/IT Sxx void ExtendedMODtoS3MEffect(); // Convert S3M/IT Sxx to MOD/XM Exx void ExtendedS3MtoMODEffect(); // "Importance" of every FX command. Table is used for importing from formats with multiple effect columns // and is approximately the same as in SchismTracker. static size_t GetEffectWeight(COMMAND cmd); // Try to convert a an effect into a volume column effect. Returns true on success. static bool ConvertVolEffect(uint8 &effect, uint8 ¶m, bool force); // Takes two "normal" effect commands and converts them to volume column + effect column commands. Returns the dropped command + param (CMD_NONE if nothing had to be dropped). static std::pair TwoRegularCommandsToMPT(uint8 &effect1, uint8 ¶m1, uint8 &effect2, uint8 ¶m2); // Try to combine two commands into one. Returns true on success and the combined command is placed in eff1 / param1. static bool CombineEffects(uint8 &eff1, uint8 ¶m1, uint8 &eff2, uint8 ¶m2); public: uint8 note = NOTE_NONE; uint8 instr = 0; uint8 volcmd = VOLCMD_NONE; uint8 command = CMD_NONE; uint8 vol = 0; uint8 param = 0; }; OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/ModInstrument.cpp0000644000175000017500000002036214175042045021414 00000000000000/* * ModInstrument.cpp * ----------------- * Purpose: Helper functions for Module Instrument handling * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Sndfile.h" #include "ModInstrument.h" OPENMPT_NAMESPACE_BEGIN // Convert envelope data between various formats. void InstrumentEnvelope::Convert(MODTYPE fromType, MODTYPE toType) { if(!(fromType & MOD_TYPE_XM) && (toType & MOD_TYPE_XM)) { // IT / MPTM -> XM: Expand loop by one tick, convert sustain loops to sustain points, remove carry flag. nSustainStart = nSustainEnd; dwFlags.reset(ENV_CARRY); if(nLoopEnd > nLoopStart && dwFlags[ENV_LOOP]) { for(uint32 node = nLoopEnd; node < size(); node++) { at(node).tick++; } } } else if((fromType & MOD_TYPE_XM) && !(toType & MOD_TYPE_XM)) { if(nSustainStart > nLoopEnd && dwFlags[ENV_LOOP]) { // In the IT format, the sustain loop is always considered before the envelope loop. // In the XM format, whichever of the two is encountered first is considered. // So we have to disable the sustain loop if it was behind the normal loop. dwFlags.reset(ENV_SUSTAIN); } // XM -> IT / MPTM: Shorten loop by one tick by inserting bogus point if(nLoopEnd > nLoopStart && dwFlags[ENV_LOOP] && nLoopEnd < size()) { if(at(nLoopEnd).tick - 1 > at(nLoopEnd - 1).tick) { // Insert an interpolated point just before the loop point. EnvelopeNode::tick_t tick = at(nLoopEnd).tick - 1u; auto interpolatedValue = static_cast(GetValueFromPosition(tick, 64)); insert(begin() + nLoopEnd, EnvelopeNode(tick, interpolatedValue)); } else { // There is already a point before the loop point: Use it as new loop end. nLoopEnd--; } } } if(toType != MOD_TYPE_MPT) { nReleaseNode = ENV_RELEASE_NODE_UNSET; } } // Get envelope value at a given tick. Assumes that the envelope data is in rage [0, rangeIn], // returns value in range [0, rangeOut]. int32 InstrumentEnvelope::GetValueFromPosition(int position, int32 rangeOut, int32 rangeIn) const { uint32 pt = size() - 1u; const int32 ENV_PRECISION = 1 << 16; // Checking where current 'tick' is relative to the envelope points. for(uint32 i = 0; i < size() - 1u; i++) { if (position <= at(i).tick) { pt = i; break; } } int x2 = at(pt).tick; int32 value = 0; if(position >= x2) { // Case: current 'tick' is on a envelope point. value = at(pt).value * ENV_PRECISION / rangeIn; } else { // Case: current 'tick' is between two envelope points. int x1 = 0; if(pt) { // Get previous node's value and tick. value = at(pt - 1).value * ENV_PRECISION / rangeIn; x1 = at(pt - 1).tick; } if(x2 > x1 && position > x1) { // Linear approximation between the points; // f(x + d) ~ f(x) + f'(x) * d, where f'(x) = (y2 - y1) / (x2 - x1) value += Util::muldiv(position - x1, (at(pt).value * ENV_PRECISION / rangeIn - value), x2 - x1); } } Limit(value, int32(0), ENV_PRECISION); return (value * rangeOut + ENV_PRECISION / 2) / ENV_PRECISION; } void InstrumentEnvelope::Sanitize(uint8 maxValue) { if(!empty()) { front().tick = 0; LimitMax(front().value, maxValue); for(iterator it = begin() + 1; it != end(); it++) { it->tick = std::max(it->tick, (it - 1)->tick); LimitMax(it->value, maxValue); } } LimitMax(nLoopEnd, static_cast(size() - 1)); LimitMax(nLoopStart, nLoopEnd); LimitMax(nSustainEnd, static_cast(size() - 1)); LimitMax(nSustainStart, nSustainEnd); if(nReleaseNode != ENV_RELEASE_NODE_UNSET) LimitMax(nReleaseNode, static_cast(size() - 1)); } ModInstrument::ModInstrument(SAMPLEINDEX sample) { SetCutoff(0, false); SetResonance(0, false); pitchToTempoLock.Set(0); pTuning = CSoundFile::GetDefaultTuning(); AssignSample(sample); ResetNoteMap(); } // Translate instrument properties between two given formats. void ModInstrument::Convert(MODTYPE fromType, MODTYPE toType) { MPT_UNREFERENCED_PARAMETER(fromType); if(toType & MOD_TYPE_XM) { ResetNoteMap(); PitchEnv.dwFlags.reset(ENV_ENABLED | ENV_FILTER); dwFlags.reset(INS_SETPANNING); SetCutoff(GetCutoff(), false); SetResonance(GetResonance(), false); filterMode = FilterMode::Unchanged; nCutSwing = nPanSwing = nResSwing = nVolSwing = 0; nPPC = NOTE_MIDDLEC - 1; nPPS = 0; nNNA = NewNoteAction::NoteCut; nDCT = DuplicateCheckType::None; nDNA = DuplicateNoteAction::NoteCut; if(nMidiChannel == MidiMappedChannel) { nMidiChannel = MidiFirstChannel; } // FT2 only has unsigned Pitch Wheel Depth, and it's limited to 0...36 (in the GUI, at least. As you would expect it from FT2, this value is actually not sanitized on load). midiPWD = static_cast(std::abs(midiPWD)); Limit(midiPWD, int8(0), int8(36)); nGlobalVol = 64; nPan = 128; LimitMax(nFadeOut, 32767u); } VolEnv.Convert(fromType, toType); PanEnv.Convert(fromType, toType); PitchEnv.Convert(fromType, toType); if(fromType == MOD_TYPE_XM && (toType & (MOD_TYPE_IT | MOD_TYPE_MPT))) { if(!VolEnv.dwFlags[ENV_ENABLED]) { // Note-Off with no envelope cuts the note immediately in XM VolEnv.resize(2); VolEnv[0].tick = 0; VolEnv[0].value = ENVELOPE_MAX; VolEnv[1].tick = 1; VolEnv[1].value = ENVELOPE_MIN; VolEnv.dwFlags.set(ENV_ENABLED | ENV_SUSTAIN); VolEnv.dwFlags.reset(ENV_LOOP); VolEnv.nSustainStart = VolEnv.nSustainEnd = 0; } } // Limit fadeout length for IT if(toType & MOD_TYPE_IT) { LimitMax(nFadeOut, 8192u); } // MPT-specific features - remove instrument tunings, Pitch/Tempo Lock, cutoff / resonance swing and filter mode for other formats if(!(toType & MOD_TYPE_MPT)) { SetTuning(nullptr); pitchToTempoLock.Set(0); nCutSwing = nResSwing = 0; filterMode = FilterMode::Unchanged; nVolRampUp = 0; } } // Get a set of all samples referenced by this instrument std::set ModInstrument::GetSamples() const { std::set referencedSamples; for(const auto sample : Keyboard) { if(sample) { referencedSamples.insert(sample); } } return referencedSamples; } // Write sample references into a bool vector. If a sample is referenced by this instrument, true is written. // The caller has to initialize the vector. void ModInstrument::GetSamples(std::vector &referencedSamples) const { for(const auto sample : Keyboard) { if(sample != 0 && sample < referencedSamples.size()) { referencedSamples[sample] = true; } } } void ModInstrument::Sanitize(MODTYPE modType) { LimitMax(nFadeOut, 65536u); LimitMax(nGlobalVol, 64u); LimitMax(nPan, 256u); LimitMax(wMidiBank, uint16(16384)); LimitMax(nMidiProgram, uint8(128)); LimitMax(nMidiChannel, uint8(17)); if(nNNA > NewNoteAction::NoteFade) nNNA = NewNoteAction::NoteCut; if(nDCT > DuplicateCheckType::Plugin) nDCT = DuplicateCheckType::None; if(nDNA > DuplicateNoteAction::NoteFade) nDNA = DuplicateNoteAction::NoteCut; LimitMax(nPanSwing, uint8(64)); LimitMax(nVolSwing, uint8(100)); Limit(nPPS, int8(-32), int8(32)); LimitMax(nCutSwing, uint8(64)); LimitMax(nResSwing, uint8(64)); #ifdef MODPLUG_TRACKER MPT_UNREFERENCED_PARAMETER(modType); const uint8 range = ENVELOPE_MAX; #else const uint8 range = modType == MOD_TYPE_AMS ? uint8_max : ENVELOPE_MAX; #endif VolEnv.Sanitize(); PanEnv.Sanitize(); PitchEnv.Sanitize(range); for(size_t i = 0; i < std::size(NoteMap); i++) { if(NoteMap[i] < NOTE_MIN || NoteMap[i] > NOTE_MAX) NoteMap[i] = static_cast(i + NOTE_MIN); } if(!Resampling::IsKnownMode(resampling)) resampling = SRCMODE_DEFAULT; if(nMixPlug > MAX_MIXPLUGINS) nMixPlug = 0; } void ModInstrument::Transpose(int8 amount) { for(auto ¬e : NoteMap) { note = static_cast(Clamp(note + amount, NOTE_MIN, NOTE_MAX)); } } uint8 ModInstrument::GetMIDIChannel(const ModChannel &channel, CHANNELINDEX chn) const { // For mapped channels, return their pattern channel, modulo 16 (because there are only 16 MIDI channels) if(nMidiChannel == MidiMappedChannel) return static_cast((channel.nMasterChn ? (channel.nMasterChn - 1u) : chn) % 16u); else if(HasValidMIDIChannel()) return (nMidiChannel - MidiFirstChannel) % 16u; else return 0; } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/ModInstrument.h0000644000175000017500000001650414175042045021064 00000000000000/* * ModInstrument.h * --------------- * Purpose: Module Instrument header class and helpers * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "modcommand.h" #include "tuningbase.h" #include "Snd_defs.h" #include "openmpt/base/FlagSet.hpp" #include "../common/misc_util.h" #include OPENMPT_NAMESPACE_BEGIN struct ModChannel; // Instrument Nodes struct EnvelopeNode { using tick_t = uint16 ; using value_t = uint8; tick_t tick = 0; // Envelope node position (x axis) value_t value = 0; // Envelope node value (y axis) EnvelopeNode() { } EnvelopeNode(tick_t tick, value_t value) : tick(tick), value(value) { } bool operator== (const EnvelopeNode &other) const { return tick == other.tick && value == other.value; } }; // Instrument Envelopes struct InstrumentEnvelope : public std::vector { FlagSet dwFlags; // Envelope flags uint8 nLoopStart = 0; // Loop start node uint8 nLoopEnd = 0; // Loop end node uint8 nSustainStart = 0; // Sustain start node uint8 nSustainEnd = 0; // Sustain end node uint8 nReleaseNode = ENV_RELEASE_NODE_UNSET; // Release node // Convert envelope data between various formats. void Convert(MODTYPE fromType, MODTYPE toType); // Get envelope value at a given tick. Assumes that the envelope data is in rage [0, rangeIn], // returns value in range [0, rangeOut]. int32 GetValueFromPosition(int position, int32 rangeOut, int32 rangeIn = ENVELOPE_MAX) const; // Ensure that ticks are ordered in increasing order and values are within the allowed range. void Sanitize(uint8 maxValue = ENVELOPE_MAX); uint32 size() const { return static_cast(std::vector::size()); } using std::vector::push_back; void push_back(EnvelopeNode::tick_t tick, EnvelopeNode::value_t value) { emplace_back(tick, value); } }; // Instrument Struct struct ModInstrument { uint32 nFadeOut = 256; // Instrument fadeout speed uint32 nGlobalVol = 64; // Global volume (0...64, all sample volumes are multiplied with this - TODO: This is 0...128 in Impulse Tracker) uint32 nPan = 32 * 4; // Default pan (0...256), if the appropriate flag is set. Sample panning overrides instrument panning. uint16 nVolRampUp = 0; // Default sample ramping up, 0 = use global default ResamplingMode resampling = SRCMODE_DEFAULT; // Resampling mode FlagSet dwFlags; // Instrument flags NewNoteAction nNNA = NewNoteAction::NoteCut; // New note action DuplicateCheckType nDCT = DuplicateCheckType::None; // Duplicate check type (i.e. which condition will trigger the duplicate note action) DuplicateNoteAction nDNA = DuplicateNoteAction::NoteCut; // Duplicate note action uint8 nPanSwing = 0; // Random panning factor (0...64) uint8 nVolSwing = 0; // Random volume factor (0...100) uint8 nIFC = 0; // Default filter cutoff (0...127). Used if the high bit is set uint8 nIFR = 0; // Default filter resonance (0...127). Used if the high bit is set uint8 nCutSwing = 0; // Random cutoff factor (0...64) uint8 nResSwing = 0; // Random resonance factor (0...64) FilterMode filterMode = FilterMode::Unchanged; // Default filter mode int8 nPPS = 0; // Pitch/Pan separation (i.e. how wide the panning spreads, -32...32) uint8 nPPC = NOTE_MIDDLEC - NOTE_MIN; // Pitch/Pan centre (zero-based) uint16 wMidiBank = 0; // MIDI Bank (1...16384). 0 = Don't send. uint8 nMidiProgram = 0; // MIDI Program (1...128). 0 = Don't send. uint8 nMidiChannel = 0; // MIDI Channel (1...16). 0 = Don't send. 17 = Mapped (Send to tracker channel modulo 16). uint8 nMidiDrumKey = 0; // Drum set note mapping (currently only used by the .MID loader) int8 midiPWD = 2; // MIDI Pitch Wheel Depth and CMD_FINETUNE depth in semitones PLUGINDEX nMixPlug = 0; // Plugin assigned to this instrument (0 = no plugin, 1 = first plugin) PlugVelocityHandling pluginVelocityHandling = PLUGIN_VELOCITYHANDLING_CHANNEL; // How to deal with plugin velocity PlugVolumeHandling pluginVolumeHandling = PLUGIN_VOLUMEHANDLING_IGNORE; // How to deal with plugin volume TEMPO pitchToTempoLock; // BPM at which the samples assigned to this instrument loop correctly (0 = unset) CTuning *pTuning = nullptr; // sample tuning assigned to this instrument InstrumentEnvelope VolEnv; // Volume envelope data InstrumentEnvelope PanEnv; // Panning envelope data InstrumentEnvelope PitchEnv; // Pitch / filter envelope data std::array NoteMap; // Note mapping, e.g. C-5 => D-5 std::array Keyboard; // Sample mapping, e.g. C-5 => Sample 1 mpt::charbuf name; mpt::charbuf filename; std::string GetName() const { return name; } std::string GetFilename() const { return filename; } // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // WHEN adding new members here, ALSO update InstrumentExtensions.cpp // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ModInstrument(SAMPLEINDEX sample = 0); // Assign all notes to a given sample. void AssignSample(SAMPLEINDEX sample) { Keyboard.fill(sample); } // Reset note mapping (i.e. every note is mapped to itself) void ResetNoteMap() { for(size_t n = 0; n < std::size(NoteMap); n++) { NoteMap[n] = static_cast(n + 1); } } // Transpose entire note mapping by given number of semitones void Transpose(int8 amount); bool IsCutoffEnabled() const { return (nIFC & 0x80) != 0; } bool IsResonanceEnabled() const { return (nIFR & 0x80) != 0; } uint8 GetCutoff() const { return (nIFC & 0x7F); } uint8 GetResonance() const { return (nIFR & 0x7F); } void SetCutoff(uint8 cutoff, bool enable) { nIFC = std::min(cutoff, uint8(0x7F)) | (enable ? 0x80 : 0x00); } void SetResonance(uint8 resonance, bool enable) { nIFR = std::min(resonance, uint8(0x7F)) | (enable ? 0x80 : 0x00); } bool HasValidMIDIChannel() const { return (nMidiChannel >= 1 && nMidiChannel <= 17); } uint8 GetMIDIChannel(const ModChannel &channel, CHANNELINDEX chn) const; void SetTuning(CTuning *pT) { pTuning = pT; } // Get a reference to a specific envelope of this instrument const InstrumentEnvelope &GetEnvelope(EnvelopeType envType) const { switch(envType) { case ENV_VOLUME: default: return VolEnv; case ENV_PANNING: return PanEnv; case ENV_PITCH: return PitchEnv; } } InstrumentEnvelope &GetEnvelope(EnvelopeType envType) { return const_cast(static_cast(*this).GetEnvelope(envType)); } // Get a set of all samples referenced by this instrument std::set GetSamples() const; // Write sample references into a bool vector. If a sample is referenced by this instrument, true is written. // The caller has to initialize the vector. void GetSamples(std::vector &referencedSamples) const; // Translate instrument properties between two given formats. void Convert(MODTYPE fromType, MODTYPE toType); // Sanitize all instrument data. void Sanitize(MODTYPE modType); }; OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/ModSample.cpp0000644000175000017500000003551414175523206020475 00000000000000/* * ModSample.h * ----------- * Purpose: Module Sample header class and helpers * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Sndfile.h" #include "ModSample.h" #include "modsmp_ctrl.h" #include "mpt/base/numbers.hpp" #include OPENMPT_NAMESPACE_BEGIN // Translate sample properties between two given formats. void ModSample::Convert(MODTYPE fromType, MODTYPE toType) { // Convert between frequency and transpose values if necessary. if((!(toType & (MOD_TYPE_MOD | MOD_TYPE_XM))) && (fromType & (MOD_TYPE_MOD | MOD_TYPE_XM))) { TransposeToFrequency(); RelativeTone = 0; nFineTune = 0; // TransposeToFrequency assumes NTSC middle-C frequency like FT2, but we play MODs with PAL middle-C! if(fromType == MOD_TYPE_MOD) nC5Speed = Util::muldivr_unsigned(nC5Speed, 8272, 8363); } else if((toType & (MOD_TYPE_MOD | MOD_TYPE_XM)) && (!(fromType & (MOD_TYPE_MOD | MOD_TYPE_XM)))) { // FrequencyToTranspose assumes NTSC middle-C frequency like FT2, but we play MODs with PAL middle-C! if(toType == MOD_TYPE_MOD) nC5Speed = Util::muldivr_unsigned(nC5Speed, 8363, 8272); FrequencyToTranspose(); } // No ping-pong loop, panning and auto-vibrato for MOD / S3M samples if(toType & (MOD_TYPE_MOD | MOD_TYPE_S3M)) { uFlags.reset(CHN_PINGPONGLOOP | CHN_PANNING); nVibDepth = 0; nVibRate = 0; nVibSweep = 0; nVibType = VIB_SINE; RelativeTone = 0; } // No global volume / sustain loops for MOD/S3M/XM if(toType & (MOD_TYPE_MOD | MOD_TYPE_XM | MOD_TYPE_S3M)) { nGlobalVol = 64; // Sustain loops - convert to normal loops if(uFlags[CHN_SUSTAINLOOP]) { // We probably overwrite a normal loop here, but since sustain loops are evaluated before normal loops, this is just correct. nLoopStart = nSustainStart; nLoopEnd = nSustainEnd; uFlags.set(CHN_LOOP); uFlags.set(CHN_PINGPONGLOOP, uFlags[CHN_PINGPONGSUSTAIN]); } nSustainStart = nSustainEnd = 0; uFlags.reset(CHN_SUSTAINLOOP | CHN_PINGPONGSUSTAIN); } // All XM samples have default panning, and XM's autovibrato settings are rather limited. if(toType & MOD_TYPE_XM) { if(!uFlags[CHN_PANNING]) { uFlags.set(CHN_PANNING); nPan = 128; } LimitMax(nVibDepth, uint8(15)); LimitMax(nVibRate, uint8(63)); } // Autovibrato sweep setting is inverse in XM (0 = "no sweep") and IT (0 = "no vibrato") if(((fromType & MOD_TYPE_XM) && (toType & (MOD_TYPE_IT | MOD_TYPE_MPT))) || ((toType & MOD_TYPE_XM) && (fromType & (MOD_TYPE_IT | MOD_TYPE_MPT)))) { if(nVibRate != 0 && nVibDepth != 0) { if(nVibSweep != 0) nVibSweep = mpt::saturate_cast(Util::muldivr_unsigned(nVibDepth, 256, nVibSweep)); else nVibSweep = 255; } } // Convert incompatible autovibrato types if(toType == MOD_TYPE_IT && nVibType == VIB_RAMP_UP) { nVibType = VIB_RAMP_DOWN; } else if(toType == MOD_TYPE_XM && nVibType == VIB_RANDOM) { nVibType = VIB_SINE; } // No external samples in formats other than MPTM. if(toType != MOD_TYPE_MPT) { uFlags.reset(SMP_KEEPONDISK); } // No Adlib instruments in formats that can't handle it. if(!CSoundFile::SupportsOPL(toType) && uFlags[CHN_ADLIB]) { SetAdlib(false); } else if(toType == MOD_TYPE_S3M && uFlags[CHN_ADLIB]) { // No support for OPL3 waveforms in S3M adlib[8] &= 0x03; adlib[9] &= 0x03; } } // Initialize sample slot with default values. void ModSample::Initialize(MODTYPE type) { FreeSample(); nLength = 0; nLoopStart = nLoopEnd = 0; nSustainStart = nSustainEnd = 0; nC5Speed = 8363; nPan = 128; nVolume = 256; nGlobalVol = 64; uFlags.reset(CHN_PANNING | CHN_SUSTAINLOOP | CHN_LOOP | CHN_PINGPONGLOOP | CHN_PINGPONGSUSTAIN | CHN_ADLIB | SMP_MODIFIED | SMP_KEEPONDISK); if(type == MOD_TYPE_XM) { uFlags.set(CHN_PANNING); } RelativeTone = 0; nFineTune = 0; nVibType = VIB_SINE; nVibSweep = 0; nVibDepth = 0; nVibRate = 0; rootNote = 0; filename = ""; RemoveAllCuePoints(); } // Returns sample rate of the sample. uint32 ModSample::GetSampleRate(const MODTYPE type) const { uint32 rate; if(CSoundFile::UseFinetuneAndTranspose(type)) rate = TransposeToFrequency(RelativeTone, nFineTune); else rate = nC5Speed; // TransposeToFrequency assumes NTSC middle-C frequency like FT2, but we play MODs with PAL middle-C! if(type == MOD_TYPE_MOD) rate = Util::muldivr_unsigned(rate, 8272, 8363); return (rate > 0) ? rate : 8363; } // Copies sample data from another sample slot and ensures that the 16-bit/stereo flags are set accordingly. bool ModSample::CopyWaveform(const ModSample &smpFrom) { if(!smpFrom.HasSampleData()) return false; // If we duplicate a sample slot, avoid deleting the sample we just copy from if(smpFrom.sampleb() == sampleb()) pData.pSample = nullptr; LimitMax(nLength, smpFrom.nLength); uFlags.set(CHN_16BIT, smpFrom.uFlags[CHN_16BIT]); uFlags.set(CHN_STEREO, smpFrom.uFlags[CHN_STEREO]); if(AllocateSample()) { memcpy(sampleb(), smpFrom.sampleb(), GetSampleSizeInBytes()); return true; } return false; } // Allocate sample based on a ModSample's properties. // Returns number of bytes allocated, 0 on failure. size_t ModSample::AllocateSample() { FreeSample(); if((pData.pSample = AllocateSample(nLength, GetBytesPerSample())) == nullptr) { return 0; } else { return GetSampleSizeInBytes(); } } // Allocate sample memory. On success, a pointer to the silenced sample buffer is returned. On failure, nullptr is returned. // numFrames must contain the sample length, bytesPerSample the size of a sampling point multiplied with the number of channels. void *ModSample::AllocateSample(SmpLength numFrames, size_t bytesPerSample) { const size_t allocSize = GetRealSampleBufferSize(numFrames, bytesPerSample); if(allocSize != 0) { char *p = new(std::nothrow) char[allocSize]; if(p != nullptr) { memset(p, 0, allocSize); return p + (InterpolationLookaheadBufferSize * MaxSamplingPointSize); } } return nullptr; } // Compute sample buffer size in bytes, including any overhead introduced by pre-computed loops and such. Returns 0 if sample is too big. size_t ModSample::GetRealSampleBufferSize(SmpLength numSamples, size_t bytesPerSample) { // Number of required lookahead samples: // * 1x InterpolationMaxLookahead samples before the actual sample start. This is set to MaxSamplingPointSize due to the way AllocateSample/FreeSample currently work. // * 1x InterpolationMaxLookahead samples of silence after the sample end (if normal loop end == sample end, this can be optimized out). // * 2x InterpolationMaxLookahead before the loop point (because we start at InterpolationMaxLookahead before the loop point and will look backwards from there as well) // * 2x InterpolationMaxLookahead after the loop point (for wrap-around) // * 4x InterpolationMaxLookahead for the sustain loop (same as the two points above) const SmpLength maxSize = Util::MaxValueOfType(numSamples); const SmpLength lookaheadBufferSize = (MaxSamplingPointSize + 1 + 4 + 4) * InterpolationLookaheadBufferSize; if(numSamples == 0 || numSamples > MAX_SAMPLE_LENGTH || lookaheadBufferSize > maxSize - numSamples) { return 0; } numSamples += lookaheadBufferSize; if(maxSize / bytesPerSample < numSamples) { return 0; } return numSamples * bytesPerSample; } void ModSample::FreeSample() { FreeSample(pData.pSample); pData.pSample = nullptr; } void ModSample::FreeSample(void *samplePtr) { if(samplePtr) { delete[](((char *)samplePtr) - (InterpolationLookaheadBufferSize * MaxSamplingPointSize)); } } // Set loop points and update loop wrap-around buffer void ModSample::SetLoop(SmpLength start, SmpLength end, bool enable, bool pingpong, CSoundFile &sndFile) { nLoopStart = start; nLoopEnd = end; LimitMax(nLoopEnd, nLength); if(nLoopStart < nLoopEnd) { uFlags.set(CHN_LOOP, enable); uFlags.set(CHN_PINGPONGLOOP, pingpong && enable); } else { nLoopStart = nLoopEnd = 0; uFlags.reset(CHN_LOOP | CHN_PINGPONGLOOP); } PrecomputeLoops(sndFile, true); } // Set sustain loop points and update loop wrap-around buffer void ModSample::SetSustainLoop(SmpLength start, SmpLength end, bool enable, bool pingpong, CSoundFile &sndFile) { nSustainStart = start; nSustainEnd = end; LimitMax(nLoopEnd, nLength); if(nSustainStart < nSustainEnd) { uFlags.set(CHN_SUSTAINLOOP, enable); uFlags.set(CHN_PINGPONGSUSTAIN, pingpong && enable); } else { nSustainStart = nSustainEnd = 0; uFlags.reset(CHN_SUSTAINLOOP | CHN_PINGPONGSUSTAIN); } PrecomputeLoops(sndFile, true); } namespace // Unnamed namespace for local implementation functions. { template class PrecomputeLoop { protected: T *target; const T *sampleData; SmpLength loopEnd; int numChannels; bool pingpong; bool ITPingPongMode; public: PrecomputeLoop(T *target, const T *sampleData, SmpLength loopEnd, int numChannels, bool pingpong, bool ITPingPongMode) : target(target), sampleData(sampleData), loopEnd(loopEnd), numChannels(numChannels), pingpong(pingpong), ITPingPongMode(ITPingPongMode) { if(loopEnd > 0) { CopyLoop(true); CopyLoop(false); } } void CopyLoop(bool direction) const { // Direction: true = start reading and writing forward, false = start reading and writing backward (write direction never changes) const int numSamples = 2 * InterpolationLookaheadBufferSize + (direction ? 1 : 0); // Loop point is included in forward loop expansion T *dest = target + numChannels * (2 * InterpolationLookaheadBufferSize - 1); // Write buffer offset SmpLength readPosition = loopEnd - 1; const int writeIncrement = direction ? 1 : -1; int readIncrement = writeIncrement; for(int i = 0; i < numSamples; i++) { // Copy sample over to lookahead buffer for(int c = 0; c < numChannels; c++) { dest[c] = sampleData[readPosition * numChannels + c]; } dest += writeIncrement * numChannels; if(readPosition == loopEnd - 1 && readIncrement > 0) { // Reached end of loop while going forward if(pingpong) { readIncrement = -1; if(ITPingPongMode && readPosition > 0) { readPosition--; } } else { readPosition = 0; } } else if(readPosition == 0 && readIncrement < 0) { // Reached start of loop while going backward if(pingpong) { readIncrement = 1; } else { readPosition = loopEnd - 1; } } else { readPosition += readIncrement; } } } }; template void PrecomputeLoopsImpl(ModSample &smp, const CSoundFile &sndFile) { const int numChannels = smp.GetNumChannels(); const int copySamples = numChannels * InterpolationLookaheadBufferSize; T *sampleData = static_cast(smp.samplev()); T *afterSampleStart = sampleData + smp.nLength * numChannels; T *loopLookAheadStart = afterSampleStart + copySamples; T *sustainLookAheadStart = loopLookAheadStart + 4 * copySamples; // Hold sample on the same level as the last sampling point at the end to prevent extra pops with interpolation. // Do the same at the sample start, too. for(int i = 0; i < (int)InterpolationLookaheadBufferSize; i++) { for(int c = 0; c < numChannels; c++) { afterSampleStart[i * numChannels + c] = afterSampleStart[-numChannels + c]; sampleData[-(i + 1) * numChannels + c] = sampleData[c]; } } if(smp.uFlags[CHN_LOOP]) { PrecomputeLoop(loopLookAheadStart, sampleData + smp.nLoopStart * numChannels, smp.nLoopEnd - smp.nLoopStart, numChannels, smp.uFlags[CHN_PINGPONGLOOP], sndFile.m_playBehaviour[kITPingPongMode]); } if(smp.uFlags[CHN_SUSTAINLOOP]) { PrecomputeLoop(sustainLookAheadStart, sampleData + smp.nSustainStart * numChannels, smp.nSustainEnd - smp.nSustainStart, numChannels, smp.uFlags[CHN_PINGPONGSUSTAIN], sndFile.m_playBehaviour[kITPingPongMode]); } } } // unnamed namespace void ModSample::PrecomputeLoops(CSoundFile &sndFile, bool updateChannels) { if(!HasSampleData()) return; SanitizeLoops(); // Update channels with possibly changed loop values if(updateChannels) { ctrlSmp::UpdateLoopPoints(*this, sndFile); } if(GetElementarySampleSize() == 2) PrecomputeLoopsImpl(*this, sndFile); else if(GetElementarySampleSize() == 1) PrecomputeLoopsImpl(*this, sndFile); } // Remove loop points if they're invalid. void ModSample::SanitizeLoops() { LimitMax(nSustainEnd, nLength); LimitMax(nLoopEnd, nLength); if(nSustainStart >= nSustainEnd) { nSustainStart = nSustainEnd = 0; uFlags.reset(CHN_SUSTAINLOOP | CHN_PINGPONGSUSTAIN); } if(nLoopStart >= nLoopEnd) { nLoopStart = nLoopEnd = 0; uFlags.reset(CHN_LOOP | CHN_PINGPONGLOOP); } } ///////////////////////////////////////////////////////////// // Transpose <-> Frequency conversions uint32 ModSample::TransposeToFrequency(int transpose, int finetune) { return mpt::saturate_round(std::pow(2.0, (transpose * 128.0 + finetune) * (1.0 / (12.0 * 128.0))) * 8363.0); } void ModSample::TransposeToFrequency() { nC5Speed = TransposeToFrequency(RelativeTone, nFineTune); } // Return a pair of {transpose, finetune} std::pair ModSample::FrequencyToTranspose(uint32 freq) { if(!freq) return {}; const auto f2t = mpt::saturate_round(std::log(freq * (1.0 / 8363.0)) * (12.0 * 128.0 * (1.0 / mpt::numbers::ln2))); const auto fine = std::div(Clamp(f2t, -16384, 16383), int32(128)); return {static_cast(fine.quot), static_cast(fine.rem)}; } void ModSample::FrequencyToTranspose() { std::tie(RelativeTone, nFineTune) = FrequencyToTranspose(nC5Speed); } // Transpose the sample by amount specified in octaves (i.e. amount=1 transposes one octave up) void ModSample::Transpose(double amount) { nC5Speed = mpt::saturate_round(nC5Speed * std::pow(2.0, amount)); } // Check if the sample has any valid cue points bool ModSample::HasAnyCuePoints() const { if(uFlags[CHN_ADLIB]) return false; for(auto pt : cues) { if(pt < nLength) return true; } return false; } // Check if the sample's cue points are the default cue point set. bool ModSample::HasCustomCuePoints() const { if(uFlags[CHN_ADLIB]) return false; for(SmpLength i = 0; i < std::size(cues); i++) { if(cues[i] != (i + 1) << 11) return true; } return false; } void ModSample::SetDefaultCuePoints() { // Default cues compatible with old-style volume column offset for(int i = 0; i < 9; i++) { cues[i] = (i + 1) << 11; } } void ModSample::Set16BitCuePoints() { // Cue points that are useful for extending regular offset command for(int i = 0; i < 9; i++) { cues[i] = (i + 1) << 16; } } void ModSample::RemoveAllCuePoints() { if(!uFlags[CHN_ADLIB]) cues.fill(MAX_SAMPLE_LENGTH); } void ModSample::SetAdlib(bool enable, OPLPatch patch) { if(!enable && uFlags[CHN_ADLIB]) { SetDefaultCuePoints(); } uFlags.set(CHN_ADLIB, enable); if(enable) { // Bogus sample to make playback work uFlags.reset(CHN_16BIT | CHN_STEREO); nLength = 4; AllocateSample(); adlib = patch; } } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/ModSample.h0000644000175000017500000001501214175337525020140 00000000000000/* * ModSample.h * ----------- * Purpose: Module Sample header class and helpers * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" OPENMPT_NAMESPACE_BEGIN class CSoundFile; // Sample Struct struct ModSample { SmpLength nLength; // In frames SmpLength nLoopStart, nLoopEnd; // Ditto SmpLength nSustainStart, nSustainEnd; // Ditto union { void *pSample; // Pointer to sample data int8 *pSample8; // Pointer to 8-bit sample data int16 *pSample16; // Pointer to 16-bit sample data } pData; uint32 nC5Speed; // Frequency of middle-C, in Hz (for IT/S3M/MPTM) uint16 nPan; // Default sample panning (if pan flag is set), 0...256 uint16 nVolume; // Default volume, 0...256 (ignored if uFlags[SMP_NODEFAULTVOLUME] is set) uint16 nGlobalVol; // Global volume (sample volume is multiplied by this), 0...64 SampleFlags uFlags; // Sample flags (see ChannelFlags enum) int8 RelativeTone; // Relative note to middle c (for MOD/XM) int8 nFineTune; // Finetune period (for MOD/XM), -128...127, unit is 1/128th of a semitone VibratoType nVibType; // Auto vibrato type uint8 nVibSweep; // Auto vibrato sweep (i.e. how long it takes until the vibrato effect reaches its full depth) uint8 nVibDepth; // Auto vibrato depth uint8 nVibRate; // Auto vibrato rate (speed) uint8 rootNote; // For multisample import //char name[MAX_SAMPLENAME]; // Maybe it would be nicer to have sample names here, but that would require some refactoring. mpt::charbuf filename; std::string GetFilename() const { return filename; } union { std::array cues; OPLPatch adlib; }; ModSample(MODTYPE type = MOD_TYPE_NONE) { pData.pSample = nullptr; Initialize(type); } bool HasSampleData() const noexcept { MPT_ASSERT(!pData.pSample || (pData.pSample && nLength > 0)); // having sample pointer implies non-zero sample length return pData.pSample != nullptr && nLength != 0; } MPT_FORCEINLINE const void *samplev() const noexcept { return pData.pSample; } MPT_FORCEINLINE void *samplev() noexcept { return pData.pSample; } MPT_FORCEINLINE const std::byte *sampleb() const noexcept { return mpt::void_cast(pData.pSample); } MPT_FORCEINLINE std::byte *sampleb() noexcept { return mpt::void_cast(pData.pSample); } MPT_FORCEINLINE const int8 *sample8() const noexcept { MPT_ASSERT(GetElementarySampleSize() == sizeof(int8)); return pData.pSample8; } MPT_FORCEINLINE int8 *sample8() noexcept { MPT_ASSERT(GetElementarySampleSize() == sizeof(int8)); return pData.pSample8; } MPT_FORCEINLINE const int16 *sample16() const noexcept { MPT_ASSERT(GetElementarySampleSize() == sizeof(int16)); return pData.pSample16; } MPT_FORCEINLINE int16 *sample16() noexcept { MPT_ASSERT(GetElementarySampleSize() == sizeof(int16)); return pData.pSample16; } // Return the size of one (elementary) sample in bytes. uint8 GetElementarySampleSize() const noexcept { return (uFlags & CHN_16BIT) ? 2 : 1; } // Return the number of channels in the sample. uint8 GetNumChannels() const noexcept { return (uFlags & CHN_STEREO) ? 2 : 1; } // Return the number of bytes per frame (Channels * Elementary Sample Size) uint8 GetBytesPerSample() const noexcept { return GetElementarySampleSize() * GetNumChannels(); } // Return the size which pSample is at least. SmpLength GetSampleSizeInBytes() const noexcept { return nLength * GetBytesPerSample(); } // Returns sample rate of the sample. The argument is needed because // the sample rate is obtained differently for different module types. uint32 GetSampleRate(const MODTYPE type) const; // Translate sample properties between two given formats. void Convert(MODTYPE fromType, MODTYPE toType); // Initialize sample slot with default values. void Initialize(MODTYPE type = MOD_TYPE_NONE); // Copies sample data from another sample slot and ensures that the 16-bit/stereo flags are set accordingly. bool CopyWaveform(const ModSample &smpFrom); // Allocate sample based on a ModSample's properties. // Returns number of bytes allocated, 0 on failure. size_t AllocateSample(); // Allocate sample memory. On sucess, a pointer to the silenced sample buffer is returned. On failure, nullptr is returned. static void *AllocateSample(SmpLength numFrames, size_t bytesPerSample); // Compute sample buffer size in bytes, including any overhead introduced by pre-computed loops and such. Returns 0 if sample is too big. static size_t GetRealSampleBufferSize(SmpLength numSamples, size_t bytesPerSample); void FreeSample(); static void FreeSample(void *samplePtr); // Set loop points and update loop wrap-around buffer void SetLoop(SmpLength start, SmpLength end, bool enable, bool pingpong, CSoundFile &sndFile); // Set sustain loop points and update loop wrap-around buffer void SetSustainLoop(SmpLength start, SmpLength end, bool enable, bool pingpong, CSoundFile &sndFile); // Update loop wrap-around buffer void PrecomputeLoops(CSoundFile &sndFile, bool updateChannels = true); constexpr bool HasLoop() const noexcept { return uFlags[CHN_LOOP] && nLoopEnd > nLoopStart; } constexpr bool HasSustainLoop() const noexcept { return uFlags[CHN_SUSTAINLOOP] && nSustainEnd > nSustainStart; } constexpr bool HasPingPongLoop() const noexcept { return uFlags.test_all(CHN_LOOP | CHN_PINGPONGLOOP) && nLoopEnd > nLoopStart; } constexpr bool HasPingPongSustainLoop() const noexcept { return uFlags.test_all(CHN_SUSTAINLOOP | CHN_PINGPONGSUSTAIN) && nSustainEnd > nSustainStart; } // Remove loop points if they're invalid. void SanitizeLoops(); // Transpose <-> Frequency conversions static uint32 TransposeToFrequency(int transpose, int finetune = 0); void TransposeToFrequency(); static std::pair FrequencyToTranspose(uint32 freq); void FrequencyToTranspose(); // Transpose the sample by amount specified in octaves (i.e. amount=1 transposes one octave up) void Transpose(double amount); // Check if the sample has any valid cue points bool HasAnyCuePoints() const; // Check if the sample's cue points are the default cue point set. bool HasCustomCuePoints() const; void SetDefaultCuePoints(); // Set cue points so that they are suitable for regular offset command extension void Set16BitCuePoints(); void RemoveAllCuePoints(); void SetAdlib(bool enable, OPLPatch patch = OPLPatch{{}}); }; OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/ModSampleCopy.h0000644000175000017500000001316114053010430020750 00000000000000/* * ModSampleCopy.h * --------------- * Purpose: Functions for copying ModSample data. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "openmpt/soundbase/SampleDecode.hpp" OPENMPT_NAMESPACE_BEGIN struct ModSample; // Copy a mono sample data buffer. template size_t CopyMonoSample(ModSample &sample, const Tbyte *sourceBuffer, size_t sourceSize, SampleConversion conv = SampleConversion()) { MPT_ASSERT(sample.GetNumChannels() == 1); MPT_ASSERT(sample.GetElementarySampleSize() == sizeof(typename SampleConversion::output_t)); const size_t frameSize = SampleConversion::input_inc; const size_t countFrames = std::min(sourceSize / frameSize, static_cast(sample.nLength)); size_t numFrames = countFrames; SampleConversion sampleConv(conv); const std::byte * MPT_RESTRICT inBuf = mpt::byte_cast(sourceBuffer); typename SampleConversion::output_t * MPT_RESTRICT outBuf = static_cast(sample.samplev()); while(numFrames--) { *outBuf = sampleConv(inBuf); inBuf += SampleConversion::input_inc; outBuf++; } return frameSize * countFrames; } // Copy a stereo interleaved sample data buffer. template size_t CopyStereoInterleavedSample(ModSample &sample, const Tbyte *sourceBuffer, size_t sourceSize, SampleConversion conv = SampleConversion()) { MPT_ASSERT(sample.GetNumChannels() == 2); MPT_ASSERT(sample.GetElementarySampleSize() == sizeof(typename SampleConversion::output_t)); const size_t frameSize = 2 * SampleConversion::input_inc; const size_t countFrames = std::min(sourceSize / frameSize, static_cast(sample.nLength)); size_t numFrames = countFrames; SampleConversion sampleConvLeft(conv); SampleConversion sampleConvRight(conv); const std::byte * MPT_RESTRICT inBuf = mpt::byte_cast(sourceBuffer); typename SampleConversion::output_t * MPT_RESTRICT outBuf = static_cast(sample.samplev()); while(numFrames--) { *outBuf = sampleConvLeft(inBuf); inBuf += SampleConversion::input_inc; outBuf++; *outBuf = sampleConvRight(inBuf); inBuf += SampleConversion::input_inc; outBuf++; } return frameSize * countFrames; } // Copy a stereo split sample data buffer. template size_t CopyStereoSplitSample(ModSample &sample, const Tbyte *sourceBuffer, size_t sourceSize, SampleConversion conv = SampleConversion()) { MPT_ASSERT(sample.GetNumChannels() == 2); MPT_ASSERT(sample.GetElementarySampleSize() == sizeof(typename SampleConversion::output_t)); const size_t sampleSize = SampleConversion::input_inc; const size_t sourceSizeLeft = std::min(static_cast(sample.nLength) * SampleConversion::input_inc, sourceSize); const size_t sourceSizeRight = std::min(static_cast(sample.nLength) * SampleConversion::input_inc, sourceSize - sourceSizeLeft); const size_t countSamplesLeft = sourceSizeLeft / sampleSize; const size_t countSamplesRight = sourceSizeRight / sampleSize; size_t numSamplesLeft = countSamplesLeft; SampleConversion sampleConvLeft(conv); const std::byte * MPT_RESTRICT inBufLeft = mpt::byte_cast(sourceBuffer); typename SampleConversion::output_t * MPT_RESTRICT outBufLeft = static_cast(sample.samplev()); while(numSamplesLeft--) { *outBufLeft = sampleConvLeft(inBufLeft); inBufLeft += SampleConversion::input_inc; outBufLeft += 2; } size_t numSamplesRight = countSamplesRight; SampleConversion sampleConvRight(conv); const std::byte * MPT_RESTRICT inBufRight = mpt::byte_cast(sourceBuffer) + sample.nLength * SampleConversion::input_inc; typename SampleConversion::output_t * MPT_RESTRICT outBufRight = static_cast(sample.samplev()) + 1; while(numSamplesRight--) { *outBufRight = sampleConvRight(inBufRight); inBufRight += SampleConversion::input_inc; outBufRight += 2; } return (countSamplesLeft + countSamplesRight) * sampleSize; } // Copy a sample data buffer and normalize it. Requires slightly advanced sample conversion functor. template size_t CopyAndNormalizeSample(ModSample &sample, const Tbyte *sourceBuffer, size_t sourceSize, typename SampleConversion::peak_t *srcPeak = nullptr, SampleConversion conv = SampleConversion()) { const size_t sampleSize = SampleConversion::input_inc; MPT_ASSERT(sample.GetElementarySampleSize() == sizeof(typename SampleConversion::output_t)); size_t numSamples = sample.nLength * sample.GetNumChannels(); LimitMax(numSamples, sourceSize / sampleSize); const std::byte * inBuf = mpt::byte_cast(sourceBuffer); // Finding max value SampleConversion sampleConv(conv); for(size_t i = numSamples; i != 0; i--) { sampleConv.FindMax(inBuf); inBuf += SampleConversion::input_inc; } // If buffer is silent (maximum is 0), don't bother normalizing the sample - just keep the already silent buffer. if(!sampleConv.IsSilent()) { inBuf = sourceBuffer; // Copying buffer. typename SampleConversion::output_t *outBuf = static_cast(sample.samplev()); for(size_t i = numSamples; i != 0; i--) { *outBuf = sampleConv(inBuf); outBuf++; inBuf += SampleConversion::input_inc; } } if(srcPeak) { *srcPeak = sampleConv.GetSrcPeak(); } return numSamples * sampleSize; } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/ModSequence.cpp0000644000175000017500000004363714144040226021021 00000000000000/* * ModSequence.cpp * --------------- * Purpose: Order and sequence handling. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "ModSequence.h" #include "Sndfile.h" #include "mod_specifications.h" #include "../common/version.h" #include "../common/serialization_utils.h" #include "mpt/io/io.hpp" #include "mpt/io/io_stdstream.hpp" OPENMPT_NAMESPACE_BEGIN ModSequence::ModSequence(CSoundFile &sndFile) : m_sndFile(sndFile) { } ModSequence& ModSequence::operator=(const ModSequence &other) { MPT_ASSERT(&other.m_sndFile == &m_sndFile); if(&other == this) return *this; std::vector::assign(other.begin(), other.end()); m_name = other.m_name; m_restartPos = other.m_restartPos; return *this; } bool ModSequence::operator== (const ModSequence &other) const { return static_cast &>(*this) == other && m_name == other.m_name && m_restartPos == other.m_restartPos; } bool ModSequence::NeedsExtraDatafield() const { return (m_sndFile.GetType() == MOD_TYPE_MPT && m_sndFile.Patterns.GetNumPatterns() > 0xFD); } void ModSequence::AdjustToNewModType(const MODTYPE oldtype) { auto &specs = m_sndFile.GetModSpecifications(); if(oldtype != MOD_TYPE_NONE) { // If not supported, remove "+++" separator order items. if(!specs.hasIgnoreIndex) { RemovePattern(GetIgnoreIndex()); } // If not supported, remove "---" items between patterns. if(!specs.hasStopIndex) { RemovePattern(GetInvalidPatIndex()); } } //Resize orderlist if needed. if(specs.ordersMax < size()) { // Order list too long? Remove "unnecessary" order items first. if(oldtype != MOD_TYPE_NONE && specs.ordersMax < GetLengthTailTrimmed()) { erase(std::remove_if(begin(), end(), [&] (PATTERNINDEX pat) { return !m_sndFile.Patterns.IsValidPat(pat); }), end()); if(GetLengthTailTrimmed() > specs.ordersMax) { m_sndFile.AddToLog(LogWarning, U_("WARNING: Order list has been trimmed!")); } } resize(specs.ordersMax); } } ORDERINDEX ModSequence::GetLengthTailTrimmed() const { if(empty()) return 0; auto last = std::find_if(rbegin(), rend(), [] (PATTERNINDEX pat) { return pat != GetInvalidPatIndex(); }); return static_cast(std::distance(begin(), last.base())); } ORDERINDEX ModSequence::GetLengthFirstEmpty() const { return static_cast(std::distance(begin(), std::find(begin(), end(), GetInvalidPatIndex()))); } ORDERINDEX ModSequence::GetNextOrderIgnoringSkips(const ORDERINDEX start) const { if(empty()) return 0; auto length = GetLength(); ORDERINDEX next = std::min(ORDERINDEX(length - 1), ORDERINDEX(start + 1)); while(next + 1 < length && at(next) == GetIgnoreIndex()) next++; return next; } ORDERINDEX ModSequence::GetPreviousOrderIgnoringSkips(const ORDERINDEX start) const { const ORDERINDEX last = GetLastIndex(); if(start == 0 || last == 0) return 0; ORDERINDEX prev = std::min(ORDERINDEX(start - 1), last); while(prev > 0 && at(prev) == GetIgnoreIndex()) prev--; return prev; } void ModSequence::Remove(ORDERINDEX posBegin, ORDERINDEX posEnd) { if(posEnd < posBegin || posEnd >= size()) return; erase(begin() + posBegin, begin() + posEnd + 1); } // Remove all references to a given pattern index from the order list. Jump commands are updated accordingly. void ModSequence::RemovePattern(PATTERNINDEX pat) { // First, calculate the offset that needs to be applied to jump commands const ORDERINDEX orderLength = GetLengthTailTrimmed(); std::vector newPosition(orderLength); ORDERINDEX maxJump = 0; for(ORDERINDEX i = 0; i < orderLength; i++) { newPosition[i] = i - maxJump; if(at(i) == pat) { maxJump++; } } if(!maxJump) { return; } erase(std::remove(begin(), end(), pat), end()); // Only apply to patterns actually found in this sequence for(auto p : *this) if(m_sndFile.Patterns.IsValidPat(p)) { for(auto &m : m_sndFile.Patterns[p]) { if(m.command == CMD_POSITIONJUMP && m.param < newPosition.size()) { m.param = static_cast(newPosition[m.param]); } } } if(m_restartPos < newPosition.size()) { m_restartPos = newPosition[m_restartPos]; } } void ModSequence::assign(ORDERINDEX newSize, PATTERNINDEX pat) { LimitMax(newSize, m_sndFile.GetModSpecifications().ordersMax); std::vector::assign(newSize, pat); } ORDERINDEX ModSequence::insert(ORDERINDEX pos, ORDERINDEX count, PATTERNINDEX fill) { const auto ordersMax = m_sndFile.GetModSpecifications().ordersMax; if(pos >= ordersMax || GetLengthTailTrimmed() >= ordersMax || count == 0) return 0; // Limit number of orders to be inserted so that we don't exceed the format limit. LimitMax(count, static_cast(ordersMax - pos)); reserve(std::max(pos, GetLength()) + count); // Inserting past the end of the container? if(pos > size()) resize(pos); std::vector::insert(begin() + pos, count, fill); // Did we overgrow? Remove patterns at end. if(size() > ordersMax) resize(ordersMax); return count; } bool ModSequence::IsValidPat(ORDERINDEX ord) const { if(ord < size()) return m_sndFile.Patterns.IsValidPat(at(ord)); return false; } CPattern *ModSequence::PatternAt(ORDERINDEX ord) const { if(!IsValidPat(ord)) return nullptr; return &m_sndFile.Patterns[at(ord)]; } ORDERINDEX ModSequence::FindOrder(PATTERNINDEX pat, ORDERINDEX startSearchAt, bool searchForward) const { const ORDERINDEX length = GetLength(); if(startSearchAt >= length) return ORDERINDEX_INVALID; ORDERINDEX ord = startSearchAt; for(ORDERINDEX p = 0; p < length; p++) { if(at(ord) == pat) { return ord; } if(searchForward) { if(++ord >= length) ord = 0; } else { if(ord-- == 0) ord = length - 1; } } return ORDERINDEX_INVALID; } PATTERNINDEX ModSequence::EnsureUnique(ORDERINDEX ord) { PATTERNINDEX pat = at(ord); if(!IsValidPat(ord)) return pat; for(const auto &sequence : m_sndFile.Order) { ORDERINDEX ords = sequence.GetLength(); for(ORDERINDEX o = 0; o < ords; o++) { if(sequence[o] == pat && (o != ord || &sequence != this)) { // Found duplicate usage. PATTERNINDEX newPat = m_sndFile.Patterns.Duplicate(pat); if(newPat != PATTERNINDEX_INVALID) { at(ord) = newPat; return newPat; } } } } return pat; } ///////////////////////////////////// // ModSequenceSet ///////////////////////////////////// ModSequenceSet::ModSequenceSet(CSoundFile &sndFile) : m_sndFile(sndFile) { Initialize(); } void ModSequenceSet::Initialize() { m_currentSeq = 0; m_Sequences.assign(1, ModSequence(m_sndFile)); } void ModSequenceSet::SetSequence(SEQUENCEINDEX n) { if(n < m_Sequences.size()) m_currentSeq = n; } SEQUENCEINDEX ModSequenceSet::AddSequence() { if(GetNumSequences() >= MAX_SEQUENCES) return SEQUENCEINDEX_INVALID; m_Sequences.push_back(ModSequence{m_sndFile}); SetSequence(GetNumSequences() - 1); return GetNumSequences() - 1; } void ModSequenceSet::RemoveSequence(SEQUENCEINDEX i) { // Do nothing if index is invalid or if there's only one sequence left. if(i >= m_Sequences.size() || m_Sequences.size() <= 1) return; m_Sequences.erase(m_Sequences.begin() + i); if(i < m_currentSeq || m_currentSeq >= GetNumSequences()) m_currentSeq--; } #ifdef MODPLUG_TRACKER bool ModSequenceSet::Rearrange(const std::vector &newOrder) { if(newOrder.empty() || newOrder.size() > MAX_SEQUENCES) return false; const auto oldSequences = std::move(m_Sequences); m_Sequences.assign(newOrder.size(), ModSequence{m_sndFile}); for(size_t i = 0; i < newOrder.size(); i++) { if(newOrder[i] < oldSequences.size()) m_Sequences[i] = oldSequences[newOrder[i]]; } if(m_currentSeq > m_Sequences.size()) m_currentSeq = GetNumSequences() - 1u; return true; } void ModSequenceSet::OnModTypeChanged(MODTYPE oldType) { for(auto &seq : m_Sequences) { seq.AdjustToNewModType(oldType); } if(m_sndFile.GetModSpecifications(oldType).sequencesMax > 1 && m_sndFile.GetModSpecifications().sequencesMax <= 1) MergeSequences(); } bool ModSequenceSet::CanSplitSubsongs() const { return GetNumSequences() == 1 && m_sndFile.GetModSpecifications().sequencesMax > 1 && m_Sequences[0].HasSubsongs(); } bool ModSequenceSet::SplitSubsongsToMultipleSequences() { if(!CanSplitSubsongs()) return false; bool modified = false; const ORDERINDEX length = m_Sequences[0].GetLengthTailTrimmed(); for(ORDERINDEX ord = 0; ord < length; ord++) { // End of subsong? if(!m_Sequences[0].IsValidPat(ord) && m_Sequences[0][ord] != GetIgnoreIndex()) { // Remove all separator patterns between current and next subsong first while(ord < length && !m_sndFile.Patterns.IsValidPat(m_Sequences[0][ord])) { m_Sequences[0][ord] = GetInvalidPatIndex(); ord++; modified = true; } if(ord >= length) break; const SEQUENCEINDEX newSeq = AddSequence(); if(newSeq == SEQUENCEINDEX_INVALID) break; const ORDERINDEX startOrd = ord; m_Sequences[newSeq].reserve(length - startOrd); modified = true; // Now, move all following orders to the new sequence while(ord < length && m_Sequences[0][ord] != GetInvalidPatIndex()) { PATTERNINDEX copyPat = m_Sequences[0][ord]; m_Sequences[newSeq].push_back(copyPat); m_Sequences[0][ord] = GetInvalidPatIndex(); ord++; // Is this a valid pattern? adjust pattern jump commands, if necessary. if(m_sndFile.Patterns.IsValidPat(copyPat)) { for(auto &m : m_sndFile.Patterns[copyPat]) { if(m.command == CMD_POSITIONJUMP && m.param >= startOrd) { m.param = static_cast(m.param - startOrd); } } } } ord--; } } SetSequence(0); return modified; } // Convert the sequence's restart position information to a pattern command. bool ModSequenceSet::RestartPosToPattern(SEQUENCEINDEX seq) { bool result = false; auto length = m_sndFile.GetLength(eNoAdjust, GetLengthTarget(true).StartPos(seq, 0, 0)); ModSequence &order = m_Sequences[seq]; for(const auto &subSong : length) { if(subSong.endOrder != ORDERINDEX_INVALID && subSong.endRow != ROWINDEX_INVALID) { if(mpt::in_range(order.GetRestartPos())) { PATTERNINDEX writePat = order.EnsureUnique(subSong.endOrder); result = m_sndFile.Patterns[writePat].WriteEffect( EffectWriter(CMD_POSITIONJUMP, static_cast(order.GetRestartPos())).Row(subSong.endRow).RetryNextRow()); } else { result = false; } } } order.SetRestartPos(0); return result; } bool ModSequenceSet::MergeSequences() { if(GetNumSequences() <= 1) return false; ModSequence &firstSeq = m_Sequences[0]; firstSeq.resize(firstSeq.GetLengthTailTrimmed()); std::vector patternsFixed(m_sndFile.Patterns.Size(), SEQUENCEINDEX_INVALID); // pattern fixed by other sequence already? // Mark patterns handled in first sequence for(auto pat : firstSeq) { if(m_sndFile.Patterns.IsValidPat(pat)) patternsFixed[pat] = 0; } for(SEQUENCEINDEX seqNum = 1; seqNum < GetNumSequences(); seqNum++) { ModSequence &seq = m_Sequences[seqNum]; const ORDERINDEX firstOrder = firstSeq.GetLength() + 1; // +1 for separator item const ORDERINDEX lengthTrimmed = seq.GetLengthTailTrimmed(); if(firstOrder + lengthTrimmed > m_sndFile.GetModSpecifications().ordersMax) { m_sndFile.AddToLog(LogWarning, MPT_UFORMAT("WARNING: Cannot merge Sequence {} (too long!)")(seqNum + 1)); continue; } firstSeq.reserve(firstOrder + lengthTrimmed); firstSeq.push_back(); // Separator item RestartPosToPattern(seqNum); for(ORDERINDEX ord = 0; ord < lengthTrimmed; ord++) { PATTERNINDEX pat = seq[ord]; firstSeq.push_back(pat); // Try to fix pattern jump commands if(!m_sndFile.Patterns.IsValidPat(pat)) continue; auto m = m_sndFile.Patterns[pat].begin(); for(size_t len = 0; len < m_sndFile.Patterns[pat].GetNumRows() * m_sndFile.m_nChannels; m++, len++) { if(m->command == CMD_POSITIONJUMP) { if(patternsFixed[pat] != SEQUENCEINDEX_INVALID && patternsFixed[pat] != seqNum) { // Oops, some other sequence uses this pattern already. const PATTERNINDEX newPat = m_sndFile.Patterns.Duplicate(pat, true); if(newPat != PATTERNINDEX_INVALID) { // Could create new pattern - copy data over and continue from here. firstSeq[firstOrder + ord] = newPat; m = m_sndFile.Patterns[newPat].begin() + len; if(newPat >= patternsFixed.size()) patternsFixed.resize(newPat + 1, SEQUENCEINDEX_INVALID); pat = newPat; } else { // Cannot create new pattern: notify the user m_sndFile.AddToLog(LogWarning, MPT_UFORMAT("CONFLICT: Pattern break commands in Pattern {} might be broken since it has been used in several sequences!")(pat)); } } m->param = static_cast(m->param + firstOrder); patternsFixed[pat] = seqNum; } } } } m_Sequences.erase(m_Sequences.begin() + 1, m_Sequences.end()); return true; } // Check if a playback position is currently locked (inaccessible) bool ModSequence::IsPositionLocked(ORDERINDEX position) const { return(m_sndFile.m_lockOrderStart != ORDERINDEX_INVALID && (position < m_sndFile.m_lockOrderStart || position > m_sndFile.m_lockOrderEnd)); } bool ModSequence::HasSubsongs() const { const auto endPat = begin() + GetLengthTailTrimmed(); return std::find_if(begin(), endPat, [&](PATTERNINDEX pat) { return pat != GetIgnoreIndex() && !m_sndFile.Patterns.IsValidPat(pat); }) != endPat; } #endif // MODPLUG_TRACKER ///////////////////////////////////// // Read/Write ///////////////////////////////////// #ifndef MODPLUG_NO_FILESAVE size_t ModSequence::WriteAsByte(std::ostream &f, const ORDERINDEX count, uint8 stopIndex, uint8 ignoreIndex) const { const size_t limit = std::min(count, GetLength()); for(size_t i = 0; i < limit; i++) { const PATTERNINDEX pat = at(i); uint8 temp = static_cast(pat); if(pat == GetInvalidPatIndex()) temp = stopIndex; else if(pat == GetIgnoreIndex() || pat > 0xFF) temp = ignoreIndex; mpt::IO::WriteIntLE(f, temp); } // Fill non-existing order items with stop indices for(size_t i = limit; i < count; i++) { mpt::IO::WriteIntLE(f, stopIndex); } return count; //Returns the number of bytes written. } #endif // MODPLUG_NO_FILESAVE void ReadModSequenceOld(std::istream& iStrm, ModSequenceSet& seq, const size_t) { uint16 size; mpt::IO::ReadIntLE(iStrm, size); if(size > ModSpecs::mptm.ordersMax) { seq.m_sndFile.AddToLog(LogWarning, MPT_UFORMAT("Module has sequence of length {}; it will be truncated to maximum supported length, {}.")(size, ModSpecs::mptm.ordersMax)); size = ModSpecs::mptm.ordersMax; } seq(0).resize(size); for(auto &pat : seq(0)) { uint16 temp; mpt::IO::ReadIntLE(iStrm, temp); pat = temp; } } #ifndef MODPLUG_NO_FILESAVE void WriteModSequenceOld(std::ostream& oStrm, const ModSequenceSet& seq) { const uint16 size = seq().GetLength(); mpt::IO::WriteIntLE(oStrm, size); for(auto pat : seq()) { mpt::IO::WriteIntLE(oStrm, static_cast(pat)); } } #endif // MODPLUG_NO_FILESAVE #ifndef MODPLUG_NO_FILESAVE void WriteModSequence(std::ostream& oStrm, const ModSequence& seq) { srlztn::SsbWrite ssb(oStrm); ssb.BeginWrite(FileIdSequence, Version::Current().GetRawVersion()); int8 useUTF8 = 1; ssb.WriteItem(useUTF8, "u"); ssb.WriteItem(mpt::ToCharset(mpt::Charset::UTF8, seq.GetName()), "n"); const uint16 length = seq.GetLengthTailTrimmed(); ssb.WriteItem(length, "l"); ssb.WriteItem(seq, "a", srlztn::VectorWriter(length)); if(seq.GetRestartPos() > 0) ssb.WriteItem(seq.GetRestartPos(), "r"); ssb.FinishWrite(); } #endif // MODPLUG_NO_FILESAVE void ReadModSequence(std::istream& iStrm, ModSequence& seq, const size_t, mpt::Charset defaultCharset) { srlztn::SsbRead ssb(iStrm); ssb.BeginRead(FileIdSequence, Version::Current().GetRawVersion()); if ((ssb.GetStatus() & srlztn::SNT_FAILURE) != 0) return; int8 useUTF8 = 0; ssb.ReadItem(useUTF8, "u"); std::string str; ssb.ReadItem(str, "n"); seq.SetName(mpt::ToUnicode(useUTF8 ? mpt::Charset::UTF8 : defaultCharset, str)); ORDERINDEX nSize = 0; ssb.ReadItem(nSize, "l"); LimitMax(nSize, ModSpecs::mptm.ordersMax); ssb.ReadItem(seq, "a", srlztn::VectorReader(nSize)); ORDERINDEX restartPos = ORDERINDEX_INVALID; if(ssb.ReadItem(restartPos, "r") != srlztn::SsbRead::EntryNotFound && restartPos < nSize) seq.SetRestartPos(restartPos); } #ifndef MODPLUG_NO_FILESAVE void WriteModSequences(std::ostream& oStrm, const ModSequenceSet& seq) { srlztn::SsbWrite ssb(oStrm); ssb.BeginWrite(FileIdSequences, Version::Current().GetRawVersion()); const uint8 nSeqs = seq.GetNumSequences(); const uint8 nCurrent = seq.GetCurrentSequenceIndex(); ssb.WriteItem(nSeqs, "n"); ssb.WriteItem(nCurrent, "c"); for(uint8 i = 0; i < nSeqs; i++) { ssb.WriteItem(seq(i), srlztn::ID::FromInt(i), &WriteModSequence); } ssb.FinishWrite(); } #endif // MODPLUG_NO_FILESAVE void ReadModSequences(std::istream& iStrm, ModSequenceSet& seq, const size_t, mpt::Charset defaultCharset) { srlztn::SsbRead ssb(iStrm); ssb.BeginRead(FileIdSequences, Version::Current().GetRawVersion()); if ((ssb.GetStatus() & srlztn::SNT_FAILURE) != 0) return; SEQUENCEINDEX seqs = 0; uint8 currentSeq = 0; ssb.ReadItem(seqs, "n"); if (seqs == 0) return; LimitMax(seqs, MAX_SEQUENCES); ssb.ReadItem(currentSeq, "c"); if (seq.GetNumSequences() < seqs) seq.m_Sequences.resize(seqs, ModSequence(seq.m_sndFile)); // There used to be only one restart position for all sequences ORDERINDEX legacyRestartPos = seq(0).GetRestartPos(); for(SEQUENCEINDEX i = 0; i < seqs; i++) { seq(i).SetRestartPos(legacyRestartPos); ssb.ReadItem(seq(i), srlztn::ID::FromInt(i), [defaultCharset](std::istream &iStrm, ModSequence &seq, std::size_t dummy) { return ReadModSequence(iStrm, seq, dummy, defaultCharset); }); } seq.m_currentSeq = (currentSeq < seq.GetNumSequences()) ? currentSeq : 0; } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/ModSequence.h0000644000175000017500000002231514144040226020454 00000000000000/* * ModSequence.h * ------------- * Purpose: Order and sequence handling. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "Snd_defs.h" #include #include OPENMPT_NAMESPACE_BEGIN class CPattern; class CSoundFile; class ModSequenceSet; class ModSequence: public std::vector { friend class ModSequenceSet; protected: mpt::ustring m_name; // Sequence name CSoundFile &m_sndFile; // Associated CSoundFile ORDERINDEX m_restartPos = 0; // Restart position when playback of this order ended public: ModSequence(CSoundFile &sndFile); ModSequence(ModSequence &&) noexcept = default; ModSequence(const ModSequence &) = default; ModSequence& operator=(const ModSequence &other); bool operator==(const ModSequence &other) const; bool operator!=(const ModSequence &other) const { return !(*this == other); } ORDERINDEX GetLength() const { return mpt::saturate_cast(size()); } // Returns last accessible index, i.e. GetLength() - 1, or 0 if the order list is empty. ORDERINDEX GetLastIndex() const { return std::max(ORDERINDEX(1), GetLength()) - 1u; } // Returns length of sequence without counting trailing '---' items. ORDERINDEX GetLengthTailTrimmed() const; // Returns length of sequence stopping counting on first '---' (or at the end of sequence). ORDERINDEX GetLengthFirstEmpty() const; // Replaces order list with 'newSize' copies of 'pat'. void assign(ORDERINDEX newSize, PATTERNINDEX pat); // Inserts 'count' orders starting from 'pos' using 'fill' as the pattern index for all inserted orders. // Sequence will automatically grow if needed and if it can't grow enough, some tail orders will be discarded. // Return: Number of orders inserted (up to 'count' many). ORDERINDEX insert(ORDERINDEX pos, ORDERINDEX count) { return insert(pos, count, GetInvalidPatIndex()); } ORDERINDEX insert(ORDERINDEX pos, ORDERINDEX count, PATTERNINDEX fill); void push_back() { push_back(GetInvalidPatIndex()); } void push_back(PATTERNINDEX pat) { if(GetLength() < MAX_ORDERS) std::vector::push_back(pat); } void resize(ORDERINDEX newSize) { resize(newSize, GetInvalidPatIndex()); } void resize(ORDERINDEX newSize, PATTERNINDEX pat) { std::vector::resize(std::min(MAX_ORDERS, newSize), pat); } // Removes orders from range [posBegin, posEnd]. void Remove(ORDERINDEX posBegin, ORDERINDEX posEnd); // Remove all references to a given pattern index from the order list. Jump commands are updated accordingly. void RemovePattern(PATTERNINDEX pat); // Replaces all occurences of oldPat with newPat. void Replace(PATTERNINDEX oldPat, PATTERNINDEX newPat) { if(oldPat != newPat) std::replace(begin(), end(), oldPat, newPat); } // Removes any "---" patterns at the end of the list. void Shrink() { resize(GetLengthTailTrimmed()); } // Check if pattern at sequence position ord is valid. bool IsValidPat(ORDERINDEX ord) const; CPattern *PatternAt(ORDERINDEX ord) const; void AdjustToNewModType(const MODTYPE oldtype); // Returns the internal representation of a stop '---' index static constexpr PATTERNINDEX GetInvalidPatIndex() { return uint16_max; } // Returns the internal representation of an ignore '+++' index static constexpr PATTERNINDEX GetIgnoreIndex() { return uint16_max - 1; } // Returns the previous/next order ignoring skip indices (+++). // If no previous/next order exists, return first/last order, and zero // when orderlist is empty. ORDERINDEX GetPreviousOrderIgnoringSkips(const ORDERINDEX start) const; ORDERINDEX GetNextOrderIgnoringSkips(const ORDERINDEX start) const; // Find an order item that contains a given pattern number. ORDERINDEX FindOrder(PATTERNINDEX pat, ORDERINDEX startSearchAt = 0, bool searchForward = true) const; // Ensures that the pattern at the specified order position is used only once (across all sequences). // If another usage is found, the pattern is replaced by a copy and the new index is returned. PATTERNINDEX EnsureUnique(ORDERINDEX ord); #ifndef MODPLUG_NO_FILESAVE // Write order items as bytes. '---' is written as stopIndex, '+++' is written as ignoreIndex size_t WriteAsByte(std::ostream &f, const ORDERINDEX count, uint8 stopIndex = 0xFF, uint8 ignoreIndex = 0xFE) const; #endif // MODPLUG_NO_FILESAVE // Returns true if the IT orderlist datafield is not sufficient to store orderlist information. bool NeedsExtraDatafield() const; #ifdef MODPLUG_TRACKER // Check if a playback position is currently locked (inaccessible) bool IsPositionLocked(ORDERINDEX position) const; // Check if this sequence has subsongs separated by invalid ("---" or non-existing) patterns bool HasSubsongs() const; #endif // MODPLUG_TRACKER // Sequence name setter / getter inline void SetName(const mpt::ustring &newName) { m_name = newName;} inline mpt::ustring GetName() const { return m_name; } // Restart position setter / getter inline void SetRestartPos(ORDERINDEX restartPos) noexcept { m_restartPos = restartPos; } inline ORDERINDEX GetRestartPos() const noexcept { return m_restartPos; } }; class ModSequenceSet { friend void ReadModSequenceOld(std::istream& iStrm, ModSequenceSet& seq, const size_t); friend void ReadModSequences(std::istream& iStrm, ModSequenceSet& seq, const size_t, mpt::Charset defaultCharset); protected: std::vector m_Sequences; // Array of sequences CSoundFile &m_sndFile; SEQUENCEINDEX m_currentSeq = 0; // Index of current sequence. public: ModSequenceSet(CSoundFile &sndFile); ModSequenceSet(ModSequenceSet &&) noexcept = default; // Remove all sequences and initialize default sequence void Initialize(); // Get the working sequence ModSequence& operator() () { return m_Sequences[m_currentSeq]; } const ModSequence& operator() () const { return m_Sequences[m_currentSeq]; } // Get an arbitrary sequence ModSequence& operator() (SEQUENCEINDEX seq) { return m_Sequences[seq]; } const ModSequence& operator() (SEQUENCEINDEX seq) const { return m_Sequences[seq]; } SEQUENCEINDEX GetNumSequences() const { return static_cast(m_Sequences.size()); } SEQUENCEINDEX GetCurrentSequenceIndex() const { return m_currentSeq; } // Sets working sequence. void SetSequence(SEQUENCEINDEX); // Add new empty sequence. // Returns the ID of the new sequence, or SEQUENCEINDEX_INVALID on failure. SEQUENCEINDEX AddSequence(); // Removes given sequence. void RemoveSequence(SEQUENCEINDEX); // Returns the internal representation of a stop '---' index static constexpr PATTERNINDEX GetInvalidPatIndex() { return ModSequence::GetInvalidPatIndex(); } // Returns the internal representation of an ignore '+++' index static constexpr PATTERNINDEX GetIgnoreIndex() { return ModSequence::GetIgnoreIndex(); } #ifdef MODPLUG_TRACKER // Assigns a new set of sequences. The vector contents indicate which existing sequences to keep / duplicate or if a new sequences should be inserted (SEQUENCEINDEX_INVALID) // The function fails if the vector is empty or contains too many sequences. bool Rearrange(const std::vector &newOrder); // Adjust sequence when converting between module formats void OnModTypeChanged(MODTYPE oldType); // Check if there is a single sequences that qualifies for subsong splitting bool CanSplitSubsongs() const; // If there are subsongs (separated by "---" patterns) in the module, // asks user whether to convert these into multiple sequences (given that the // modformat supports multiple sequences). // Returns true if sequences were modified, false otherwise. bool SplitSubsongsToMultipleSequences(); // Convert the sequence's restart position information to a pattern command. bool RestartPosToPattern(SEQUENCEINDEX seq); // Merges multiple sequences into one and destroys all other sequences. // Returns false if there were no sequences to merge, true otherwise. bool MergeSequences(); #endif // MODPLUG_TRACKER std::vector::iterator begin() { return m_Sequences.begin(); } std::vector::const_iterator begin() const { return m_Sequences.begin(); } std::vector::const_iterator cbegin() const { return m_Sequences.cbegin(); } std::vector::iterator end() { return m_Sequences.end(); } std::vector::const_iterator end() const { return m_Sequences.end(); } std::vector::const_iterator cend() const { return m_Sequences.cend(); } }; const char FileIdSequences[] = "mptSeqC"; const char FileIdSequence[] = "mptSeq"; #ifndef MODPLUG_NO_FILESAVE void WriteModSequences(std::ostream& oStrm, const ModSequenceSet& seq); #endif // MODPLUG_NO_FILESAVE void ReadModSequences(std::istream& iStrm, ModSequenceSet& seq, const size_t nSize, mpt::Charset defaultCharset); #ifndef MODPLUG_NO_FILESAVE void WriteModSequence(std::ostream& oStrm, const ModSequence& seq); #endif // MODPLUG_NO_FILESAVE void ReadModSequence(std::istream& iStrm, ModSequence& seq, const size_t, mpt::Charset defaultCharset); #ifndef MODPLUG_NO_FILESAVE void WriteModSequenceOld(std::ostream& oStrm, const ModSequenceSet& seq); #endif // MODPLUG_NO_FILESAVE void ReadModSequenceOld(std::istream& iStrm, ModSequenceSet& seq, const size_t); OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/modsmp_ctrl.cpp0000644000175000017500000003014414053010430021112 00000000000000/* * modsmp_ctrl.cpp * --------------- * Purpose: Basic sample editing code. * Notes : This is a legacy namespace. Some of this stuff is not required in libopenmpt (but stuff in soundlib/ still depends on it). The rest could be merged into struct ModSample. * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "modsmp_ctrl.h" #include "AudioCriticalSection.h" #include "Sndfile.h" OPENMPT_NAMESPACE_BEGIN namespace ctrlSmp { void ReplaceSample(ModSample &smp, void *pNewSample, const SmpLength newLength, CSoundFile &sndFile) { void * const pOldSmp = smp.samplev(); FlagSet setFlags, resetFlags; setFlags.set(CHN_16BIT, smp.uFlags[CHN_16BIT]); resetFlags.set(CHN_16BIT, !smp.uFlags[CHN_16BIT]); setFlags.set(CHN_STEREO, smp.uFlags[CHN_STEREO]); resetFlags.set(CHN_STEREO, !smp.uFlags[CHN_STEREO]); CriticalSection cs; ctrlChn::ReplaceSample(sndFile, smp, pNewSample, newLength, setFlags, resetFlags); smp.pData.pSample = pNewSample; smp.nLength = newLength; ModSample::FreeSample(pOldSmp); } // Propagate loop point changes to player bool UpdateLoopPoints(const ModSample &smp, CSoundFile &sndFile) { if(!smp.HasSampleData()) return false; CriticalSection cs; // Update channels with new loop values for(auto &chn : sndFile.m_PlayState.Chn) if((chn.pModSample == &smp) && chn.nLength != 0) { bool looped = false, bidi = false; if(smp.nSustainStart < smp.nSustainEnd && smp.nSustainEnd <= smp.nLength && smp.uFlags[CHN_SUSTAINLOOP] && !chn.dwFlags[CHN_KEYOFF]) { // Sustain loop is active chn.nLoopStart = smp.nSustainStart; chn.nLoopEnd = smp.nSustainEnd; chn.nLength = smp.nSustainEnd; looped = true; bidi = smp.uFlags[CHN_PINGPONGSUSTAIN]; } else if(smp.nLoopStart < smp.nLoopEnd && smp.nLoopEnd <= smp.nLength && smp.uFlags[CHN_LOOP]) { // Normal loop is active chn.nLoopStart = smp.nLoopStart; chn.nLoopEnd = smp.nLoopEnd; chn.nLength = smp.nLoopEnd; looped = true; bidi = smp.uFlags[CHN_PINGPONGLOOP]; } chn.dwFlags.set(CHN_LOOP, looped); chn.dwFlags.set(CHN_PINGPONGLOOP, looped && bidi); if(chn.position.GetUInt() > chn.nLength) { chn.position.Set(chn.nLoopStart); chn.dwFlags.reset(CHN_PINGPONGFLAG); } if(!bidi) { chn.dwFlags.reset(CHN_PINGPONGFLAG); } if(!looped) { chn.nLength = smp.nLength; } } return true; } template static void ReverseSampleImpl(T *pStart, const SmpLength length) { for(SmpLength i = 0; i < length / 2; i++) { std::swap(pStart[i], pStart[length - 1 - i]); } } // Reverse sample data bool ReverseSample(ModSample &smp, SmpLength start, SmpLength end, CSoundFile &sndFile) { if(!smp.HasSampleData()) return false; if(end == 0 || start > smp.nLength || end > smp.nLength) { start = 0; end = smp.nLength; } if(end - start < 2) return false; static_assert(MaxSamplingPointSize <= 4); if(smp.GetBytesPerSample() == 4) // 16 bit stereo ReverseSampleImpl(static_cast(smp.samplev()) + start, end - start); else if(smp.GetBytesPerSample() == 2) // 16 bit mono / 8 bit stereo ReverseSampleImpl(static_cast(smp.samplev()) + start, end - start); else if(smp.GetBytesPerSample() == 1) // 8 bit mono ReverseSampleImpl(static_cast(smp.samplev()) + start, end - start); else return false; smp.PrecomputeLoops(sndFile, false); return true; } template static void InvertSampleImpl(T *pStart, const SmpLength length) { for(SmpLength i = 0; i < length; i++) { pStart[i] = ~pStart[i]; } } // Invert sample data (flip by 180 degrees) bool InvertSample(ModSample &smp, SmpLength start, SmpLength end, CSoundFile &sndFile) { if(!smp.HasSampleData()) return false; if(end == 0 || start > smp.nLength || end > smp.nLength) { start = 0; end = smp.nLength; } start *= smp.GetNumChannels(); end *= smp.GetNumChannels(); if(smp.GetElementarySampleSize() == 2) InvertSampleImpl(smp.sample16() + start, end - start); else if(smp.GetElementarySampleSize() == 1) InvertSampleImpl(smp.sample8() + start, end - start); else return false; smp.PrecomputeLoops(sndFile, false); return true; } template static void XFadeSampleImpl(const T *srcIn, const T *srcOut, T *output, const SmpLength fadeLength, double e) { const double length = 1.0 / static_cast(fadeLength); for(SmpLength i = 0; i < fadeLength; i++, srcIn++, srcOut++, output++) { double fact1 = std::pow(i * length, e); double fact2 = std::pow((fadeLength - i) * length, e); int32 val = static_cast( static_cast(*srcIn) * fact1 + static_cast(*srcOut) * fact2); *output = mpt::saturate_cast(val); } } // X-Fade sample data to create smooth loop transitions bool XFadeSample(ModSample &smp, SmpLength fadeLength, int fadeLaw, bool afterloopFade, bool useSustainLoop, CSoundFile &sndFile) { if(!smp.HasSampleData()) return false; const SmpLength loopStart = useSustainLoop ? smp.nSustainStart : smp.nLoopStart; const SmpLength loopEnd = useSustainLoop ? smp.nSustainEnd : smp.nLoopEnd; if(loopEnd <= loopStart || loopEnd > smp.nLength) return false; if(loopStart < fadeLength) return false; const SmpLength start = (loopStart - fadeLength) * smp.GetNumChannels(); const SmpLength end = (loopEnd - fadeLength) * smp.GetNumChannels(); const SmpLength afterloopStart = loopStart * smp.GetNumChannels(); const SmpLength afterloopEnd = loopEnd * smp.GetNumChannels(); const SmpLength afterLoopLength = std::min(smp.nLength - loopEnd, fadeLength) * smp.GetNumChannels(); fadeLength *= smp.GetNumChannels(); // e=0.5: constant power crossfade (for uncorrelated samples), e=1.0: constant volume crossfade (for perfectly correlated samples) const double e = 1.0 - fadeLaw / 200000.0; if(smp.GetElementarySampleSize() == 2) { XFadeSampleImpl(smp.sample16() + start, smp.sample16() + end, smp.sample16() + end, fadeLength, e); if(afterloopFade) XFadeSampleImpl(smp.sample16() + afterloopEnd, smp.sample16() + afterloopStart, smp.sample16() + afterloopEnd, afterLoopLength, e); } else if(smp.GetElementarySampleSize() == 1) { XFadeSampleImpl(smp.sample8() + start, smp.sample8() + end, smp.sample8() + end, fadeLength, e); if(afterloopFade) XFadeSampleImpl(smp.sample8() + afterloopEnd, smp.sample8() + afterloopStart, smp.sample8() + afterloopEnd, afterLoopLength, e); } else return false; smp.PrecomputeLoops(sndFile, true); return true; } template static void ConvertStereoToMonoMixImpl(T *pDest, const SmpLength length) { const T *pEnd = pDest + length; for(T *pSource = pDest; pDest != pEnd; pDest++, pSource += 2) { *pDest = static_cast(mpt::rshift_signed(pSource[0] + pSource[1] + 1, 1)); } } template static void ConvertStereoToMonoOneChannelImpl(T *pDest, const T *pSource, const SmpLength length) { for(const T *pEnd = pDest + length; pDest != pEnd; pDest++, pSource += 2) { *pDest = *pSource; } } // Convert a multichannel sample to mono (currently only implemented for stereo) bool ConvertToMono(ModSample &smp, CSoundFile &sndFile, StereoToMonoMode conversionMode) { if(!smp.HasSampleData() || smp.GetNumChannels() != 2) return false; // Note: Sample is overwritten in-place! Unused data is not deallocated! if(conversionMode == mixChannels) { if(smp.GetElementarySampleSize() == 2) ConvertStereoToMonoMixImpl(smp.sample16(), smp.nLength); else if(smp.GetElementarySampleSize() == 1) ConvertStereoToMonoMixImpl(smp.sample8(), smp.nLength); else return false; } else { if(conversionMode == splitSample) { conversionMode = onlyLeft; } if(smp.GetElementarySampleSize() == 2) ConvertStereoToMonoOneChannelImpl(smp.sample16(), smp.sample16() + (conversionMode == onlyLeft ? 0 : 1), smp.nLength); else if(smp.GetElementarySampleSize() == 1) ConvertStereoToMonoOneChannelImpl(smp.sample8(), smp.sample8() + (conversionMode == onlyLeft ? 0 : 1), smp.nLength); else return false; } CriticalSection cs; smp.uFlags.reset(CHN_STEREO); for(auto &chn : sndFile.m_PlayState.Chn) { if(chn.pModSample == &smp) { chn.dwFlags.reset(CHN_STEREO); } } smp.PrecomputeLoops(sndFile, false); return true; } template static void SplitStereoImpl(void *destL, void *destR, const T *source, SmpLength length) { T *l = static_cast(destL), *r = static_cast(destR); while(length--) { *(l++) = source[0]; *(r++) = source[1]; source += 2; } } // Converts a stereo sample into two mono samples. Source sample will not be deleted. bool SplitStereo(const ModSample &source, ModSample &left, ModSample &right, CSoundFile &sndFile) { if(!source.HasSampleData() || source.GetNumChannels() != 2 || &left == &right) return false; const bool sourceIsLeft = &left == &source, sourceIsRight = &right == &source; if(left.HasSampleData() && !sourceIsLeft) return false; if(right.HasSampleData() && !sourceIsRight) return false; void *leftData = sourceIsLeft ? left.samplev() : ModSample::AllocateSample(source.nLength, source.GetElementarySampleSize()); void *rightData = sourceIsRight ? right.samplev() : ModSample::AllocateSample(source.nLength, source.GetElementarySampleSize()); if(!leftData || !rightData) { if(!sourceIsLeft) ModSample::FreeSample(leftData); if(!sourceIsRight) ModSample::FreeSample(rightData); return false; } if(source.GetElementarySampleSize() == 2) SplitStereoImpl(leftData, rightData, source.sample16(), source.nLength); else if(source.GetElementarySampleSize() == 1) SplitStereoImpl(leftData, rightData, source.sample8(), source.nLength); else MPT_ASSERT_NOTREACHED(); CriticalSection cs; left = source; left.uFlags.reset(CHN_STEREO); left.pData.pSample = leftData; right = source; right.uFlags.reset(CHN_STEREO); right.pData.pSample = rightData; for(auto &chn : sndFile.m_PlayState.Chn) { if(chn.pModSample == &left || chn.pModSample == &right) chn.dwFlags.reset(CHN_STEREO); } left.PrecomputeLoops(sndFile, false); right.PrecomputeLoops(sndFile, false); return true; } template static void ConvertMonoToStereoImpl(const T *MPT_RESTRICT src, T *MPT_RESTRICT dst, SmpLength length) { while(length--) { dst[0] = *src; dst[1] = *src; dst += 2; src++; } } // Convert a multichannel sample to mono (currently only implemented for stereo) bool ConvertToStereo(ModSample &smp, CSoundFile &sndFile) { if(!smp.HasSampleData() || smp.GetNumChannels() != 1) return false; void *newSample = ModSample::AllocateSample(smp.nLength, smp.GetBytesPerSample() * 2); if(newSample == nullptr) { return 0; } if(smp.GetElementarySampleSize() == 2) ConvertMonoToStereoImpl(smp.sample16(), (int16 *)newSample, smp.nLength); else if(smp.GetElementarySampleSize() == 1) ConvertMonoToStereoImpl(smp.sample8(), (int8 *)newSample, smp.nLength); else return false; CriticalSection cs; smp.uFlags.set(CHN_STEREO); ReplaceSample(smp, newSample, smp.nLength, sndFile); smp.PrecomputeLoops(sndFile, false); return true; } } // namespace ctrlSmp namespace ctrlChn { void ReplaceSample( CSoundFile &sndFile, const ModSample &sample, const void * const pNewSample, const SmpLength newLength, FlagSet setFlags, FlagSet resetFlags) { const bool periodIsFreq = sndFile.PeriodsAreFrequencies(); for(auto &chn : sndFile.m_PlayState.Chn) { if(chn.pModSample == &sample) { if(chn.pCurrentSample != nullptr) chn.pCurrentSample = pNewSample; if(chn.position.GetUInt() > newLength) chn.position.Set(0); if(chn.nLength > 0) LimitMax(chn.nLength, newLength); if(chn.InSustainLoop()) { chn.nLoopStart = sample.nSustainStart; chn.nLoopEnd = sample.nSustainEnd; } else { chn.nLoopStart = sample.nLoopStart; chn.nLoopEnd = sample.nLoopEnd; } chn.dwFlags.set(setFlags); chn.dwFlags.reset(resetFlags); if(chn.nC5Speed && sample.nC5Speed && !sndFile.UseFinetuneAndTranspose()) { if(periodIsFreq) chn.nPeriod = Util::muldivr_unsigned(chn.nPeriod, sample.nC5Speed, chn.nC5Speed); else chn.nPeriod = Util::muldivr_unsigned(chn.nPeriod, chn.nC5Speed, sample.nC5Speed); } chn.nC5Speed = sample.nC5Speed; } } } } // namespace ctrlChn OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/modsmp_ctrl.h0000644000175000017500000000402314052666041020572 00000000000000/* * modsmp_ctrl.h * ------------- * Purpose: Basic sample editing code * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "Snd_defs.h" OPENMPT_NAMESPACE_BEGIN class CSoundFile; struct ModSample; struct ModChannel; namespace ctrlSmp { // Replaces sample in 'smp' with given sample and frees the old sample. void ReplaceSample(ModSample &smp, void *pNewSample, const SmpLength newLength, CSoundFile &sndFile); // Propagate loop point changes to player bool UpdateLoopPoints(const ModSample &smp, CSoundFile &sndFile); // Reverse sample data bool ReverseSample(ModSample &smp, SmpLength start, SmpLength end, CSoundFile &sndFile); // Invert sample data (flip by 180 degrees) bool InvertSample(ModSample &smp, SmpLength start, SmpLength end, CSoundFile &sndFile); // Crossfade sample data to create smooth loops bool XFadeSample(ModSample &smp, SmpLength fadeLength, int fadeLaw, bool afterloopFade, bool useSustainLoop, CSoundFile &sndFile); enum StereoToMonoMode { mixChannels, onlyLeft, onlyRight, splitSample, }; // Convert a sample with any number of channels to mono bool ConvertToMono(ModSample &smp, CSoundFile &sndFile, StereoToMonoMode conversionMode); // Converts a stereo sample into two mono samples. Source sample will not be deleted. // Either of the two target samples may be identical to the source sample. bool SplitStereo(const ModSample &source, ModSample &left, ModSample &right, CSoundFile &sndFile); // Convert a mono sample to stereo bool ConvertToStereo(ModSample &smp, CSoundFile &sndFile); } // Namespace ctrlSmp namespace ctrlChn { // Replaces sample from sound channels by given sample. void ReplaceSample( CSoundFile &sndFile, const ModSample &sample, const void * const pNewSample, const SmpLength newLength, FlagSet setFlags, FlagSet resetFlags); } // namespace ctrlChn OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/mod_specifications.cpp0000644000175000017500000004113614163335272022454 00000000000000/* * mod_specifications.cpp * ---------------------- * Purpose: Mod specifications characterise the features of every editable module format in OpenMPT, such as the number of supported channels, samples, effects, etc... * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "mod_specifications.h" #include "../common/misc_util.h" #include OPENMPT_NAMESPACE_BEGIN namespace ModSpecs { constexpr CModSpecifications mptm_ = { /* TODO: Proper, less arbitrarily chosen values here. NOTE: If changing limits, see whether: -savefile format and GUI methods can handle new values(might not be a small task :). */ MOD_TYPE_MPT, // Internal MODTYPE value "mptm", // File extension NOTE_MIN, // Minimum note index NOTE_MAX, // Maximum note index 4000, // Pattern max. 4000, // Order max. MAX_SEQUENCES, // Sequences max 1, // Channel min 127, // Channel max 32, // Min tempo 1000, // Max tempo 1, // Min Speed 255, // Max Speed 1, // Min pattern rows 1024, // Max pattern rows 25, // Max mod name length 25, // Max sample name length 12, // Max sample filename length 25, // Max instrument name length 12, // Max instrument filename length 0, // Max comment line length 3999, // SamplesMax 255, // instrumentMax MixLevels::v1_17RC3, // defaultMixLevels SONG_LINEARSLIDES | SONG_EXFILTERRANGE | SONG_ITOLDEFFECTS | SONG_ITCOMPATGXX, // Supported song flags 200, // Max MIDI mapping directives MAX_ENVPOINTS, // Envelope point count true, // Has notecut. true, // Has noteoff. true, // Has notefade. true, // Has envelope release node true, // Has song comments true, // Has "+++" pattern true, // Has "---" pattern true, // Has restart position (order) true, // Supports plugins true, // Custom pattern time signatures true, // Pattern names true, // Has artist name true, // Has default resampling true, // Fixed point tempo " JFEGHLKRXODB?CQATI?SMNVW?UY?P?Z\\:#+*?????????", // Supported Effects " vpcdabuh??gfe?o", // Supported Volume Column commands }; constexpr CModSpecifications mod_ = { MOD_TYPE_MOD, // Internal MODTYPE value "mod", // File extension 25, // Minimum note index 108, // Maximum note index 128, // Pattern max. 128, // Order max. 1, // Only one order list 1, // Channel min 99, // Channel max 32, // Min tempo 255, // Max tempo 1, // Min Speed 31, // Max Speed 64, // Min pattern rows 64, // Max pattern rows 20, // Max mod name length 22, // Max sample name length 0, // Max sample filename length 0, // Max instrument name length 0, // Max instrument filename length 0, // Max comment line length 31, // SamplesMax 0, // instrumentMax MixLevels::Compatible, // defaultMixLevels SONG_PT_MODE | SONG_AMIGALIMITS | SONG_ISAMIGA, // Supported song flags 0, // Max MIDI mapping directives 0, // No instrument envelopes false, // No notecut. false, // No noteoff. false, // No notefade. false, // No envelope release node false, // No song comments false, // Doesn't have "+++" pattern false, // Doesn't have "---" pattern true, // Has restart position (order) false, // Doesn't support plugins false, // No custom pattern time signatures false, // No pattern names false, // Doesn't have artist name false, // Doesn't have default resampling false, // Integer tempo " 0123456789ABCD?FF?E??????????????????????????", // Supported Effects " ???????????????", // Supported Volume Column commands }; constexpr CModSpecifications xm_ = { MOD_TYPE_XM, // Internal MODTYPE value "xm", // File extension 13, // Minimum note index 108, // Maximum note index 256, // Pattern max. 255, // Order max. 1, // Only one order list 1, // Channel min 32, // Channel max 32, // Min tempo 1000, // Max tempo 1, // Min Speed 31, // Max Speed 1, // Min pattern rows 256, // Max pattern rows 20, // Max mod name length 22, // Max sample name length 0, // Max sample filename length 22, // Max instrument name length 0, // Max instrument filename length 0, // Max comment line length 128 * 16, // SamplesMax (actually 16 per instrument) 128, // instrumentMax MixLevels::CompatibleFT2, // defaultMixLevels SONG_LINEARSLIDES, // Supported song flags 0, // Max MIDI mapping directives 12, // Envelope point count false, // No notecut. true, // Has noteoff. false, // No notefade. false, // No envelope release node false, // No song comments false, // Doesn't have "+++" pattern false, // Doesn't have "---" pattern true, // Has restart position (order) false, // Doesn't support plugins false, // No custom pattern time signatures false, // No pattern names false, // Doesn't have artist name false, // Doesn't have default resampling false, // Integer tempo " 0123456789ABCDRFFTE???GHK??XPL??????W????????", // Supported Effects " vpcdabuhlrg????", // Supported Volume Column commands }; // XM with MPT extensions constexpr CModSpecifications xmEx_ = { MOD_TYPE_XM, // Internal MODTYPE value "xm", // File extension 13, // Minimum note index 108, // Maximum note index 256, // Pattern max. 255, // Order max. 1, // Only one order list 1, // Channel min 127, // Channel max 32, // Min tempo 1000, // Max tempo 1, // Min Speed 31, // Max Speed 1, // Min pattern rows 1024, // Max pattern rows 20, // Max mod name length 22, // Max sample name length 0, // Max sample filename length 22, // Max instrument name length 0, // Max instrument filename length 0, // Max comment line length MAX_SAMPLES - 1, // SamplesMax (actually 32 per instrument(256 * 32 = 8192), but limited to MAX_SAMPLES = 4000) 255, // instrumentMax MixLevels::CompatibleFT2, // defaultMixLevels SONG_LINEARSLIDES | SONG_EXFILTERRANGE, // Supported song flags 200, // Max MIDI mapping directives 12, // Envelope point count false, // No notecut. true, // Has noteoff. false, // No notefade. false, // No envelope release node true, // Has song comments false, // Doesn't have "+++" pattern false, // Doesn't have "---" pattern true, // Has restart position (order) true, // Supports plugins false, // No custom pattern time signatures true, // Pattern names true, // Has artist name false, // Doesn't have default resampling false, // Integer tempo " 0123456789ABCDRFFTE???GHK?YXPLZ\\?#??W????????", // Supported Effects " vpcdabuhlrg????", // Supported Volume Column commands }; constexpr CModSpecifications s3m_ = { MOD_TYPE_S3M, // Internal MODTYPE value "s3m", // File extension 13, // Minimum note index 108, // Maximum note index 100, // Pattern max. 255, // Order max. 1, // Only one order list 1, // Channel min 32, // Channel max 33, // Min tempo 255, // Max tempo 1, // Min Speed 255, // Max Speed 64, // Min pattern rows 64, // Max pattern rows 27, // Max mod name length 27, // Max sample name length 12, // Max sample filename length 0, // Max instrument name length 0, // Max instrument filename length 0, // Max comment line length 99, // SamplesMax 0, // instrumentMax MixLevels::Compatible, // defaultMixLevels SONG_FASTVOLSLIDES | SONG_AMIGALIMITS | SONG_S3MOLDVIBRATO, // Supported song flags 0, // Max MIDI mapping directives 0, // No instrument envelopes true, // Has notecut. false, // No noteoff. false, // No notefade. false, // No envelope release node false, // No song comments true, // Has "+++" pattern true, // Has "---" pattern false, // Doesn't have restart position (order) false, // Doesn't support plugins false, // No custom pattern time signatures false, // No pattern names false, // Doesn't have artist name false, // Doesn't have default resampling false, // Integer tempo " JFEGHLKRXODB?CQATI?SMNVW?U?????????? ????????", // Supported Effects " vp?????????????", // Supported Volume Column commands }; // S3M with MPT extensions constexpr CModSpecifications s3mEx_ = { MOD_TYPE_S3M, // Internal MODTYPE value "s3m", // File extension 13, // Minimum note index 108, // Maximum note index 100, // Pattern max. 255, // Order max. 1, // Only one order list 1, // Channel min 32, // Channel max 33, // Min tempo 255, // Max tempo 1, // Min Speed 255, // Max Speed 64, // Min pattern rows 64, // Max pattern rows 27, // Max mod name length 27, // Max sample name length 12, // Max sample filename length 0, // Max instrument name length 0, // Max instrument filename length 0, // Max comment line length 99, // SamplesMax 0, // instrumentMax MixLevels::Compatible, // defaultMixLevels SONG_FASTVOLSLIDES | SONG_AMIGALIMITS, // Supported song flags 0, // Max MIDI mapping directives 0, // No instrument envelopes true, // Has notecut. false, // No noteoff. false, // No notefade. false, // No envelope release node false, // No song comments true, // Has "+++" pattern true, // Has "---" pattern false, // Doesn't have restart position (order) false, // Doesn't support plugins false, // No custom pattern time signatures false, // No pattern names false, // Doesn't have artist name false, // Doesn't have default resampling false, // Integer tempo " JFEGHLKRXODB?CQATI?SMNVW?UY?P?Z????? ????????", // Supported Effects " vp?????????????", // Supported Volume Column commands }; constexpr CModSpecifications it_ = { MOD_TYPE_IT, // Internal MODTYPE value "it", // File extension 1, // Minimum note index 120, // Maximum note index 200, // Pattern max. 256, // Order max. 1, // Only one order list 1, // Channel min 64, // Channel max 32, // Min tempo 255, // Max tempo 1, // Min Speed 255, // Max Speed 1, // Min pattern rows 200, // Max pattern rows 25, // Max mod name length 25, // Max sample name length 12, // Max sample filename length 25, // Max instrument name length 12, // Max instrument filename length 75, // Max comment line length 99, // SamplesMax 99, // instrumentMax MixLevels::Compatible, // defaultMixLevels SONG_LINEARSLIDES | SONG_ITOLDEFFECTS | SONG_ITCOMPATGXX, // Supported song flags 0, // Max MIDI mapping directives 25, // Envelope point count true, // Has notecut. true, // Has noteoff. true, // Has notefade. false, // No envelope release node true, // Has song comments true, // Has "+++" pattern true, // Has "--" pattern false, // Doesn't have restart position (order) false, // Doesn't support plugins false, // No custom pattern time signatures false, // No pattern names false, // Doesn't have artist name false, // Doesn't have default resampling false, // Integer tempo " JFEGHLKRXODB?CQATI?SMNVW?UY?P?Z????? ????????", // Supported Effects " vpcdab?h??gfe??", // Supported Volume Column commands }; constexpr CModSpecifications itEx_ = { MOD_TYPE_IT, // Internal MODTYPE value "it", // File extension 1, // Minimum note index 120, // Maximum note index 240, // Pattern max. 256, // Order max. 1, // Only one order list 1, // Channel min 127, // Channel max 32, // Min tempo 512, // Max tempo 1, // Min Speed 255, // Max Speed 1, // Min pattern rows 1024, // Max pattern rows 25, // Max mod name length 25, // Max sample name length 12, // Max sample filename length 25, // Max instrument name length 12, // Max instrument filename length 75, // Max comment line length 3999, // SamplesMax 255, // instrumentMax MixLevels::Compatible, // defaultMixLevels SONG_LINEARSLIDES | SONG_EXFILTERRANGE | SONG_ITOLDEFFECTS | SONG_ITCOMPATGXX, // Supported song flags 200, // Max MIDI mapping directives 25, // Envelope point count true, // Has notecut. true, // Has noteoff. true, // Has notefade. false, // No envelope release node true, // Has song comments true, // Has "+++" pattern true, // Has "---" pattern false, // Doesn't have restart position (order) true, // Supports plugins false, // No custom pattern time signatures true, // Pattern names true, // Has artist name false, // Doesn't have default resampling false, // Integer tempo " JFEGHLKRXODB?CQATI?SMNVW?UY?P?Z\\?#?? ????????", // Supported Effects " vpcdab?h??gfe??", // Supported Volume Column commands }; const std::array Collection = { &mptm_, &mod_, &s3m_, &s3mEx_, &xm_, &xmEx_, &it_, &itEx_ }; const CModSpecifications &mptm = mptm_; const CModSpecifications &mod = mod_; const CModSpecifications &s3m = s3m_; const CModSpecifications &s3mEx = s3mEx_; const CModSpecifications &xm = xm_; const CModSpecifications &xmEx = xmEx_; const CModSpecifications &it = it_; const CModSpecifications &itEx = itEx_; } // namespace ModSpecs MODTYPE CModSpecifications::ExtensionToType(std::string ext) { if(ext.empty()) { return MOD_TYPE_NONE; } else if(ext[0] == '.') { ext.erase(0, 1); } ext = mpt::ToLowerCaseAscii(ext); for(const auto &spec : ModSpecs::Collection) { if(ext == spec->fileExtension) { return spec->internalType; } } return MOD_TYPE_NONE; } bool CModSpecifications::HasNote(ModCommand::NOTE note) const { if(note >= noteMin && note <= noteMax) return true; else if(ModCommand::IsSpecialNote(note)) { if(note == NOTE_NOTECUT) return hasNoteCut; else if(note == NOTE_KEYOFF) return hasNoteOff; else if(note == NOTE_FADE) return hasNoteFade; else return (internalType == MOD_TYPE_MPT); } else if(note == NOTE_NONE) return true; return false; } bool CModSpecifications::HasVolCommand(ModCommand::VOLCMD volcmd) const { if(volcmd >= MAX_VOLCMDS) return false; if(volcommands[volcmd] == '?') return false; return true; } bool CModSpecifications::HasCommand(ModCommand::COMMAND cmd) const { if(cmd >= MAX_EFFECTS) return false; if(commands[cmd] == '?') return false; return true; } char CModSpecifications::GetVolEffectLetter(ModCommand::VOLCMD volcmd) const { if(volcmd >= MAX_VOLCMDS) return '?'; return volcommands[volcmd]; } char CModSpecifications::GetEffectLetter(ModCommand::COMMAND cmd) const { if(cmd >= MAX_EFFECTS) return '?'; return commands[cmd]; } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/mod_specifications.h0000644000175000017500000001125214052666041022113 00000000000000/* * mod_specifications.h * -------------------- * Purpose: Mod specifications characterise the features of every editable module format in OpenMPT, such as the number of supported channels, samples, effects, etc... * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "Snd_defs.h" #include "modcommand.h" // ModCommand:: #include "../soundlib/SoundFilePlayConfig.h" // mixlevel constants. OPENMPT_NAMESPACE_BEGIN struct CModSpecifications { /// Returns modtype corresponding to given file extension. The extension string /// may begin with or without dot, e.g. both ".it" and "it" will be handled correctly. static MODTYPE ExtensionToType(std::string ext); // (encoded in UTF8) // Return true if format supports given note. bool HasNote(ModCommand::NOTE note) const; bool HasVolCommand(ModCommand::VOLCMD volcmd) const; bool HasCommand(ModCommand::COMMAND cmd) const; // Return corresponding effect letter for this format char GetEffectLetter(ModCommand::COMMAND cmd) const; char GetVolEffectLetter(ModCommand::VOLCMD cmd) const; // NOTE: If changing order, update all initializations in .cpp file. MODTYPE internalType; // Internal MODTYPE value const char *fileExtension; // File extension without dot (encoded in UTF8). ModCommand::NOTE noteMin; // Minimum note index (index starts from 1) ModCommand::NOTE noteMax; // Maximum note index (index starts from 1) PATTERNINDEX patternsMax; ORDERINDEX ordersMax; SEQUENCEINDEX sequencesMax; CHANNELINDEX channelsMin; // Minimum number of editable channels in pattern. CHANNELINDEX channelsMax; // Maximum number of editable channels in pattern. uint32 tempoMinInt; uint32 tempoMaxInt; uint32 speedMin; // Minimum ticks per frame uint32 speedMax; // Maximum ticks per frame ROWINDEX patternRowsMin; ROWINDEX patternRowsMax; uint16 modNameLengthMax; // Meaning 'usable letters', possible null character is not included. uint16 sampleNameLengthMax; // Ditto uint16 sampleFilenameLengthMax; // Ditto uint16 instrNameLengthMax; // Ditto uint16 instrFilenameLengthMax; // Ditto uint16 commentLineLengthMax; // not including line break, 0 for unlimited SAMPLEINDEX samplesMax; // Max number of samples == Highest possible sample index INSTRUMENTINDEX instrumentsMax; // Max number of instruments == Highest possible instrument index MixLevels defaultMixLevels; // Default mix levels that are used when creating a new file in this format FlagSet songFlags; // Supported song flags uint8 MIDIMappingDirectivesMax; // Number of MIDI Mapping directives that the format can store (0 = none) uint8 envelopePointsMax; // Maximum number of points of each envelope bool hasNoteCut; // True if format has note cut (^^). bool hasNoteOff; // True if format has note off (==). bool hasNoteFade; // True if format has note fade (~~). bool hasReleaseNode; // Envelope release node bool hasComments; // True if format has a comments field bool hasIgnoreIndex; // Does "+++" pattern exist? bool hasStopIndex; // Does "---" pattern exist? bool hasRestartPos; // Format has an automatic restart order position bool supportsPlugins; // Format can store plugins bool hasPatternSignatures; // Can patterns have a custom time signature? bool hasPatternNames; // Can patterns have a name? bool hasArtistName; // Can artist name be stored in file? bool hasDefaultResampling; // Can default resampling be saved? (if not, it can still be modified in the GUI but won't set the module as modified) bool hasFractionalTempo; // Are fractional tempos allowed? const char *commands; // An array holding all commands this format supports; commands that are not supported are marked with "?" const char *volcommands; // Ditto, but for volume column MPT_CONSTEXPRINLINE TEMPO GetTempoMin() const { return TEMPO(tempoMinInt, 0); } MPT_CONSTEXPRINLINE TEMPO GetTempoMax() const { return TEMPO(tempoMaxInt, 0); } }; namespace ModSpecs { extern const CModSpecifications & mptm; extern const CModSpecifications & mod; extern const CModSpecifications & s3m; extern const CModSpecifications & s3mEx; extern const CModSpecifications & xm; extern const CModSpecifications & xmEx; extern const CModSpecifications & it; extern const CModSpecifications & itEx; extern const std::array Collection; } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/MPEGFrame.cpp0000644000175000017500000000645713600355210020311 00000000000000/* * MPEGFrame.cpp * ------------- * Purpose: Basic MPEG frame parsing functionality * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "MPEGFrame.h" #include "../common/FileReader.h" OPENMPT_NAMESPACE_BEGIN // Samples per frame - for each MPEG version and all three layers static constexpr uint16 samplesPerFrame[2][3] = { { 384, 1152, 1152 }, // MPEG 1 { 384, 1152, 576 } // MPEG 2 / 2.5 }; // Bit rates for each MPEG version and all three layers static constexpr uint16 bitRates[2][3][15] = { // MPEG 1 { { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448 }, // Layer 1 { 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384 }, // Layer 2 { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320 } // Layer 3 }, // MPEG 2 / 2.5 { { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256 }, // Layer 1 { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160 }, // Layer 2 { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160 } // Layer 3 } }; // Sampling rates for each MPEG version and all three layers static constexpr uint16 samplingRates[4][3] = { { 11025, 12000, 8000 }, // MPEG 2.5 { 0, 0, 0 }, // Invalid { 22050, 24000, 16000 }, // MPEG 2 { 44100, 48000, 32000 } // MPEG 1 }; // Samples per Frame / 8 static constexpr uint8 mpegCoefficients[2][3] = { { 12, 144, 144 }, // MPEG 1 { 12, 144, 72 } // MPEG 2 / 2.5 }; // Side info size = Offset in frame where Xing/Info magic starts static constexpr uint8 sideInfoSize[2][2] = { { 17, 32 }, // MPEG 1 { 9, 17 } // MPEG 2 / 2.5 }; bool MPEGFrame::IsMPEGHeader(const uint8 (&header)[3]) { return header[0] == 0xFF && (header[1] & 0xE0) == 0xE0 // Sync && (header[1] & 0x18) != 0x08 // Invalid MPEG version && (header[1] & 0x06) != 0x00 // Invalid MPEG layer && (header[2] & 0x0C) != 0x0C // Invalid frequency && (header[2] & 0xF0) != 0xF0; // Invalid bitrate } MPEGFrame::MPEGFrame(FileReader &file) : frameSize(0) , numSamples(0) , isValid(false) , isLAME(false) { uint8 header[4]; file.ReadArray(header); if(!IsMPEGHeader(reinterpret_cast(header))) return; uint8 version = (header[1] & 0x18) >> 3; uint8 mpeg1 = (version == 3) ? 0 : 1; uint8 layer = 3 - ((header[1] & 0x06) >> 1); uint8 bitRate = (header[2] & 0xF0) >> 4; uint8 sampleRate = (header[2] & 0x0C) >> 2; uint8 padding = (header[2] & 0x02) >> 1; bool stereo = ((header[3] & 0xC0) >> 6) != 3; isValid = true; frameSize = (((mpegCoefficients[mpeg1][layer] * (bitRates[mpeg1][layer][bitRate] * 1000) / samplingRates[version][sampleRate]) + padding)) * (layer == 0 ? 4 : 1); numSamples = samplesPerFrame[mpeg1][layer]; if(stereo) numSamples *= 2u; uint32 lameOffset = sideInfoSize[mpeg1][stereo ? 1 : 0]; if(frameSize < lameOffset + 8) return; uint8 frame[36]; file.ReadStructPartial(frame, lameOffset + 4); // Don't check first two bytes, might be CRC for(uint32 i = 2; i < lameOffset; i++) { if(frame[i] != 0) return; } // This is all we really need to know for our purposes in the MO3 decoder. isLAME = !memcmp(frame + lameOffset, "Info", 4) || !memcmp(frame + lameOffset, "Xing", 4); } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/MPEGFrame.h0000644000175000017500000000131314052666041017751 00000000000000/* * MPEGFrame.h * ----------- * Purpose: Basic MPEG frame parsing functionality * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "../common/FileReaderFwd.h" OPENMPT_NAMESPACE_BEGIN class MPEGFrame { public: uint16 frameSize; // Complete frame size in bytes uint16 numSamples; // Number of samples in this frame (multiplied by number of channels) bool isValid; // Is a valid frame at all bool isLAME; // Has Xing/LAME header MPEGFrame(FileReader &file); static bool IsMPEGHeader(const uint8 (&header)[3]); }; OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/OggStream.cpp0000644000175000017500000001067114056354456020510 00000000000000/* * OggStream.cpp * ------------- * Purpose: Basic Ogg stream parsing functionality * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "OggStream.h" #include "mpt/crc/crc.hpp" #include "../common/FileReader.h" OPENMPT_NAMESPACE_BEGIN namespace Ogg { uint16 PageInfo::GetPagePhysicalSize() const { uint16 size = 0; size += sizeof(PageHeader); size += header.page_segments; for(uint8 segment = 0; segment < header.page_segments; ++segment) { size += segment_table[segment]; } return size; } uint16 PageInfo::GetPageHeaderSize() const { uint16 size = 0; size += sizeof(PageHeader); size += header.page_segments; return size; } uint16 PageInfo::GetPageDataSize() const { uint16 size = 0; for(uint8 segment = 0; segment < header.page_segments; ++segment) { size += segment_table[segment]; } return size; } bool AdvanceToPageMagic(FileReader &file) { #if MPT_COMPILER_MSVC #pragma warning(push) #pragma warning(disable:4127) // conditional expression is constant #endif // MPT_COMPILER_MSVC while(true) #if MPT_COMPILER_MSVC #pragma warning(pop) #endif // MPT_COMPILER_MSVC { if(!file.CanRead(4)) { return false; } if(file.ReadMagic("OggS")) { file.SkipBack(4); return true; } file.Skip(1); } } bool ReadPage(FileReader &file, PageInfo &pageInfo, std::vector *pageData) { pageInfo = PageInfo(); if(pageData) { (*pageData).clear(); } if(!file.ReadMagic("OggS")) { return false; } file.SkipBack(4); FileReader filePageReader = file; // do not modify original file read position if(!filePageReader.ReadStruct(pageInfo.header)) { return false; } if(!filePageReader.CanRead(pageInfo.header.page_segments)) { return false; } uint16 pageDataSize = 0; for(uint8 segment = 0; segment < pageInfo.header.page_segments; ++segment) { pageInfo.segment_table[segment] = filePageReader.ReadIntLE(); pageDataSize += pageInfo.segment_table[segment]; } if(!filePageReader.CanRead(pageDataSize)) { return false; } if(pageData) { filePageReader.ReadVector(*pageData, pageDataSize); } else { filePageReader.Skip(pageDataSize); } filePageReader.SkipBack(pageInfo.GetPagePhysicalSize()); { mpt::crc32_ogg calculatedCRC; uint8 rawHeader[sizeof(PageHeader)]; MemsetZero(rawHeader); filePageReader.ReadArray(rawHeader); std::memset(rawHeader + 22, 0, 4); // clear out old crc calculatedCRC.process(rawHeader, rawHeader + sizeof(rawHeader)); filePageReader.Skip(pageInfo.header.page_segments); calculatedCRC.process(pageInfo.segment_table, pageInfo.segment_table + pageInfo.header.page_segments); if(pageData) { filePageReader.Skip(pageDataSize); calculatedCRC.process(*pageData); } else { FileReader pageDataReader = filePageReader.ReadChunk(pageDataSize); auto pageDataView = pageDataReader.GetPinnedView(); calculatedCRC.process(pageDataView.GetSpan()); } if(calculatedCRC != pageInfo.header.CRC_checksum) { return false; } } file.Skip(pageInfo.GetPagePhysicalSize()); return true; } bool ReadPage(FileReader &file, PageInfo &pageInfo, std::vector &pageData) { return ReadPage(file, pageInfo, &pageData); } bool ReadPage(FileReader &file) { PageInfo pageInfo; return ReadPage(file, pageInfo); } bool ReadPageAndSkipJunk(FileReader &file, PageInfo &pageInfo, std::vector &pageData) { pageInfo = PageInfo(); pageData.clear(); #if MPT_COMPILER_MSVC #pragma warning(push) #pragma warning(disable:4127) // conditional expression is constant #endif // MPT_COMPILER_MSVC while(true) #if MPT_COMPILER_MSVC #pragma warning(pop) #endif // MPT_COMPILER_MSVC { if(!AdvanceToPageMagic(file)) { return false; } if(ReadPage(file, pageInfo, pageData)) { return true; } else { pageInfo = PageInfo(); pageData.clear(); } file.Skip(1); } } bool UpdatePageCRC(PageInfo &pageInfo, const std::vector &pageData) { if(pageData.size() != pageInfo.GetPageDataSize()) { return false; } mpt::crc32_ogg crc; pageInfo.header.CRC_checksum = 0; char rawHeader[sizeof(PageHeader)]; std::memcpy(rawHeader, &pageInfo.header, sizeof(PageHeader)); crc.process(rawHeader, rawHeader + sizeof(PageHeader)); crc.process(pageInfo.segment_table, pageInfo.segment_table + pageInfo.header.page_segments); crc.process(pageData); pageInfo.header.CRC_checksum = crc; return true; } } // namespace Ogg OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/OggStream.h0000644000175000017500000000346514056354456020160 00000000000000/* * OggStream.h * ----------- * Purpose: Basic Ogg stream parsing functionality * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "openmpt/base/Endian.hpp" #include "mpt/io/io.hpp" #include "../common/FileReaderFwd.h" OPENMPT_NAMESPACE_BEGIN namespace Ogg { struct PageHeader { char capture_pattern[4]; // "OggS" uint8le version; uint8le header_type; uint64le granule_position; uint32le bitstream_serial_number; uint32le page_sequence_number; uint32le CRC_checksum; uint8le page_segments; }; MPT_BINARY_STRUCT(PageHeader, 27) struct PageInfo { PageHeader header; uint8 segment_table[255]; PageInfo() { MemsetZero(header); MemsetZero(segment_table); } uint16 GetPagePhysicalSize() const; uint16 GetPageHeaderSize() const; uint16 GetPageDataSize() const; }; // returns false on EOF bool AdvanceToPageMagic(FileReader &file); bool ReadPage(FileReader &file, PageInfo &pageInfo, std::vector *pageData = nullptr); bool ReadPage(FileReader &file, PageInfo &pageInfo, std::vector &pageData); bool ReadPage(FileReader &file); bool ReadPageAndSkipJunk(FileReader &file, PageInfo &pageInfo, std::vector &pageData); bool UpdatePageCRC(PageInfo &pageInfo, const std::vector &pageData); template bool WritePage(Tfile & f, const PageInfo &pageInfo, const std::vector &pageData) { if(!mpt::IO::Write(f, pageInfo.header)) { return false; } if(!mpt::IO::WriteRaw(f, pageInfo.segment_table, pageInfo.header.page_segments)) { return false; } if(!mpt::IO::WriteRaw(f, pageData.data(), pageData.size())) { return false; } return true; } } // namespace Ogg OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/opal.h0000644000175000017500000013572514124131076017213 00000000000000// This is the Opal OPL3 emulator from Reality Adlib Tracker v2.0a (http://www.3eality.com/productions/reality-adlib-tracker). // It was released by Shayde/Reality into the public domain. // Minor modifications to silence some warnings and fix a bug in the envelope generator have been applied. // Additional fixes by JP Cimalando. /* The Opal OPL3 emulator. Note: this is not a complete emulator, just enough for Reality Adlib Tracker tunes. Missing features compared to a real OPL3: - Timers/interrupts - OPL3 enable bit (it defaults to always on) - CSW mode - Test register - Percussion mode */ #include //================================================================================================== // Opal class. //================================================================================================== class Opal { class Channel; // Various constants enum { OPL3SampleRate = 49716, NumChannels = 18, NumOperators = 36, EnvOff = -1, EnvAtt, EnvDec, EnvSus, EnvRel, }; // A single FM operator class Operator { public: Operator(); void SetMaster(Opal *opal) { Master = opal; } void SetChannel(Channel *chan) { Chan = chan; } int16_t Output(uint16_t keyscalenum, uint32_t phase_step, int16_t vibrato, int16_t mod = 0, int16_t fbshift = 0); void SetKeyOn(bool on); void SetTremoloEnable(bool on); void SetVibratoEnable(bool on); void SetSustainMode(bool on); void SetEnvelopeScaling(bool on); void SetFrequencyMultiplier(uint16_t scale); void SetKeyScale(uint16_t scale); void SetOutputLevel(uint16_t level); void SetAttackRate(uint16_t rate); void SetDecayRate(uint16_t rate); void SetSustainLevel(uint16_t level); void SetReleaseRate(uint16_t rate); void SetWaveform(uint16_t wave); void ComputeRates(); void ComputeKeyScaleLevel(); protected: Opal * Master; // Master object Channel * Chan; // Owning channel uint32_t Phase; // The current offset in the selected waveform uint16_t Waveform; // The waveform id this operator is using uint16_t FreqMultTimes2; // Frequency multiplier * 2 int EnvelopeStage; // Which stage the envelope is at (see Env* enums above) int16_t EnvelopeLevel; // 0 - $1FF, 0 being the loudest uint16_t OutputLevel; // 0 - $FF uint16_t AttackRate; uint16_t DecayRate; uint16_t SustainLevel; uint16_t ReleaseRate; uint16_t AttackShift; uint16_t AttackMask; uint16_t AttackAdd; const uint16_t *AttackTab; uint16_t DecayShift; uint16_t DecayMask; uint16_t DecayAdd; const uint16_t *DecayTab; uint16_t ReleaseShift; uint16_t ReleaseMask; uint16_t ReleaseAdd; const uint16_t *ReleaseTab; uint16_t KeyScaleShift; uint16_t KeyScaleLevel; int16_t Out[2]; bool KeyOn; bool KeyScaleRate; // Affects envelope rate scaling bool SustainMode; // Whether to sustain during the sustain phase, or release instead bool TremoloEnable; bool VibratoEnable; }; // A single channel, which can contain two or more operators class Channel { public: Channel(); void SetMaster(Opal *opal) { Master = opal; } void SetOperators(Operator *a, Operator *b, Operator *c, Operator *d) { Op[0] = a; Op[1] = b; Op[2] = c; Op[3] = d; if (a) a->SetChannel(this); if (b) b->SetChannel(this); if (c) c->SetChannel(this); if (d) d->SetChannel(this); } void Output(int16_t &left, int16_t &right); void SetEnable(bool on) { Enable = on; } void SetChannelPair(Channel *pair) { ChannelPair = pair; } void SetFrequencyLow(uint16_t freq); void SetFrequencyHigh(uint16_t freq); void SetKeyOn(bool on); void SetOctave(uint16_t oct); void SetLeftEnable(bool on); void SetRightEnable(bool on); void SetFeedback(uint16_t val); void SetModulationType(uint16_t type); uint16_t GetFreq() const { return Freq; } uint16_t GetOctave() const { return Octave; } uint16_t GetKeyScaleNumber() const { return KeyScaleNumber; } uint16_t GetModulationType() const { return ModulationType; } Channel * GetChannelPair() const { return ChannelPair; } void ComputeKeyScaleNumber(); protected: void ComputePhaseStep(); Operator * Op[4]; Opal * Master; // Master object uint16_t Freq; // Frequency; actually it's a phase stepping value uint16_t Octave; // Also known as "block" in Yamaha parlance uint32_t PhaseStep; uint16_t KeyScaleNumber; uint16_t FeedbackShift; uint16_t ModulationType; Channel * ChannelPair; bool Enable; bool LeftEnable, RightEnable; }; public: Opal(int sample_rate); Opal(const Opal &) = delete; Opal(Opal &&) = delete; ~Opal(); void SetSampleRate(int sample_rate); void Port(uint16_t reg_num, uint8_t val); void Sample(int16_t *left, int16_t *right); protected: void Init(int sample_rate); void Output(int16_t &left, int16_t &right); int32_t SampleRate; int32_t SampleAccum; int16_t LastOutput[2], CurrOutput[2]; Channel Chan[NumChannels]; Operator Op[NumOperators]; // uint16_t ExpTable[256]; // uint16_t LogSinTable[256]; uint16_t Clock; uint16_t TremoloClock; uint16_t TremoloLevel; uint16_t VibratoTick; uint16_t VibratoClock; bool NoteSel; bool TremoloDepth; bool VibratoDepth; static const uint16_t RateTables[4][8]; static const uint16_t ExpTable[256]; static const uint16_t LogSinTable[256]; }; //-------------------------------------------------------------------------------------------------- const uint16_t Opal::RateTables[4][8] = { { 1, 0, 1, 0, 1, 0, 1, 0 }, { 1, 0, 1, 0, 0, 0, 1, 0 }, { 1, 0, 0, 0, 1, 0, 0, 0 }, { 1, 0, 0, 0, 0, 0, 0, 0 }, }; //-------------------------------------------------------------------------------------------------- const uint16_t Opal::ExpTable[0x100] = { 1018, 1013, 1007, 1002, 996, 991, 986, 980, 975, 969, 964, 959, 953, 948, 942, 937, 932, 927, 921, 916, 911, 906, 900, 895, 890, 885, 880, 874, 869, 864, 859, 854, 849, 844, 839, 834, 829, 824, 819, 814, 809, 804, 799, 794, 789, 784, 779, 774, 770, 765, 760, 755, 750, 745, 741, 736, 731, 726, 722, 717, 712, 708, 703, 698, 693, 689, 684, 680, 675, 670, 666, 661, 657, 652, 648, 643, 639, 634, 630, 625, 621, 616, 612, 607, 603, 599, 594, 590, 585, 581, 577, 572, 568, 564, 560, 555, 551, 547, 542, 538, 534, 530, 526, 521, 517, 513, 509, 505, 501, 496, 492, 488, 484, 480, 476, 472, 468, 464, 460, 456, 452, 448, 444, 440, 436, 432, 428, 424, 420, 416, 412, 409, 405, 401, 397, 393, 389, 385, 382, 378, 374, 370, 367, 363, 359, 355, 352, 348, 344, 340, 337, 333, 329, 326, 322, 318, 315, 311, 308, 304, 300, 297, 293, 290, 286, 283, 279, 276, 272, 268, 265, 262, 258, 255, 251, 248, 244, 241, 237, 234, 231, 227, 224, 220, 217, 214, 210, 207, 204, 200, 197, 194, 190, 187, 184, 181, 177, 174, 171, 168, 164, 161, 158, 155, 152, 148, 145, 142, 139, 136, 133, 130, 126, 123, 120, 117, 114, 111, 108, 105, 102, 99, 96, 93, 90, 87, 84, 81, 78, 75, 72, 69, 66, 63, 60, 57, 54, 51, 48, 45, 42, 40, 37, 34, 31, 28, 25, 22, 20, 17, 14, 11, 8, 6, 3, 0, }; //-------------------------------------------------------------------------------------------------- const uint16_t Opal::LogSinTable[0x100] = { 2137, 1731, 1543, 1419, 1326, 1252, 1190, 1137, 1091, 1050, 1013, 979, 949, 920, 894, 869, 846, 825, 804, 785, 767, 749, 732, 717, 701, 687, 672, 659, 646, 633, 621, 609, 598, 587, 576, 566, 556, 546, 536, 527, 518, 509, 501, 492, 484, 476, 468, 461, 453, 446, 439, 432, 425, 418, 411, 405, 399, 392, 386, 380, 375, 369, 363, 358, 352, 347, 341, 336, 331, 326, 321, 316, 311, 307, 302, 297, 293, 289, 284, 280, 276, 271, 267, 263, 259, 255, 251, 248, 244, 240, 236, 233, 229, 226, 222, 219, 215, 212, 209, 205, 202, 199, 196, 193, 190, 187, 184, 181, 178, 175, 172, 169, 167, 164, 161, 159, 156, 153, 151, 148, 146, 143, 141, 138, 136, 134, 131, 129, 127, 125, 122, 120, 118, 116, 114, 112, 110, 108, 106, 104, 102, 100, 98, 96, 94, 92, 91, 89, 87, 85, 83, 82, 80, 78, 77, 75, 74, 72, 70, 69, 67, 66, 64, 63, 62, 60, 59, 57, 56, 55, 53, 52, 51, 49, 48, 47, 46, 45, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 23, 22, 21, 20, 20, 19, 18, 17, 17, 16, 15, 15, 14, 13, 13, 12, 12, 11, 10, 10, 9, 9, 8, 8, 7, 7, 7, 6, 6, 5, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, }; //================================================================================================== // This is the temporary code for generating the above tables. Maths and data from this nice // reverse-engineering effort: // // https://docs.google.com/document/d/18IGx18NQY_Q1PJVZ-bHywao9bhsDoAqoIn1rIm42nwo/edit //================================================================================================== #if 0 #include void GenerateTables() { // Build the exponentiation table (reversed from the official OPL3 ROM) FILE *fd = fopen("exptab.txt", "wb"); if (fd) { for (int i = 0; i < 0x100; i++) { int v = (pow(2, (0xFF - i) / 256.0) - 1) * 1024 + 0.5; if (i & 15) fprintf(fd, " %4d,", v); else fprintf(fd, "\n\t%4d,", v); } fclose(fd); } // Build the log-sin table fd = fopen("sintab.txt", "wb"); if (fd) { for (int i = 0; i < 0x100; i++) { int v = -log(sin((i + 0.5) * 3.1415926535897933 / 256 / 2)) / log(2) * 256 + 0.5; if (i & 15) fprintf(fd, " %4d,", v); else fprintf(fd, "\n\t%4d,", v); } fclose(fd); } } #endif //================================================================================================== // Constructor/destructor. //================================================================================================== Opal::Opal(int sample_rate) { Init(sample_rate); } //-------------------------------------------------------------------------------------------------- Opal::~Opal() { } //================================================================================================== // Initialise the emulation. //================================================================================================== void Opal::Init(int sample_rate) { Clock = 0; TremoloClock = 0; TremoloLevel = 0; VibratoTick = 0; VibratoClock = 0; NoteSel = false; TremoloDepth = false; VibratoDepth = false; // // Build the exponentiation table (reversed from the official OPL3 ROM) // for (int i = 0; i < 0x100; i++) // ExpTable[i] = (pow(2, (0xFF - i) / 256.0) - 1) * 1024 + 0.5; // // // Build the log-sin table // for (int i = 0; i < 0x100; i++) // LogSinTable[i] = -log(sin((i + 0.5) * 3.1415926535897933 / 256 / 2)) / log(2) * 256 + 0.5; // Let sub-objects know where to find us for (int i = 0; i < NumOperators; i++) Op[i].SetMaster(this); for (int i = 0; i < NumChannels; i++) Chan[i].SetMaster(this); // Add the operators to the channels. Note, some channels can't use all the operators // FIXME: put this into a separate routine const int chan_ops[] = { 0, 1, 2, 6, 7, 8, 12, 13, 14, 18, 19, 20, 24, 25, 26, 30, 31, 32, }; for (int i = 0; i < NumChannels; i++) { Channel *chan = &Chan[i]; int op = chan_ops[i]; if (i < 3 || (i >= 9 && i < 12)) chan->SetOperators(&Op[op], &Op[op + 3], &Op[op + 6], &Op[op + 9]); else chan->SetOperators(&Op[op], &Op[op + 3], 0, 0); } // Initialise the operator rate data. We can't do this in the Operator constructor as it // relies on referencing the master and channel objects for (int i = 0; i < NumOperators; i++) Op[i].ComputeRates(); SetSampleRate(sample_rate); } //================================================================================================== // Change the sample rate. //================================================================================================== void Opal::SetSampleRate(int sample_rate) { // Sanity if (sample_rate == 0) sample_rate = OPL3SampleRate; SampleRate = sample_rate; SampleAccum = 0; LastOutput[0] = LastOutput[1] = 0; CurrOutput[0] = CurrOutput[1] = 0; } //================================================================================================== // Write a value to an OPL3 register. //================================================================================================== void Opal::Port(uint16_t reg_num, uint8_t val) { static constexpr int8_t op_lookup[] = { // 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 0, 1, 2, 3, 4, 5, -1, -1, 6, 7, 8, 9, 10, 11, -1, -1, // 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 12, 13, 14, 15, 16, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, }; uint16_t type = reg_num & 0xE0; // Is it BD, the one-off register stuck in the middle of the register array? if (reg_num == 0xBD) { TremoloDepth = (val & 0x80) != 0; VibratoDepth = (val & 0x40) != 0; return; } // Global registers if (type == 0x00) { // 4-OP enables if (reg_num == 0x104) { // Enable/disable channels based on which 4-op enables uint8_t mask = 1; for (int i = 0; i < 6; i++, mask <<= 1) { // The 4-op channels are 0, 1, 2, 9, 10, 11 uint16_t chan = static_cast(i < 3 ? i : i + 6); Channel *primary = &Chan[chan]; Channel *secondary = &Chan[chan + 3]; if (val & mask) { // Let primary channel know it's controlling the secondary channel primary->SetChannelPair(secondary); // Turn off the second channel in the pair secondary->SetEnable(false); } else { // Let primary channel know it's no longer controlling the secondary channel primary->SetChannelPair(0); // Turn on the second channel in the pair secondary->SetEnable(true); } } // CSW / Note-sel } else if (reg_num == 0x08) { NoteSel = (val & 0x40) != 0; // Get the channels to recompute the Key Scale No. as this varies based on NoteSel for (int i = 0; i < NumChannels; i++) Chan[i].ComputeKeyScaleNumber(); } // Channel registers } else if (type >= 0xA0 && type <= 0xC0) { // Convert to channel number int chan_num = reg_num & 15; // Valid channel? if (chan_num >= 9) return; // Is it the other bank of channels? if (reg_num & 0x100) chan_num += 9; Channel &chan = Chan[chan_num]; // Registers Ax and Bx affect both channels Channel *chans[2] = {&chan, chan.GetChannelPair()}; int numchans = chans[1] ? 2 : 1; // Do specific registers switch (reg_num & 0xF0) { // Frequency low case 0xA0: { for (int i = 0; i < numchans; i++) { chans[i]->SetFrequencyLow(val); } break; } // Key-on / Octave / Frequency High case 0xB0: { for (int i = 0; i < numchans; i++) { chans[i]->SetKeyOn((val & 0x20) != 0); chans[i]->SetOctave(val >> 2 & 7); chans[i]->SetFrequencyHigh(val & 3); } break; } // Right Stereo Channel Enable / Left Stereo Channel Enable / Feedback Factor / Modulation Type case 0xC0: { chan.SetRightEnable((val & 0x20) != 0); chan.SetLeftEnable((val & 0x10) != 0); chan.SetFeedback(val >> 1 & 7); chan.SetModulationType(val & 1); break; } } // Operator registers } else if ((type >= 0x20 && type <= 0x80) || type == 0xE0) { // Convert to operator number int op_num = op_lookup[reg_num & 0x1F]; // Valid register? if (op_num < 0) return; // Is it the other bank of operators? if (reg_num & 0x100) op_num += 18; Operator &op = Op[op_num]; // Do specific registers switch (type) { // Tremolo Enable / Vibrato Enable / Sustain Mode / Envelope Scaling / Frequency Multiplier case 0x20: { op.SetTremoloEnable((val & 0x80) != 0); op.SetVibratoEnable((val & 0x40) != 0); op.SetSustainMode((val & 0x20) != 0); op.SetEnvelopeScaling((val & 0x10) != 0); op.SetFrequencyMultiplier(val & 15); break; } // Key Scale / Output Level case 0x40: { op.SetKeyScale(val >> 6); op.SetOutputLevel(val & 0x3F); break; } // Attack Rate / Decay Rate case 0x60: { op.SetAttackRate(val >> 4); op.SetDecayRate(val & 15); break; } // Sustain Level / Release Rate case 0x80: { op.SetSustainLevel(val >> 4); op.SetReleaseRate(val & 15); break; } // Waveform case 0xE0: { op.SetWaveform(val & 7); break; } } } } //================================================================================================== // Generate sample. Every time you call this you will get two signed 16-bit samples (one for each // stereo channel) which will sound correct when played back at the sample rate given when the // class was constructed. //================================================================================================== void Opal::Sample(int16_t *left, int16_t *right) { // If the destination sample rate is higher than the OPL3 sample rate, we need to skip ahead while (SampleAccum >= SampleRate) { LastOutput[0] = CurrOutput[0]; LastOutput[1] = CurrOutput[1]; Output(CurrOutput[0], CurrOutput[1]); SampleAccum -= SampleRate; } // Mix with the partial accumulation int32_t omblend = SampleRate - SampleAccum; *left = static_cast((LastOutput[0] * omblend + CurrOutput[0] * SampleAccum) / SampleRate); *right = static_cast((LastOutput[1] * omblend + CurrOutput[1] * SampleAccum) / SampleRate); SampleAccum += OPL3SampleRate; } //================================================================================================== // Produce final output from the chip. This is at the OPL3 sample-rate. //================================================================================================== void Opal::Output(int16_t &left, int16_t &right) { int32_t leftmix = 0, rightmix = 0; // Sum the output of each channel for (int i = 0; i < NumChannels; i++) { int16_t chanleft, chanright; Chan[i].Output(chanleft, chanright); leftmix += chanleft; rightmix += chanright; } // Clamp if (leftmix < -0x8000) left = -0x8000; else if (leftmix > 0x7FFF) left = 0x7FFF; else left = static_cast(leftmix); if (rightmix < -0x8000) right = -0x8000; else if (rightmix > 0x7FFF) right = 0x7FFF; else right = static_cast(rightmix); Clock++; // Tremolo. According to this post, the OPL3 tremolo is a 13,440 sample length triangle wave // with a peak at 26 and a trough at 0 and is simply added to the logarithmic level accumulator // http://forums.submarine.org.uk/phpBB/viewtopic.php?f=9&t=1171 TremoloClock = (TremoloClock + 1) % 13440; TremoloLevel = ((TremoloClock < 13440 / 2) ? TremoloClock : 13440 - TremoloClock) / 256; if (!TremoloDepth) TremoloLevel >>= 2; // Vibrato. This appears to be a 8 sample long triangle wave with a magnitude of the three // high bits of the channel frequency, positive and negative, divided by two if the vibrato // depth is zero. It is only cycled every 1,024 samples. VibratoTick++; if (VibratoTick >= 1024) { VibratoTick = 0; VibratoClock = (VibratoClock + 1) & 7; } } //================================================================================================== // Channel constructor. //================================================================================================== Opal::Channel::Channel() { Master = 0; Freq = 0; Octave = 0; PhaseStep = 0; KeyScaleNumber = 0; FeedbackShift = 0; ModulationType = 0; ChannelPair = 0; Enable = true; LeftEnable = true; RightEnable = true; } //================================================================================================== // Produce output from channel. //================================================================================================== void Opal::Channel::Output(int16_t &left, int16_t &right) { // Has the channel been disabled? This is usually a result of the 4-op enables being used to // disable the secondary channel in each 4-op pair if (!Enable) { left = right = 0; return; } int16_t vibrato = (Freq >> 7) & 7; if (!Master->VibratoDepth) vibrato >>= 1; // 0 3 7 3 0 -3 -7 -3 uint16_t clk = Master->VibratoClock; if (!(clk & 3)) vibrato = 0; // Position 0 and 4 is zero else { if (clk & 1) vibrato >>= 1; // Odd positions are half the magnitude vibrato <<= Octave; if (clk & 4) vibrato = -vibrato; // The second half positions are negative } // Combine individual operator outputs int16_t out, acc; // Running in 4-op mode? if (ChannelPair) { // Get the secondary channel's modulation type. This is the only thing from the secondary // channel that is used if (ChannelPair->GetModulationType() == 0) { if (ModulationType == 0) { // feedback -> modulator -> modulator -> modulator -> carrier out = Op[0]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, FeedbackShift); out = Op[1]->Output(KeyScaleNumber, PhaseStep, vibrato, out, 0); out = Op[2]->Output(KeyScaleNumber, PhaseStep, vibrato, out, 0); out = Op[3]->Output(KeyScaleNumber, PhaseStep, vibrato, out, 0); } else { // (feedback -> carrier) + (modulator -> modulator -> carrier) out = Op[0]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, FeedbackShift); acc = Op[1]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, 0); acc = Op[2]->Output(KeyScaleNumber, PhaseStep, vibrato, acc, 0); out += Op[3]->Output(KeyScaleNumber, PhaseStep, vibrato, acc, 0); } } else { if (ModulationType == 0) { // (feedback -> modulator -> carrier) + (modulator -> carrier) out = Op[0]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, FeedbackShift); out = Op[1]->Output(KeyScaleNumber, PhaseStep, vibrato, out, 0); acc = Op[2]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, 0); out += Op[3]->Output(KeyScaleNumber, PhaseStep, vibrato, acc, 0); } else { // (feedback -> carrier) + (modulator -> carrier) + carrier out = Op[0]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, FeedbackShift); acc = Op[1]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, 0); out += Op[2]->Output(KeyScaleNumber, PhaseStep, vibrato, acc, 0); out += Op[3]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, 0); } } } else { // Standard 2-op mode if (ModulationType == 0) { // Frequency modulation (well, phase modulation technically) out = Op[0]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, FeedbackShift); out = Op[1]->Output(KeyScaleNumber, PhaseStep, vibrato, out, 0); } else { // Additive out = Op[0]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, FeedbackShift); out += Op[1]->Output(KeyScaleNumber, PhaseStep, vibrato); } } left = LeftEnable ? out : 0; right = RightEnable ? out : 0; } //================================================================================================== // Set phase step for operators using this channel. //================================================================================================== void Opal::Channel::SetFrequencyLow(uint16_t freq) { Freq = (Freq & 0x300) | (freq & 0xFF); ComputePhaseStep(); } //-------------------------------------------------------------------------------------------------- void Opal::Channel::SetFrequencyHigh(uint16_t freq) { Freq = (Freq & 0xFF) | ((freq & 3) << 8); ComputePhaseStep(); // Only the high bits of Freq affect the Key Scale No. ComputeKeyScaleNumber(); } //================================================================================================== // Set the octave of the channel (0 to 7). //================================================================================================== void Opal::Channel::SetOctave(uint16_t oct) { Octave = oct & 7; ComputePhaseStep(); ComputeKeyScaleNumber(); } //================================================================================================== // Keys the channel on/off. //================================================================================================== void Opal::Channel::SetKeyOn(bool on) { Op[0]->SetKeyOn(on); Op[1]->SetKeyOn(on); } //================================================================================================== // Enable left stereo channel. //================================================================================================== void Opal::Channel::SetLeftEnable(bool on) { LeftEnable = on; } //================================================================================================== // Enable right stereo channel. //================================================================================================== void Opal::Channel::SetRightEnable(bool on) { RightEnable = on; } //================================================================================================== // Set the channel feedback amount. //================================================================================================== void Opal::Channel::SetFeedback(uint16_t val) { FeedbackShift = val ? 9 - val : 0; } //================================================================================================== // Set frequency modulation/additive modulation //================================================================================================== void Opal::Channel::SetModulationType(uint16_t type) { ModulationType = type; } //================================================================================================== // Compute the stepping factor for the operator waveform phase based on the frequency and octave // values of the channel. //================================================================================================== void Opal::Channel::ComputePhaseStep() { PhaseStep = uint32_t(Freq) << Octave; } //================================================================================================== // Compute the key scale number and key scale levels. // // From the Yamaha data sheet this is the block/octave number as bits 3-1, with bit 0 coming from // the MSB of the frequency if NoteSel is 1, and the 2nd MSB if NoteSel is 0. //================================================================================================== void Opal::Channel::ComputeKeyScaleNumber() { uint16_t lsb = Master->NoteSel ? Freq >> 9 : (Freq >> 8) & 1; KeyScaleNumber = Octave << 1 | lsb; // Get the channel operators to recompute their rates as they're dependent on this number. They // also need to recompute their key scale level for (int i = 0; i < 4; i++) { if (!Op[i]) continue; Op[i]->ComputeRates(); Op[i]->ComputeKeyScaleLevel(); } } //================================================================================================== // Operator constructor. //================================================================================================== Opal::Operator::Operator() { Master = 0; Chan = 0; Phase = 0; Waveform = 0; FreqMultTimes2 = 1; EnvelopeStage = EnvOff; EnvelopeLevel = 0x1FF; AttackRate = 0; DecayRate = 0; SustainLevel = 0; ReleaseRate = 0; KeyScaleShift = 0; KeyScaleLevel = 0; Out[0] = Out[1] = 0; KeyOn = false; KeyScaleRate = false; SustainMode = false; TremoloEnable = false; VibratoEnable = false; } //================================================================================================== // Produce output from operator. //================================================================================================== int16_t Opal::Operator::Output(uint16_t /*keyscalenum*/, uint32_t phase_step, int16_t vibrato, int16_t mod, int16_t fbshift) { // Advance wave phase if (VibratoEnable) phase_step += vibrato; Phase += (phase_step * FreqMultTimes2) / 2; uint16_t level = (EnvelopeLevel + OutputLevel + KeyScaleLevel + (TremoloEnable ? Master->TremoloLevel : 0)) << 3; switch (EnvelopeStage) { // Attack stage case EnvAtt: { uint16_t add = ((AttackAdd >> AttackTab[Master->Clock >> AttackShift & 7]) * ~EnvelopeLevel) >> 3; if (AttackRate == 0) add = 0; if (AttackMask && (Master->Clock & AttackMask)) add = 0; EnvelopeLevel += add; if (EnvelopeLevel <= 0) { EnvelopeLevel = 0; EnvelopeStage = EnvDec; } break; } // Decay stage case EnvDec: { uint16_t add = DecayAdd >> DecayTab[Master->Clock >> DecayShift & 7]; if (DecayRate == 0) add = 0; if (DecayMask && (Master->Clock & DecayMask)) add = 0; EnvelopeLevel += add; if (EnvelopeLevel >= SustainLevel) { EnvelopeLevel = SustainLevel; EnvelopeStage = EnvSus; } break; } // Sustain stage case EnvSus: { if (SustainMode) break; // Note: fall-through! [[fallthrough]]; } // Release stage case EnvRel: { uint16_t add = ReleaseAdd >> ReleaseTab[Master->Clock >> ReleaseShift & 7]; if (ReleaseRate == 0) add = 0; if (ReleaseMask && (Master->Clock & ReleaseMask)) add = 0; EnvelopeLevel += add; if (EnvelopeLevel >= 0x1FF) { EnvelopeLevel = 0x1FF; EnvelopeStage = EnvOff; Out[0] = Out[1] = 0; return 0; } break; } // Envelope, and therefore the operator, is not running default: Out[0] = Out[1] = 0; return 0; } // Feedback? In that case we modulate by a blend of the last two samples if (fbshift) mod += (Out[0] + Out[1]) >> fbshift; uint16_t phase = static_cast(Phase >> 10) + mod; uint16_t offset = phase & 0xFF; uint16_t logsin; bool negate = false; switch (Waveform) { //------------------------------------ // Standard sine wave //------------------------------------ case 0: if (phase & 0x100) offset ^= 0xFF; logsin = Master->LogSinTable[offset]; negate = (phase & 0x200) != 0; break; //------------------------------------ // Half sine wave //------------------------------------ case 1: if (phase & 0x200) offset = 0; else if (phase & 0x100) offset ^= 0xFF; logsin = Master->LogSinTable[offset]; break; //------------------------------------ // Positive sine wave //------------------------------------ case 2: if (phase & 0x100) offset ^= 0xFF; logsin = Master->LogSinTable[offset]; break; //------------------------------------ // Quarter positive sine wave //------------------------------------ case 3: if (phase & 0x100) offset = 0; logsin = Master->LogSinTable[offset]; break; //------------------------------------ // Double-speed sine wave //------------------------------------ case 4: if (phase & 0x200) offset = 0; else { if (phase & 0x80) offset ^= 0xFF; offset = (offset + offset) & 0xFF; negate = (phase & 0x100) != 0; } logsin = Master->LogSinTable[offset]; break; //------------------------------------ // Double-speed positive sine wave //------------------------------------ case 5: if (phase & 0x200) offset = 0; else { offset = (offset + offset) & 0xFF; if (phase & 0x80) offset ^= 0xFF; } logsin = Master->LogSinTable[offset]; break; //------------------------------------ // Square wave //------------------------------------ case 6: logsin = 0; negate = (phase & 0x200) != 0; break; //------------------------------------ // Exponentiation wave //------------------------------------ default: logsin = phase & 0x1FF; if (phase & 0x200) { logsin ^= 0x1FF; negate = true; } logsin <<= 3; break; } uint16_t mix = logsin + level; if (mix > 0x1FFF) mix = 0x1FFF; // From the OPLx decapsulated docs: // "When such a table is used for calculation of the exponential, the table is read at the // position given by the 8 LSB's of the input. The value + 1024 (the hidden bit) is then the // significand of the floating point output and the yet unused MSB's of the input are the // exponent of the floating point output." int16_t v = (Master->ExpTable[mix & 0xFF] + 1024u) >> (mix >> 8u); v += v; if (negate) v = ~v; // Keep last two results for feedback calculation Out[1] = Out[0]; Out[0] = v; return v; } //================================================================================================== // Trigger operator. //================================================================================================== void Opal::Operator::SetKeyOn(bool on) { // Already on/off? if (KeyOn == on) return; KeyOn = on; if (on) { // The highest attack rate is instant; it bypasses the attack phase if (AttackRate == 15) { EnvelopeStage = EnvDec; EnvelopeLevel = 0; } else EnvelopeStage = EnvAtt; Phase = 0; } else { // Stopping current sound? if (EnvelopeStage != EnvOff && EnvelopeStage != EnvRel) EnvelopeStage = EnvRel; } } //================================================================================================== // Enable amplitude vibrato. //================================================================================================== void Opal::Operator::SetTremoloEnable(bool on) { TremoloEnable = on; } //================================================================================================== // Enable frequency vibrato. //================================================================================================== void Opal::Operator::SetVibratoEnable(bool on) { VibratoEnable = on; } //================================================================================================== // Sets whether we release or sustain during the sustain phase of the envelope. 'true' is to // sustain, otherwise release. //================================================================================================== void Opal::Operator::SetSustainMode(bool on) { SustainMode = on; } //================================================================================================== // Key scale rate. Sets how much the Key Scaling Number affects the envelope rates. //================================================================================================== void Opal::Operator::SetEnvelopeScaling(bool on) { KeyScaleRate = on; ComputeRates(); } //================================================================================================== // Multiplies the phase frequency. //================================================================================================== void Opal::Operator::SetFrequencyMultiplier(uint16_t scale) { // Needs to be multiplied by two (and divided by two later when we use it) because the first // entry is actually .5 const uint16_t mul_times_2[] = { 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 20, 24, 24, 30, 30, }; FreqMultTimes2 = mul_times_2[scale & 15]; } //================================================================================================== // Attenuates output level towards higher pitch. //================================================================================================== void Opal::Operator::SetKeyScale(uint16_t scale) { static constexpr uint8_t kslShift[4] = { 8, 1, 2, 0 }; KeyScaleShift = kslShift[scale]; ComputeKeyScaleLevel(); } //================================================================================================== // Sets the output level (volume) of the operator. //================================================================================================== void Opal::Operator::SetOutputLevel(uint16_t level) { OutputLevel = level * 4; } //================================================================================================== // Operator attack rate. //================================================================================================== void Opal::Operator::SetAttackRate(uint16_t rate) { AttackRate = rate; ComputeRates(); } //================================================================================================== // Operator decay rate. //================================================================================================== void Opal::Operator::SetDecayRate(uint16_t rate) { DecayRate = rate; ComputeRates(); } //================================================================================================== // Operator sustain level. //================================================================================================== void Opal::Operator::SetSustainLevel(uint16_t level) { SustainLevel = level < 15 ? level : 31; SustainLevel *= 16; } //================================================================================================== // Operator release rate. //================================================================================================== void Opal::Operator::SetReleaseRate(uint16_t rate) { ReleaseRate = rate; ComputeRates(); } //================================================================================================== // Assign the waveform this operator will use. //================================================================================================== void Opal::Operator::SetWaveform(uint16_t wave) { Waveform = wave & 7; } //================================================================================================== // Compute actual rate from register rate. From the Yamaha data sheet: // // Actual rate = Rate value * 4 + Rof, if Rate value = 0, actual rate = 0 // // Rof is set as follows depending on the KSR setting: // // Key scale 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 // KSR = 0 0 0 0 0 1 1 1 1 2 2 2 2 3 3 3 3 // KSR = 1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 // // Note: zero rates are infinite, and are treated separately elsewhere //================================================================================================== void Opal::Operator::ComputeRates() { int combined_rate = AttackRate * 4 + (Chan->GetKeyScaleNumber() >> (KeyScaleRate ? 0 : 2)); int rate_high = combined_rate >> 2; int rate_low = combined_rate & 3; AttackShift = static_cast(rate_high < 12 ? 12 - rate_high : 0); AttackMask = (1 << AttackShift) - 1; AttackAdd = (rate_high < 12) ? 1 : 1 << (rate_high - 12); AttackTab = Master->RateTables[rate_low]; // Attack rate of 15 is always instant if (AttackRate == 15) AttackAdd = 0xFFF; combined_rate = DecayRate * 4 + (Chan->GetKeyScaleNumber() >> (KeyScaleRate ? 0 : 2)); rate_high = combined_rate >> 2; rate_low = combined_rate & 3; DecayShift = static_cast(rate_high < 12 ? 12 - rate_high : 0); DecayMask = (1 << DecayShift) - 1; DecayAdd = (rate_high < 12) ? 1 : 1 << (rate_high - 12); DecayTab = Master->RateTables[rate_low]; combined_rate = ReleaseRate * 4 + (Chan->GetKeyScaleNumber() >> (KeyScaleRate ? 0 : 2)); rate_high = combined_rate >> 2; rate_low = combined_rate & 3; ReleaseShift = static_cast(rate_high < 12 ? 12 - rate_high : 0); ReleaseMask = (1 << ReleaseShift) - 1; ReleaseAdd = (rate_high < 12) ? 1 : 1 << (rate_high - 12); ReleaseTab = Master->RateTables[rate_low]; } //================================================================================================== // Compute the operator's key scale level. This changes based on the channel frequency/octave and // operator key scale value. //================================================================================================== void Opal::Operator::ComputeKeyScaleLevel() { static constexpr uint8_t levtab[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 12, 16, 20, 24, 28, 32, 0, 0, 0, 0, 0, 12, 20, 28, 32, 40, 44, 48, 52, 56, 60, 64, 0, 0, 0, 20, 32, 44, 52, 60, 64, 72, 76, 80, 84, 88, 92, 96, 0, 0, 32, 52, 64, 76, 84, 92, 96, 104, 108, 112, 116, 120, 124, 128, 0, 32, 64, 84, 96, 108, 116, 124, 128, 136, 140, 144, 148, 152, 156, 160, 0, 64, 96, 116, 128, 140, 148, 156, 160, 168, 172, 176, 180, 184, 188, 192, 0, 96, 128, 148, 160, 172, 180, 188, 192, 200, 204, 208, 212, 216, 220, 224, }; // This uses a combined value of the top four bits of frequency with the octave/block uint16_t i = (Chan->GetOctave() << 4) | (Chan->GetFreq() >> 6); KeyScaleLevel = levtab[i] >> KeyScaleShift; } libopenmpt-0.6.1+release.autotools/soundlib/OPL.cpp0000644000175000017500000002127114146453651017245 00000000000000/* * OPL.cpp * ------- * Purpose: Translate data coming from OpenMPT's mixer into OPL commands to be sent to the Opal emulator. * Notes : (currently none) * Authors: OpenMPT Devs * Schism Tracker contributors (bisqwit, JosepMa, Malvineous, code relicensed from GPL to BSD with permission) * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "../common/misc_util.h" #include "OPL.h" #include "opal.h" OPENMPT_NAMESPACE_BEGIN OPL::OPL(uint32 samplerate) { Initialize(samplerate); } OPL::OPL(IRegisterLogger &logger) : m_logger{&logger} { Initialize(OPL_BASERATE); } OPL::~OPL() { // This destructor is put here so that we can forward-declare the Opal emulator class. } void OPL::Initialize(uint32 samplerate) { if(m_opl == nullptr) m_opl = std::make_unique(samplerate); else m_opl->SetSampleRate(samplerate); Reset(); } void OPL::Mix(int32 *target, size_t count, uint32 volumeFactorQ16) { if(!m_isActive) return; // This factor causes a sample voice to be more or less as loud as an OPL voice const int32 factor = Util::muldiv_unsigned(volumeFactorQ16, 6169, (1 << 16)); while(count--) { int16 l, r; m_opl->Sample(&l, &r); target[0] += l * factor; target[1] += r * factor; target += 2; } } uint16 OPL::ChannelToRegister(uint8 oplCh) { if(oplCh < 9) return oplCh; else return (oplCh - 9) | 0x100; } // Translate a channel's first operator address into a register uint16 OPL::OperatorToRegister(uint8 oplCh) { static constexpr uint8 OPLChannelToOperator[] = { 0, 1, 2, 8, 9, 10, 16, 17, 18 }; if(oplCh < 9) return OPLChannelToOperator[oplCh]; else return OPLChannelToOperator[oplCh - 9] | 0x100; } uint8 OPL::GetVoice(CHANNELINDEX c) const { if((m_ChanToOPL[c] & OPL_CHANNEL_CUT) || m_ChanToOPL[c] == OPL_CHANNEL_INVALID) return OPL_CHANNEL_INVALID; return m_ChanToOPL[c] & OPL_CHANNEL_MASK; } uint8 OPL::AllocateVoice(CHANNELINDEX c) { // Can we re-use a previous channel? if(auto oplCh = m_ChanToOPL[c]; oplCh != OPL_CHANNEL_INVALID) { if(!(m_ChanToOPL[c] & OPL_CHANNEL_CUT)) return oplCh; // Check re-use hint oplCh &= OPL_CHANNEL_MASK; if(m_OPLtoChan[oplCh] == CHANNELINDEX_INVALID || m_OPLtoChan[oplCh] == c) { m_OPLtoChan[oplCh] = c; m_ChanToOPL[c] = oplCh; return oplCh; } } // Search for unused channel or channel with released note uint8 releasedChn = OPL_CHANNEL_INVALID, releasedCutChn = OPL_CHANNEL_INVALID; for(uint8 oplCh = 0; oplCh < OPL_CHANNELS; oplCh++) { if(m_OPLtoChan[oplCh] == CHANNELINDEX_INVALID) { m_OPLtoChan[oplCh] = c; m_ChanToOPL[c] = oplCh; return oplCh; } else if(!(m_KeyOnBlock[oplCh] & KEYON_BIT)) { releasedChn = oplCh; if(m_ChanToOPL[m_OPLtoChan[oplCh]] & OPL_CHANNEL_CUT) releasedCutChn = oplCh; } } if(releasedChn != OPL_CHANNEL_INVALID) { // Prefer channel that has been marked as cut over channel that has just been released if(releasedCutChn != OPL_CHANNEL_INVALID) releasedChn = releasedCutChn; m_ChanToOPL[m_OPLtoChan[releasedChn]] = OPL_CHANNEL_INVALID; m_OPLtoChan[releasedChn] = c; m_ChanToOPL[c] = releasedChn; } return GetVoice(c); } void OPL::MoveChannel(CHANNELINDEX from, CHANNELINDEX to) { uint8 oplCh = GetVoice(from); if(oplCh == OPL_CHANNEL_INVALID) return; m_OPLtoChan[oplCh] = to; m_ChanToOPL[from] = OPL_CHANNEL_INVALID; m_ChanToOPL[to] = oplCh; } void OPL::NoteOff(CHANNELINDEX c) { uint8 oplCh = GetVoice(c); if(oplCh == OPL_CHANNEL_INVALID || m_opl == nullptr) return; m_KeyOnBlock[oplCh] &= ~KEYON_BIT; Port(c, KEYON_BLOCK | ChannelToRegister(oplCh), m_KeyOnBlock[oplCh]); } void OPL::NoteCut(CHANNELINDEX c, bool unassign) { uint8 oplCh = GetVoice(c); if(oplCh == OPL_CHANNEL_INVALID) return; NoteOff(c); Volume(c, 0, false); // Note that a volume of 0 is not complete silence; the release portion of the sound will still be heard at -48dB if(unassign) { m_OPLtoChan[oplCh] = CHANNELINDEX_INVALID; m_ChanToOPL[c] |= OPL_CHANNEL_CUT; } } void OPL::Frequency(CHANNELINDEX c, uint32 milliHertz, bool keyOff, bool beatingOscillators) { uint8 oplCh = GetVoice(c); if(oplCh == OPL_CHANNEL_INVALID || m_opl == nullptr) return; uint16 fnum = 1023; uint8 block = 7; if(milliHertz <= 6208431) { if(milliHertz > 3104215) block = 7; else if(milliHertz > 1552107) block = 6; else if(milliHertz > 776053) block = 5; else if(milliHertz > 388026) block = 4; else if(milliHertz > 194013) block = 3; else if(milliHertz > 97006) block = 2; else if(milliHertz > 48503) block = 1; else block = 0; fnum = static_cast(Util::muldivr_unsigned(milliHertz, 1 << (20 - block), OPL_BASERATE * 1000)); MPT_ASSERT(fnum < 1024); } // Evil CDFM hack! Composer 670 slightly detunes each note based on the OPL channel number modulo 4. // We allocate our OPL channels dynamically, which would result in slightly different beating characteristics, // but we can just take the pattern channel number instead, as the pattern channel layout is always identical. if(beatingOscillators) fnum = std::min(static_cast(fnum + (c & 3)), uint16(1023)); fnum |= (block << 10); uint16 channel = ChannelToRegister(oplCh); m_KeyOnBlock[oplCh] = (keyOff ? 0 : KEYON_BIT) | (fnum >> 8); // Key on bit + Octave (block) + F-number high 2 bits Port(c, FNUM_LOW | channel, fnum & 0xFF); // F-Number low 8 bits Port(c, KEYON_BLOCK | channel, m_KeyOnBlock[oplCh]); m_isActive = true; } uint8 OPL::CalcVolume(uint8 trackerVol, uint8 kslVolume) { if(trackerVol >= 63u) return kslVolume; if(trackerVol > 0) trackerVol++; return (kslVolume & KSL_MASK) | (63u - ((63u - (kslVolume & TOTAL_LEVEL_MASK)) * trackerVol) / 64u); } void OPL::Volume(CHANNELINDEX c, uint8 vol, bool applyToModulator) { uint8 oplCh = GetVoice(c); if(oplCh == OPL_CHANNEL_INVALID || m_opl == nullptr) return; const auto &patch = m_Patches[oplCh]; const uint16 modulator = OperatorToRegister(oplCh), carrier = modulator + 3; if((patch[10] & CONNECTION_BIT) || applyToModulator) { // Set volume of both operators in additive mode Port(c, KSL_LEVEL + modulator, CalcVolume(vol, patch[2])); } if(!applyToModulator) { Port(c, KSL_LEVEL + carrier, CalcVolume(vol, patch[3])); } } int8 OPL::Pan(CHANNELINDEX c, int32 pan) { uint8 oplCh = GetVoice(c); if(oplCh == OPL_CHANNEL_INVALID || m_opl == nullptr) return 0; const auto &patch = m_Patches[oplCh]; uint8 fbConn = patch[10] & ~STEREO_BITS; // OPL3 only knows hard left, center and right, so we need to translate our // continuous panning range into one of those three states. // 0...84 = left, 85...170 = center, 171...256 = right if(pan <= 170) fbConn |= VOICE_TO_LEFT; if(pan >= 85) fbConn |= VOICE_TO_RIGHT; Port(c, FEEDBACK_CONNECTION | ChannelToRegister(oplCh), fbConn); return ((fbConn & VOICE_TO_LEFT) ? -1 : 0) + ((fbConn & VOICE_TO_RIGHT) ? 1 : 0); } void OPL::Patch(CHANNELINDEX c, const OPLPatch &patch) { uint8 oplCh = AllocateVoice(c); if(oplCh == OPL_CHANNEL_INVALID || m_opl == nullptr) return; m_Patches[oplCh] = patch; const uint16 modulator = OperatorToRegister(oplCh), carrier = modulator + 3; for(uint8 op = 0; op < 2; op++) { const auto opReg = op ? carrier : modulator; Port(c, AM_VIB | opReg, patch[0 + op]); Port(c, KSL_LEVEL | opReg, patch[2 + op]); Port(c, ATTACK_DECAY | opReg, patch[4 + op]); Port(c, SUSTAIN_RELEASE | opReg, patch[6 + op]); Port(c, WAVE_SELECT | opReg, patch[8 + op]); } Port(c, FEEDBACK_CONNECTION | ChannelToRegister(oplCh), patch[10]); } void OPL::Reset() { if(m_isActive) { for(CHANNELINDEX chn = 0; chn < MAX_CHANNELS; chn++) { NoteCut(chn); } m_isActive = false; } m_KeyOnBlock.fill(0); m_OPLtoChan.fill(CHANNELINDEX_INVALID); m_ChanToOPL.fill(OPL_CHANNEL_INVALID); Port(CHANNELINDEX_INVALID, 0x105, 1); // Enable OPL3 Port(CHANNELINDEX_INVALID, 0x104, 0); // No 4-op voices } void OPL::Port(CHANNELINDEX c, uint16 reg, uint8 value) { if(!m_logger) m_opl->Port(reg, value); else m_logger->Port(c, reg, value); } std::vector OPL::AllVoiceRegisters() { static constexpr uint8 opRegisters[] = {OPL::AM_VIB, OPL::KSL_LEVEL, OPL::ATTACK_DECAY, OPL::SUSTAIN_RELEASE, OPL::WAVE_SELECT}; static constexpr uint8 chnRegisters[] = {OPL::FNUM_LOW, OPL::KEYON_BLOCK, OPL::FEEDBACK_CONNECTION}; std::vector result; result.reserve(234); for(uint16 chip = 0; chip < 2; chip++) { for(uint8 opReg : opRegisters) { for(uint8 op = 0; op < 22; op++) { if((op & 7) < 6) result.push_back((chip << 8) | opReg | op); } } for(uint8 chnReg : chnRegisters) { for(uint8 chn = 0; chn < 9; chn++) { result.push_back((chip << 8) | chnReg | chn); } } } return result; } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/OPL.h0000644000175000017500000000717214146453651016716 00000000000000/* * OPL.h * ----- * Purpose: Translate data coming from OpenMPT's mixer into OPL commands to be sent to the Opal emulator. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "Snd_defs.h" class Opal; OPENMPT_NAMESPACE_BEGIN class OPL { public: enum OPLRegisters : uint8 { // Operators (combine with result of OperatorToRegister) AM_VIB = 0x20, // AM / VIB / EG / KSR / Multiple (0x20 to 0x35) KSL_LEVEL = 0x40, // KSL / Total level (0x40 to 0x55) ATTACK_DECAY = 0x60, // Attack rate / Decay rate (0x60 to 0x75) SUSTAIN_RELEASE = 0x80, // Sustain level / Release rate (0x80 to 0x95) WAVE_SELECT = 0xE0, // Wave select (0xE0 to 0xF5) // Channels (combine with result of ChannelToRegister) FNUM_LOW = 0xA0, // F-number low bits (0xA0 to 0xA8) KEYON_BLOCK = 0xB0, // F-number high bits / Key on / Block (octave) (0xB0 to 0xB8) FEEDBACK_CONNECTION = 0xC0, // Feedback / Connection (0xC0 to 0xC8) }; enum OPLValues : uint8 { // AM_VIB TREMOLO_ON = 0x80, VIBRATO_ON = 0x40, SUSTAIN_ON = 0x20, KSR = 0x10, // Key scaling rate MULTIPLE_MASK = 0x0F, // Frequency multiplier // KSL_LEVEL KSL_MASK = 0xC0, // Envelope scaling bits TOTAL_LEVEL_MASK = 0x3F, // Strength (volume) of OP // ATTACK_DECAY ATTACK_MASK = 0xF0, DECAY_MASK = 0x0F, // SUSTAIN_RELEASE SUSTAIN_MASK = 0xF0, RELEASE_MASK = 0x0F, // KEYON_BLOCK KEYON_BIT = 0x20, // FEEDBACK_CONNECTION FEEDBACK_MASK = 0x0E, // Valid just for first OP of a voice CONNECTION_BIT = 0x01, VOICE_TO_LEFT = 0x10, VOICE_TO_RIGHT = 0x20, STEREO_BITS = VOICE_TO_LEFT | VOICE_TO_RIGHT, }; class IRegisterLogger { public: virtual void Port(CHANNELINDEX c, uint16 reg, uint8 value) = 0; virtual ~IRegisterLogger() {} }; OPL(uint32 samplerate); OPL(IRegisterLogger &logger); ~OPL(); void Initialize(uint32 samplerate); void Mix(int32 *buffer, size_t count, uint32 volumeFactorQ16); void NoteOff(CHANNELINDEX c); void NoteCut(CHANNELINDEX c, bool unassign = true); void Frequency(CHANNELINDEX c, uint32 milliHertz, bool keyOff, bool beatingOscillators); void Volume(CHANNELINDEX c, uint8 vol, bool applyToModulator); int8 Pan(CHANNELINDEX c, int32 pan); void Patch(CHANNELINDEX c, const OPLPatch &patch); bool IsActive(CHANNELINDEX c) const { return GetVoice(c) != OPL_CHANNEL_INVALID; } void MoveChannel(CHANNELINDEX from, CHANNELINDEX to); void Reset(); // A list of all registers for channels and operators static std::vector AllVoiceRegisters(); protected: static uint16 ChannelToRegister(uint8 oplCh); static uint16 OperatorToRegister(uint8 oplCh); static uint8 CalcVolume(uint8 trackerVol, uint8 kslVolume); uint8 GetVoice(CHANNELINDEX c) const; uint8 AllocateVoice(CHANNELINDEX c); void Port(CHANNELINDEX c, uint16 reg, uint8 value); enum { OPL_CHANNELS = 18, // 9 for OPL2 or 18 for OPL3 OPL_CHANNEL_CUT = 0x80, // Indicates that the channel has been cut and used as a hint to re-use the channel for the same tracker channel if possible OPL_CHANNEL_MASK = 0x7F, OPL_CHANNEL_INVALID = 0xFF, OPL_BASERATE = 49716, }; std::unique_ptr m_opl; IRegisterLogger *m_logger = nullptr; std::array m_KeyOnBlock; std::array m_OPLtoChan; std::array m_ChanToOPL; std::array m_Patches; bool m_isActive = false; }; OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Paula.cpp0000644000175000017500000002232214047736406017655 00000000000000/* * Paula.cpp * --------- * Purpose: Emulating the Amiga's sound chip, Paula, by implementing resampling using band-limited steps (BLEPs) * Notes : The BLEP table generator code is a translation of Antti S. Lankila's original Python code. * Authors: OpenMPT Devs * Antti S. Lankila * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Paula.h" #include "TinyFFT.h" #include "Tables.h" #include "mpt/base/numbers.hpp" #include #include OPENMPT_NAMESPACE_BEGIN namespace Paula { namespace { MPT_NOINLINE std::vector KaiserFIR(int numTaps, double cutoff, double beta) { const double izeroBeta = Izero(beta); const double kPi = 4.0 * std::atan(1.0) * cutoff; const double xDiv = 1.0 / ((numTaps / 2) * (numTaps / 2)); const int numTapsDiv2 = numTaps / 2; std::vector result(numTaps); for(int i = 0; i < numTaps; i++) { double fsinc; if(i == numTapsDiv2) { fsinc = 1.0; } else { const double x = i - numTapsDiv2; const double xPi = x * kPi; // - sinc - - Kaiser window - -sinc- fsinc = std::sin(xPi) * Izero(beta * std::sqrt(1 - x * x * xDiv)) / (izeroBeta * xPi); } result[i] = fsinc * cutoff; } return result; } MPT_NOINLINE void FIR_MinPhase(std::vector &table, const TinyFFT &fft) { std::vector> cepstrum(fft.Size()); MPT_ASSERT(cepstrum.size() >= table.size()); for(size_t i = 0; i < table.size(); i++) cepstrum[i] = table[i]; // Compute the real cepstrum: fft -> abs + ln -> ifft -> real fft.FFT(cepstrum); for(auto &v : cepstrum) v = std::log(std::abs(v)); fft.IFFT(cepstrum); fft.Normalize(cepstrum); // Window the cepstrum in such a way that anticausal components become rejected for(size_t i = 1; i < cepstrum.size() / 2; i++) { cepstrum[i] *= 2; cepstrum[i + cepstrum.size() / 2] *= 0; } // Now cancel the previous steps: fft -> exp -> ifft -> real fft.FFT(cepstrum); for(auto &v : cepstrum) v = std::exp(v); fft.IFFT(cepstrum); fft.Normalize(cepstrum); for(size_t i = 0; i < table.size(); i++) table[i] = cepstrum[i].real(); } class BiquadFilter { double b0, b1, b2, a1, a2, x1 = 0.0, x2 = 0.0, y1 = 0.0, y2 = 0.0; double Filter(double x0) { double y0 = b0 * x0 + b1 * x1 + b2 * x2 - a1 * y1 - a2 * y2; x2 = x1; x1 = x0; y2 = y1; y1 = y0; return y0; } public: BiquadFilter(double b0_, double b1_, double b2_, double a1_, double a2_) : b0(b0_), b1(b1_), b2(b2_), a1(a1_), a2(a2_) { } std::vector Run(std::vector table) { x1 = 0.0; x2 = 0.0; y1 = 0.0; y2 = 0.0; // Initialize filter to stable state for(int i = 0; i < 10000; i++) Filter(table[0]); // Now run the filter for(auto &v : table) v = Filter(v); return table; } }; // Observe: a and b are reversed here. To be absolutely clear: // a is the nominator and b is the denominator. :-/ BiquadFilter ZTransform(double a0, double a1, double a2, double b0, double b1, double b2, double fc, double fs) { // Prewarp s - domain coefficients const double wp = 2.0 * fs * std::tan(mpt::numbers::pi * fc / fs); a2 /= wp * wp; a1 /= wp; b2 /= wp * wp; b1 /= wp; // Compute bilinear transform and return it const double bd = 4 * b2 * fs * fs + 2 * b1 * fs + b0; return BiquadFilter( (4 * a2 * fs * fs + 2 * a1 * fs + a0) / bd, (2 * a0 - 8 * a2 * fs * fs) / bd, (4 * a2 * fs * fs - 2 * a1 * fs + a0) / bd, (2 * b0 - 8 * b2 * fs * fs) / bd, (4 * b2 * fs * fs - 2 * b1 * fs + b0) / bd); } BiquadFilter MakeRCLowpass(double sampleRate, double freq) { const double omega = (2.0 * mpt::numbers::pi) * freq / sampleRate; const double term = 1 + 1 / omega; return BiquadFilter(1 / term, 0.0, 0.0, -1.0 + 1.0 / term, 0.0); } BiquadFilter MakeButterworth(double fs, double fc, double res_dB = 0) { // 2nd-order Butterworth s-domain coefficients are: // // b0 = 1.0 b1 = 0 b2 = 0 // a0 = 1 a1 = sqrt(2) a2 = 1 // // by tweaking the a1 parameter, some resonance can be produced. const double res = std::pow(10.0, (-res_dB / 10.0 / 2.0)); return ZTransform(1, 0, 0, 1, std::sqrt(2) * res, 1, fc, fs); } MPT_NOINLINE void Integrate(std::vector &table) { const double total = std::accumulate(table.begin(), table.end(), 0.0); double startVal = -total; for(auto &v : table) { startVal += v; v = startVal; } } MPT_NOINLINE void Quantize(const std::vector &in, Paula::BlepArray &quantized) { MPT_ASSERT(in.size() == Paula::BLEP_SIZE); constexpr int fact = 1 << Paula::BLEP_SCALE; const double cv = fact / (in.back() - in.front()); for(int i = 0; i < Paula::BLEP_SIZE; i++) { double val = in[i] * cv; #ifdef MPT_INTMIXER val = mpt::round(val); #endif quantized[i] = static_cast(-val); } } } // namespace void BlepTables::InitTables() { constexpr double sampleRate = Paula::PAULA_HZ; // Because Amiga only has 84 dB SNR, the noise floor is low enough with -90 dB. // A500 model uses slightly lower-quality kaiser window to obtain slightly // steeper stopband attenuation. The fixed filters attenuates the sidelobes by // 12 dB, compensating for the worse performance of the kaiser window. // 21 kHz stopband is not fully attenuated by 22 kHz. If the sampling frequency // is 44.1 kHz, all frequencies above 22 kHz will alias over 20 kHz, thus inaudible. // The output should be aliasingless for 48 kHz sampling frequency. auto unfilteredA500 = KaiserFIR(Paula::BLEP_SIZE, 21000.0 / sampleRate * 2.0, 8.0); auto unfilteredA1200 = KaiserFIR(Paula::BLEP_SIZE, 21000.0 / sampleRate * 2.0, 9.0); // Move filtering effects to start to allow IIRs more time to settle constexpr size_t padSize = 8; constexpr int fftSize = static_cast(mpt::bit_width(size_t(Paula::BLEP_SIZE)) + mpt::bit_width(padSize) - 2); const TinyFFT fft(fftSize); FIR_MinPhase(unfilteredA500, fft); FIR_MinPhase(unfilteredA1200, fft); // Make digital models for the filters on Amiga 500 and 1200. auto filterFixed5kHz = MakeRCLowpass(sampleRate, 4900.0); // The leakage filter seems to reduce treble in both models a bit // The A500 filter seems to be well modelled only with a 4.9 kHz // filter although the component values would suggest 5 kHz filter. auto filterLeakage = MakeRCLowpass(sampleRate, 32000.0); auto filterLED = MakeButterworth(sampleRate, 3275.0, -0.70); // Apply fixed filter to A500 auto amiga500Off = filterFixed5kHz.Run(unfilteredA500); // Produce the filtered outputs auto amiga1200Off = filterLeakage.Run(unfilteredA1200); // Produce LED filters auto amiga500On = filterLED.Run(amiga500Off); auto amiga1200On = filterLED.Run(amiga1200Off); // Integrate to produce blep Integrate(amiga500Off); Integrate(amiga500On); Integrate(amiga1200Off); Integrate(amiga1200On); Integrate(unfilteredA1200); // Quantize and scale Quantize(amiga500Off, WinSincIntegral[A500Off]); Quantize(amiga500On, WinSincIntegral[A500On]); Quantize(amiga1200Off, WinSincIntegral[A1200Off]); Quantize(amiga1200On, WinSincIntegral[A1200On]); Quantize(unfilteredA1200, WinSincIntegral[Unfiltered]); } const Paula::BlepArray &BlepTables::GetAmigaTable(Resampling::AmigaFilter amigaType, bool enableFilter) const { if(amigaType == Resampling::AmigaFilter::A500) return enableFilter ? WinSincIntegral[A500On] : WinSincIntegral[A500Off]; if(amigaType == Resampling::AmigaFilter::A1200) return enableFilter ? WinSincIntegral[A1200On] : WinSincIntegral[A1200Off]; return WinSincIntegral[Unfiltered]; } // we do not initialize blepState here // cppcheck-suppress uninitMemberVar State::State(uint32 sampleRate) { double amigaClocksPerSample = static_cast(PAULA_HZ) / sampleRate; numSteps = static_cast(amigaClocksPerSample / MINIMUM_INTERVAL); stepRemainder = SamplePosition::FromDouble(amigaClocksPerSample - numSteps * MINIMUM_INTERVAL); remainder = SamplePosition(0); } void State::Reset() { remainder = SamplePosition(0); activeBleps = 0; firstBlep = MAX_BLEPS / 2u; globalOutputLevel = 0; } void State::InputSample(int16 sample) { if(sample != globalOutputLevel) { // Start a new blep: level is the difference, age (or phase) is 0 clocks. firstBlep = (firstBlep - 1u) % MAX_BLEPS; if(activeBleps < std::size(blepState)) activeBleps++; blepState[firstBlep].age = 0; blepState[firstBlep].level = sample - globalOutputLevel; globalOutputLevel = sample; } } // Return output simulated as series of bleps int State::OutputSample(const BlepArray &WinSincIntegral) { int output = globalOutputLevel * (1 << Paula::BLEP_SCALE); uint32 lastBlep = firstBlep + activeBleps; for(uint32 i = firstBlep; i != lastBlep; i++) { const auto &blep = blepState[i % MAX_BLEPS]; output -= WinSincIntegral[blep.age] * blep.level; } output /= (1 << (Paula::BLEP_SCALE - 2)); // - 2 to compensate for the fact that we reduced the input sample bit depth return output; } // Advance the simulation by given number of clock ticks void State::Clock(int cycles) { uint32 lastBlep = firstBlep + activeBleps; for(uint32 i = firstBlep; i != lastBlep; i++) { auto &blep = blepState[i % MAX_BLEPS]; blep.age += static_cast(cycles); if(blep.age >= Paula::BLEP_SIZE) { activeBleps = static_cast(i - firstBlep); return; } } } } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Paula.h0000644000175000017500000000433614052666041017320 00000000000000/* * Paula.h * ------- * Purpose: Emulating the Amiga's sound chip, Paula, by implementing resampling using band-limited steps (BLEPs) * Notes : (currently none) * Authors: OpenMPT Devs * Antti S. Lankila * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "Snd_defs.h" #include "Mixer.h" OPENMPT_NAMESPACE_BEGIN namespace Paula { inline constexpr int PAULA_HZ = 3546895; inline constexpr int MINIMUM_INTERVAL = 4; // Tradeoff between quality and speed (lower = less aliasing) inline constexpr int BLEP_SCALE = 17; // TODO: Should be 1 for float mixer inline constexpr int BLEP_SIZE = 2048; using BlepArray = std::array; class BlepTables { enum AmigaFilter { A500Off = 0, A500On, A1200Off, A1200On, Unfiltered, NumFilterTypes }; std::array WinSincIntegral; public: void InitTables(); const Paula::BlepArray &GetAmigaTable(Resampling::AmigaFilter amigaType, bool enableFilter) const; }; class State { // MAX_BLEPS configures how many BLEPs (volume steps) are being kept track of per channel, // and thus directly influences how much memory this feature wastes per virtual channel. // Paula::BLEP_SIZE / Paula::MINIMUM_INTERVAL would be a safe maximum, // but even a sample (alternating at +1/-1, thus causing a step on every frame) played at 200 kHz, // which is way out of spec for the Amiga, will only get close to 128 active BLEPs with Paula::MINIMUM_INTERVAL == 4. // Hence 128 is chosen as a tradeoff between quality and memory consumption. static constexpr uint16 MAX_BLEPS = 128; struct Blep { int16 level; uint16 age; }; public: SamplePosition remainder, stepRemainder; int numSteps; // Number of full-length steps private: uint16 activeBleps = 0, firstBlep = 0; // Count of simultaneous bleps to keep track of int16 globalOutputLevel = 0; // The instantenous value of Paula output Blep blepState[MAX_BLEPS]; public: State(uint32 sampleRate = 48000); void Reset(); void InputSample(int16 sample); int OutputSample(const BlepArray &WinSincIntegral); void Clock(int cycles); }; } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/patternContainer.cpp0000644000175000017500000001044714170113375022127 00000000000000/* * PatternContainer.cpp * -------------------- * Purpose: Container class for managing patterns. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "patternContainer.h" #include "Sndfile.h" #include "mod_specifications.h" #include "../common/serialization_utils.h" #include "../common/version.h" OPENMPT_NAMESPACE_BEGIN void CPatternContainer::ClearPatterns() { DestroyPatterns(); m_Patterns.assign(m_Patterns.size(), CPattern(*this)); } void CPatternContainer::DestroyPatterns() { m_Patterns.clear(); } PATTERNINDEX CPatternContainer::Duplicate(PATTERNINDEX from, bool respectQtyLimits) { if(!IsValidPat(from)) { return PATTERNINDEX_INVALID; } PATTERNINDEX newPatIndex = InsertAny(m_Patterns[from].GetNumRows(), respectQtyLimits); if(newPatIndex != PATTERNINDEX_INVALID) { m_Patterns[newPatIndex] = m_Patterns[from]; } return newPatIndex; } PATTERNINDEX CPatternContainer::InsertAny(const ROWINDEX rows, bool respectQtyLimits) { PATTERNINDEX i = 0; for(i = 0; i < m_Patterns.size(); i++) if(!m_Patterns[i].IsValid()) break; if(respectQtyLimits && i >= m_rSndFile.GetModSpecifications().patternsMax) return PATTERNINDEX_INVALID; if(!Insert(i, rows)) return PATTERNINDEX_INVALID; else return i; } bool CPatternContainer::Insert(const PATTERNINDEX index, const ROWINDEX rows) { if(rows > MAX_PATTERN_ROWS || rows == 0 || index >= PATTERNINDEX_INVALID) return false; if(IsValidPat(index)) return false; try { if(index >= m_Patterns.size()) { m_Patterns.resize(index + 1, CPattern(*this)); } m_Patterns[index].AllocatePattern(rows); m_Patterns[index].RemoveSignature(); m_Patterns[index].SetName(""); } catch(mpt::out_of_memory e) { mpt::delete_out_of_memory(e); return false; } return m_Patterns[index].IsValid(); } void CPatternContainer::Remove(const PATTERNINDEX ipat) { if(ipat < m_Patterns.size()) m_Patterns[ipat].Deallocate(); } bool CPatternContainer::IsPatternEmpty(const PATTERNINDEX nPat) const { if(!IsValidPat(nPat)) return false; for(const auto &m : m_Patterns[nPat].m_ModCommands) { if(!m.IsEmpty()) return false; } return true; } void CPatternContainer::ResizeArray(const PATTERNINDEX newSize) { m_Patterns.resize(newSize, CPattern(*this)); } void CPatternContainer::OnModTypeChanged(const MODTYPE /*oldtype*/) { const CModSpecifications specs = m_rSndFile.GetModSpecifications(); //if(specs.patternsMax < Size()) // ResizeArray(specs.patternsMax); // remove pattern time signatures if(!specs.hasPatternSignatures) { for(PATTERNINDEX nPat = 0; nPat < m_Patterns.size(); nPat++) { m_Patterns[nPat].RemoveSignature(); m_Patterns[nPat].RemoveTempoSwing(); } } } PATTERNINDEX CPatternContainer::GetNumPatterns() const { for(PATTERNINDEX pat = Size(); pat > 0; pat--) { if(IsValidPat(pat - 1)) { return pat; } } return 0; } PATTERNINDEX CPatternContainer::GetNumNamedPatterns() const { if(Size() == 0) { return 0; } for(PATTERNINDEX nPat = Size(); nPat > 0; nPat--) { if(!m_Patterns[nPat - 1].GetName().empty()) { return nPat; } } return 0; } void WriteModPatterns(std::ostream& oStrm, const CPatternContainer& patc) { srlztn::SsbWrite ssb(oStrm); ssb.BeginWrite(FileIdPatterns, Version::Current().GetRawVersion()); const PATTERNINDEX nPatterns = patc.Size(); uint16 nCount = 0; for(uint16 i = 0; i < nPatterns; i++) if (patc[i].IsValid()) { ssb.WriteItem(patc[i], srlztn::ID::FromInt(i), &WriteModPattern); nCount = i + 1; } ssb.WriteItem(nCount, "num"); // Index of last pattern + 1. ssb.FinishWrite(); } void ReadModPatterns(std::istream& iStrm, CPatternContainer& patc, const size_t) { srlztn::SsbRead ssb(iStrm); ssb.BeginRead(FileIdPatterns, Version::Current().GetRawVersion()); if ((ssb.GetStatus() & srlztn::SNT_FAILURE) != 0) return; PATTERNINDEX nPatterns = patc.Size(); uint16 nCount = uint16_max; if (ssb.ReadItem(nCount, "num") != srlztn::SsbRead::EntryNotFound) nPatterns = nCount; LimitMax(nPatterns, ModSpecs::mptm.patternsMax); if (nPatterns > patc.Size()) patc.ResizeArray(nPatterns); for(uint16 i = 0; i < nPatterns; i++) { ssb.ReadItem(patc[i], srlztn::ID::FromInt(i), &ReadModPattern); } } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/patternContainer.h0000644000175000017500000001046714052666041021600 00000000000000/* * PatternContainer.h * ------------------ * Purpose: Container class for managing patterns. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "pattern.h" #include OPENMPT_NAMESPACE_BEGIN class CSoundFile; class CPatternContainer { public: CPattern& operator[](const int pat) { return m_Patterns[pat]; } const CPattern& operator[](const int pat) const { return m_Patterns[pat]; } public: CPatternContainer(CSoundFile& sndFile) : m_rSndFile(sndFile) { } // Empty and initialize all patterns. void ClearPatterns(); // Delete all patterns. void DestroyPatterns(); // Insert (default)pattern to given position. If pattern already exists at that position, // ignoring request. Returns true on success, false otherwise. bool Insert(const PATTERNINDEX index, const ROWINDEX rows); // Insert pattern to position with the lowest index, and return that index, PATTERNINDEX_INVALID on failure. // If respectQtyLimits is true, inserting patterns will fail if the resulting pattern index would exceed the current format's pattern quantity limits. PATTERNINDEX InsertAny(const ROWINDEX rows, bool respectQtyLimits = false); // Duplicate an existing pattern. Returns new pattern index on success, or PATTERNINDEX_INVALID on failure. // If respectQtyLimits is true, inserting patterns will fail if the resulting pattern index would exceed the current format's pattern quantity limits. PATTERNINDEX Duplicate(PATTERNINDEX from, bool respectQtyLimits = false); //Remove pattern from given position. Currently it actually makes the pattern //'invisible' - the pattern data is cleared but the actual pattern object won't get removed. void Remove(const PATTERNINDEX index); // Applies function object for modcommands in patterns in given range. // Return: Copy of the function object. template Func ForEachModCommand(PATTERNINDEX nStartPat, PATTERNINDEX nLastPat, Func func); template Func ForEachModCommand(Func func) { return ForEachModCommand(0, Size() - 1, func); } std::vector::iterator begin() { return m_Patterns.begin(); } std::vector::const_iterator begin() const { return m_Patterns.begin(); } std::vector::const_iterator cbegin() const { return m_Patterns.cbegin(); } std::vector::iterator end() { return m_Patterns.end(); } std::vector::const_iterator end() const { return m_Patterns.end(); } std::vector::const_iterator cend() const { return m_Patterns.cend(); } PATTERNINDEX Size() const { return static_cast(m_Patterns.size()); } CSoundFile& GetSoundFile() { return m_rSndFile; } const CSoundFile& GetSoundFile() const { return m_rSndFile; } // Return true if pattern can be accessed with operator[](iPat), false otherwise. bool IsValidIndex(const PATTERNINDEX iPat) const { return (iPat < Size()); } // Return true if IsValidIndex() is true and the corresponding pattern has allocated modcommand array, false otherwise. bool IsValidPat(const PATTERNINDEX iPat) const { return IsValidIndex(iPat) && m_Patterns[iPat].IsValid(); } // Returns true if the pattern is empty, i.e. there are no notes/effects in this pattern bool IsPatternEmpty(const PATTERNINDEX nPat) const; void ResizeArray(const PATTERNINDEX newSize); void OnModTypeChanged(const MODTYPE oldtype); // Returns index of last valid pattern + 1, zero if no such pattern exists. PATTERNINDEX GetNumPatterns() const; // Returns index of highest pattern with pattern named + 1. PATTERNINDEX GetNumNamedPatterns() const; private: std::vector m_Patterns; CSoundFile &m_rSndFile; }; template Func CPatternContainer::ForEachModCommand(PATTERNINDEX nStartPat, PATTERNINDEX nLastPat, Func func) { if (nStartPat > nLastPat || nLastPat >= Size()) return func; for (PATTERNINDEX nPat = nStartPat; nPat <= nLastPat; nPat++) if (m_Patterns[nPat].IsValid()) std::for_each(m_Patterns[nPat].begin(), m_Patterns[nPat].end(), func); return func; } const char FileIdPatterns[] = "mptPc"; void ReadModPatterns(std::istream& iStrm, CPatternContainer& patc, const size_t nSize = 0); void WriteModPatterns(std::ostream& oStrm, const CPatternContainer& patc); OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/pattern.cpp0000644000175000017500000003634114154725442020273 00000000000000/* * Pattern.cpp * ----------- * Purpose: Module Pattern header class * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "pattern.h" #include "patternContainer.h" #include "../common/serialization_utils.h" #include "../common/version.h" #include "ITTools.h" #include "Sndfile.h" #include "mod_specifications.h" #include "mpt/io/io.hpp" #include "mpt/io/io_stdstream.hpp" OPENMPT_NAMESPACE_BEGIN CSoundFile& CPattern::GetSoundFile() { return m_rPatternContainer.GetSoundFile(); } const CSoundFile& CPattern::GetSoundFile() const { return m_rPatternContainer.GetSoundFile(); } CHANNELINDEX CPattern::GetNumChannels() const { return GetSoundFile().GetNumChannels(); } // Check if there is any note data on a given row. bool CPattern::IsEmptyRow(ROWINDEX row) const { if(m_ModCommands.empty() || !IsValidRow(row)) { return true; } PatternRow data = GetRow(row); for(CHANNELINDEX chn = 0; chn < GetNumChannels(); chn++, data++) { if(!data->IsEmpty()) { return false; } } return true; } bool CPattern::SetSignature(const ROWINDEX rowsPerBeat, const ROWINDEX rowsPerMeasure) { if(rowsPerBeat < 1 || rowsPerBeat > GetSoundFile().GetModSpecifications().patternRowsMax || rowsPerMeasure < rowsPerBeat || rowsPerMeasure > GetSoundFile().GetModSpecifications().patternRowsMax) { return false; } m_RowsPerBeat = rowsPerBeat; m_RowsPerMeasure = rowsPerMeasure; return true; } // Add or remove rows from the pattern. bool CPattern::Resize(const ROWINDEX newRowCount, bool enforceFormatLimits, bool resizeAtEnd) { CSoundFile &sndFile = GetSoundFile(); if(newRowCount == m_Rows || newRowCount < 1 || newRowCount > MAX_PATTERN_ROWS) { return false; } if(enforceFormatLimits) { auto &specs = sndFile.GetModSpecifications(); if(newRowCount > specs.patternRowsMax || newRowCount < specs.patternRowsMin) return false; } try { size_t count = ((newRowCount > m_Rows) ? (newRowCount - m_Rows) : (m_Rows - newRowCount)) * GetNumChannels(); if(newRowCount > m_Rows) m_ModCommands.insert(resizeAtEnd ? m_ModCommands.end() : m_ModCommands.begin(), count, ModCommand::Empty()); else if(resizeAtEnd) m_ModCommands.erase(m_ModCommands.end() - count, m_ModCommands.end()); else m_ModCommands.erase(m_ModCommands.begin(), m_ModCommands.begin() + count); } catch(mpt::out_of_memory e) { mpt::delete_out_of_memory(e); return false; } m_Rows = newRowCount; return true; } void CPattern::ClearCommands() { std::fill(m_ModCommands.begin(), m_ModCommands.end(), ModCommand::Empty()); } bool CPattern::AllocatePattern(ROWINDEX rows) { size_t newSize = GetNumChannels() * rows; if(rows == 0) { return false; } else if(rows == GetNumRows() && m_ModCommands.size() == newSize) { // Re-use allocated memory ClearCommands(); return true; } else { // Do this in two steps in order to keep the old pattern data in case of OOM decltype(m_ModCommands) newPattern(newSize, ModCommand::Empty()); m_ModCommands = std::move(newPattern); } m_Rows = rows; return true; } void CPattern::Deallocate() { m_Rows = m_RowsPerBeat = m_RowsPerMeasure = 0; m_ModCommands.clear(); m_PatternName.clear(); } CPattern& CPattern::operator= (const CPattern &pat) { m_ModCommands = pat.m_ModCommands; m_Rows = pat.m_Rows; m_RowsPerBeat = pat.m_RowsPerBeat; m_RowsPerMeasure = pat.m_RowsPerMeasure; m_tempoSwing = pat.m_tempoSwing; m_PatternName = pat.m_PatternName; return *this; } bool CPattern::operator== (const CPattern &other) const { return GetNumRows() == other.GetNumRows() && GetNumChannels() == other.GetNumChannels() && GetOverrideSignature() == other.GetOverrideSignature() && GetRowsPerBeat() == other.GetRowsPerBeat() && GetRowsPerMeasure() == other.GetRowsPerMeasure() && GetTempoSwing() == other.GetTempoSwing() && m_ModCommands == other.m_ModCommands; } #ifdef MODPLUG_TRACKER bool CPattern::Expand() { const ROWINDEX newRows = m_Rows * 2; const CHANNELINDEX nChns = GetNumChannels(); if(m_ModCommands.empty() || newRows > GetSoundFile().GetModSpecifications().patternRowsMax) { return false; } decltype(m_ModCommands) newPattern; try { newPattern.assign(m_ModCommands.size() * 2, ModCommand::Empty()); } catch(mpt::out_of_memory e) { mpt::delete_out_of_memory(e); return false; } for(auto mSrc = m_ModCommands.begin(), mDst = newPattern.begin(); mSrc != m_ModCommands.end(); mSrc += nChns, mDst += 2 * nChns) { std::copy(mSrc, mSrc + nChns, mDst); } m_ModCommands = std::move(newPattern); m_Rows = newRows; return true; } bool CPattern::Shrink() { if (m_ModCommands.empty() || m_Rows < GetSoundFile().GetModSpecifications().patternRowsMin * 2) { return false; } m_Rows /= 2; const CHANNELINDEX nChns = GetNumChannels(); for(ROWINDEX y = 0; y < m_Rows; y++) { const PatternRow srcRow = GetRow(y * 2); const PatternRow nextSrcRow = GetRow(y * 2 + 1); PatternRow destRow = GetRow(y); for(CHANNELINDEX x = 0; x < nChns; x++) { const ModCommand &src = srcRow[x]; const ModCommand &srcNext = nextSrcRow[x]; ModCommand &dest = destRow[x]; dest = src; if(dest.note == NOTE_NONE && !dest.instr) { // Fill in data from next row if field is empty dest.note = srcNext.note; dest.instr = srcNext.instr; if(srcNext.volcmd != VOLCMD_NONE) { dest.volcmd = srcNext.volcmd; dest.vol = srcNext.vol; } if(dest.command == CMD_NONE) { dest.command = srcNext.command; dest.param = srcNext.param; } } } } m_ModCommands.resize(m_Rows * nChns); return true; } #endif // MODPLUG_TRACKER bool CPattern::SetName(const std::string &newName) { m_PatternName = newName; return true; } bool CPattern::SetName(const char *newName, size_t maxChars) { if(newName == nullptr || maxChars == 0) { return false; } const auto nameEnd = std::find(newName, newName + maxChars, '\0'); m_PatternName.assign(newName, nameEnd); return true; } // Write some kind of effect data to the pattern. Exact data to be written and write behaviour can be found in the EffectWriter object. bool CPattern::WriteEffect(EffectWriter &settings) { // First, reject invalid parameters. if(m_ModCommands.empty() || settings.m_row >= GetNumRows() || (settings.m_channel >= GetNumChannels() && settings.m_channel != CHANNELINDEX_INVALID)) { return false; } CHANNELINDEX scanChnMin = settings.m_channel, scanChnMax = settings.m_channel; // Scan all channels if(settings.m_channel == CHANNELINDEX_INVALID) { scanChnMin = 0; scanChnMax = GetNumChannels() - 1; } ModCommand * const baseCommand = GetpModCommand(settings.m_row, scanChnMin); ModCommand *m; // Scan channel(s) for same effect type - if an effect of the same type is already present, exit. if(!settings.m_allowMultiple) { m = baseCommand; for(CHANNELINDEX i = scanChnMin; i <= scanChnMax; i++, m++) { if(!settings.m_isVolEffect && m->command == settings.m_command) return true; if(settings.m_isVolEffect && m->volcmd == settings.m_volcmd) return true; } } // Easy case: check if there's some space left to put the effect somewhere m = baseCommand; for(CHANNELINDEX i = scanChnMin; i <= scanChnMax; i++, m++) { if(!settings.m_isVolEffect && m->command == CMD_NONE) { m->command = settings.m_command; m->param = settings.m_param; return true; } if(settings.m_isVolEffect && m->volcmd == VOLCMD_NONE) { m->volcmd = settings.m_volcmd; m->vol = settings.m_vol; return true; } } // Ok, apparently there's no space. If we haven't tried already, try to map it to the volume column or effect column instead. if(settings.m_retry) { const bool isS3M = (GetSoundFile().GetType() & MOD_TYPE_S3M); // Move some effects that also work in the volume column, so there's place for our new effect. if(!settings.m_isVolEffect) { m = baseCommand; for(CHANNELINDEX i = scanChnMin; i <= scanChnMax; i++, m++) { switch(m->command) { case CMD_VOLUME: if(!GetSoundFile().GetModSpecifications().HasVolCommand(VOLCMD_VOLUME)) { break; } m->volcmd = VOLCMD_VOLUME; m->vol = m->param; m->command = settings.m_command; m->param = settings.m_param; return true; case CMD_PANNING8: if(isS3M && m->param > 0x80) { break; } m->volcmd = VOLCMD_PANNING; m->command = settings.m_command; if(isS3M) m->vol = (m->param + 1u) / 2u; else m->vol = (m->param + 2u) / 4u; m->param = settings.m_param; return true; default: break; } } } // Let's try it again by writing into the "other" effect column. if(settings.m_isVolEffect) { // Convert volume effect to normal effect ModCommand::COMMAND newCommand = CMD_NONE; ModCommand::PARAM newParam = settings.m_vol; switch(settings.m_volcmd) { case VOLCMD_PANNING: newCommand = CMD_PANNING8; newParam = mpt::saturate_cast(settings.m_vol * (isS3M ? 2u : 4u)); break; case VOLCMD_VOLUME: newCommand = CMD_VOLUME; break; default: break; } if(newCommand != CMD_NONE) { settings.m_command = static_cast(newCommand); settings.m_param = newParam; settings.m_retry = false; } } else { // Convert normal effect to volume effect ModCommand::VOLCMD newVolCmd = VOLCMD_NONE; ModCommand::VOL newVol = settings.m_param; if(settings.m_command == CMD_PANNING8 && isS3M) { // This needs some manual fixing. if(settings.m_param <= 0x80) { // Can't have surround in volume column, only normal panning newVolCmd = VOLCMD_PANNING; newVol /= 2u; } } else { newVolCmd = settings.m_command; if(!ModCommand::ConvertVolEffect(newVolCmd, newVol, true)) { // No Success :( newVolCmd = VOLCMD_NONE; } } if(newVolCmd != CMD_NONE) { settings.m_volcmd = static_cast(newVolCmd); settings.m_vol = newVol; settings.m_retry = false; } } if(!settings.m_retry) { settings.m_isVolEffect = !settings.m_isVolEffect; if(WriteEffect(settings)) { return true; } } } // Try in the next row if possible (this may also happen if we already retried) if(settings.m_retryMode == EffectWriter::rmTryNextRow && settings.m_row + 1 < GetNumRows()) { settings.m_row++; settings.m_retry = true; return WriteEffect(settings); } else if(settings.m_retryMode == EffectWriter::rmTryPreviousRow && settings.m_row > 0) { settings.m_row--; settings.m_retry = true; return WriteEffect(settings); } return false; } //////////////////////////////////////////////////////////////////////// // // Pattern serialization functions // //////////////////////////////////////////////////////////////////////// enum maskbits { noteBit = (1 << 0), instrBit = (1 << 1), volcmdBit = (1 << 2), volBit = (1 << 3), commandBit = (1 << 4), effectParamBit = (1 << 5), extraData = (1 << 6) }; void WriteData(std::ostream& oStrm, const CPattern& pat); void ReadData(std::istream& iStrm, CPattern& pat, const size_t nSize = 0); void WriteModPattern(std::ostream& oStrm, const CPattern& pat) { srlztn::SsbWrite ssb(oStrm); ssb.BeginWrite(FileIdPattern, Version::Current().GetRawVersion()); ssb.WriteItem(pat, "data", &WriteData); // pattern time signature if(pat.GetOverrideSignature()) { ssb.WriteItem(pat.GetRowsPerBeat(), "RPB."); ssb.WriteItem(pat.GetRowsPerMeasure(), "RPM."); } if(pat.HasTempoSwing()) { ssb.WriteItem(pat.GetTempoSwing(), "SWNG", TempoSwing::Serialize); } ssb.FinishWrite(); } void ReadModPattern(std::istream& iStrm, CPattern& pat, const size_t) { srlztn::SsbRead ssb(iStrm); ssb.BeginRead(FileIdPattern, Version::Current().GetRawVersion()); if ((ssb.GetStatus() & srlztn::SNT_FAILURE) != 0) return; ssb.ReadItem(pat, "data", &ReadData); // pattern time signature uint32 rpb = 0, rpm = 0; ssb.ReadItem(rpb, "RPB."); ssb.ReadItem(rpm, "RPM."); pat.SetSignature(rpb, rpm); TempoSwing swing; ssb.ReadItem(swing, "SWNG", TempoSwing::Deserialize); if(!swing.empty()) swing.resize(pat.GetRowsPerBeat()); pat.SetTempoSwing(swing); } static uint8 CreateDiffMask(const ModCommand &chnMC, const ModCommand &newMC) { uint8 mask = 0; if(chnMC.note != newMC.note) mask |= noteBit; if(chnMC.instr != newMC.instr) mask |= instrBit; if(chnMC.volcmd != newMC.volcmd) mask |= volcmdBit; if(chnMC.vol != newMC.vol) mask |= volBit; if(chnMC.command != newMC.command) mask |= commandBit; if(chnMC.param != newMC.param) mask |= effectParamBit; return mask; } // Writes pattern data. Adapted from SaveIT. void WriteData(std::ostream& oStrm, const CPattern& pat) { if(!pat.IsValid()) return; const ROWINDEX rows = pat.GetNumRows(); const CHANNELINDEX chns = pat.GetNumChannels(); std::vector lastChnMC(chns); for(ROWINDEX r = 0; r(c+1); if(diffmask != 0) chval |= IT_bitmask_patternChanEnabled_c; mpt::IO::WriteIntLE(oStrm, chval); if(diffmask) { lastChnMC[c] = m; mpt::IO::WriteIntLE(oStrm, diffmask); if(diffmask & noteBit) mpt::IO::WriteIntLE(oStrm, m.note); if(diffmask & instrBit) mpt::IO::WriteIntLE(oStrm, m.instr); if(diffmask & volcmdBit) mpt::IO::WriteIntLE(oStrm, m.volcmd); if(diffmask & volBit) mpt::IO::WriteIntLE(oStrm, m.vol); if(diffmask & commandBit) mpt::IO::WriteIntLE(oStrm, m.command); if(diffmask & effectParamBit) mpt::IO::WriteIntLE(oStrm, m.param); } } mpt::IO::WriteIntLE(oStrm, 0); // Write end of row marker. } } #define READITEM(itembit,id) \ if(diffmask & itembit) \ { \ mpt::IO::ReadIntLE(iStrm, temp); \ if(ch < chns) \ lastChnMC[ch].id = temp; \ } \ if(ch < chns) \ m.id = lastChnMC[ch].id; void ReadData(std::istream& iStrm, CPattern& pat, const size_t) { if (!pat.IsValid()) // Expecting patterns to be allocated and resized properly. return; const CHANNELINDEX chns = pat.GetNumChannels(); const ROWINDEX rows = pat.GetNumRows(); std::vector lastChnMC(chns); ROWINDEX row = 0; while(row < rows && iStrm.good()) { uint8 t = 0; mpt::IO::ReadIntLE(iStrm, t); if(t == 0) { row++; continue; } CHANNELINDEX ch = (t & IT_bitmask_patternChanField_c); if(ch > 0) ch--; uint8 diffmask = 0; if((t & IT_bitmask_patternChanEnabled_c) != 0) mpt::IO::ReadIntLE(iStrm, diffmask); uint8 temp = 0; ModCommand dummy = ModCommand::Empty(); ModCommand& m = (ch < chns) ? *pat.GetpModCommand(row, ch) : dummy; READITEM(noteBit, note); READITEM(instrBit, instr); READITEM(volcmdBit, volcmd); READITEM(volBit, vol); READITEM(commandBit, command); READITEM(effectParamBit, param); if(diffmask & extraData) { //Ignore additional data. uint8 size; mpt::IO::ReadIntLE(iStrm, size); iStrm.ignore(size); } } } #undef READITEM OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/pattern.h0000644000175000017500000001572214052666041017734 00000000000000/* * Pattern.h * --------- * Purpose: Module Pattern header class * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include #include "modcommand.h" #include "Snd_defs.h" OPENMPT_NAMESPACE_BEGIN class CPatternContainer; class CSoundFile; class EffectWriter; typedef ModCommand* PatternRow; class CPattern { friend class CPatternContainer; public: CPattern& operator= (const CPattern &pat); bool operator== (const CPattern &other) const; bool operator!= (const CPattern &other) const { return !(*this == other); } public: ModCommand* GetpModCommand(const ROWINDEX r, const CHANNELINDEX c) { return &m_ModCommands[r * GetNumChannels() + c]; } const ModCommand* GetpModCommand(const ROWINDEX r, const CHANNELINDEX c) const { return &m_ModCommands[r * GetNumChannels() + c]; } ROWINDEX GetNumRows() const { return m_Rows; } ROWINDEX GetRowsPerBeat() const { return m_RowsPerBeat; } // pattern-specific rows per beat ROWINDEX GetRowsPerMeasure() const { return m_RowsPerMeasure; } // pattern-specific rows per measure bool GetOverrideSignature() const { return (m_RowsPerBeat + m_RowsPerMeasure > 0); } // override song time signature? // Returns true if pattern data can be accessed at given row, false otherwise. bool IsValidRow(const ROWINDEX row) const { return (row < GetNumRows()); } // Returns true if any pattern data is present. bool IsValid() const { return !m_ModCommands.empty(); } // Return PatternRow object which has operator[] defined so that ModCommand // at (iRow, iChn) can be accessed with GetRow(iRow)[iChn]. PatternRow GetRow(const ROWINDEX row) { return GetpModCommand(row, 0); } PatternRow GetRow(const ROWINDEX row) const { return const_cast(GetpModCommand(row, 0)); } CHANNELINDEX GetNumChannels() const; // Add or remove rows from the pattern. bool Resize(const ROWINDEX newRowCount, bool enforceFormatLimits = true, bool resizeAtEnd = true); // Check if there is any note data on a given row. bool IsEmptyRow(ROWINDEX row) const; // Allocate new pattern memory and replace old pattern data. bool AllocatePattern(ROWINDEX rows); // Deallocate pattern data. void Deallocate(); // Removes all modcommands from the pattern. void ClearCommands(); // Returns associated soundfile. CSoundFile& GetSoundFile(); const CSoundFile& GetSoundFile() const; const std::vector &GetData() const { return m_ModCommands; } void SetData(std::vector &&data) { MPT_ASSERT(data.size() == GetNumRows() * GetNumChannels()); m_ModCommands = std::move(data); } // Set pattern signature (rows per beat, rows per measure). Returns true on success. bool SetSignature(const ROWINDEX rowsPerBeat, const ROWINDEX rowsPerMeasure); void RemoveSignature() { m_RowsPerBeat = m_RowsPerMeasure = 0; } bool HasTempoSwing() const { return !m_tempoSwing.empty(); } const TempoSwing& GetTempoSwing() const { return m_tempoSwing; } void SetTempoSwing(const TempoSwing &swing) { m_tempoSwing = swing; m_tempoSwing.Normalize(); } void RemoveTempoSwing() { m_tempoSwing.clear(); } // Pattern name functions - bool functions return true on success. bool SetName(const std::string &newName); bool SetName(const char *newName, size_t maxChars); template bool SetName(const char (&buffer)[bufferSize]) { return SetName(buffer, bufferSize); } std::string GetName() const { return m_PatternName; } #ifdef MODPLUG_TRACKER // Double number of rows bool Expand(); // Halve number of rows bool Shrink(); #endif // MODPLUG_TRACKER // Write some kind of effect data to the pattern bool WriteEffect(EffectWriter &settings); typedef std::vector::iterator iterator; typedef std::vector::const_iterator const_iterator; iterator begin() { return m_ModCommands.begin(); } const_iterator begin() const { return m_ModCommands.begin(); } const_iterator cbegin() const { return m_ModCommands.cbegin(); } iterator end() { return m_ModCommands.end(); } const_iterator end() const { return m_ModCommands.end(); } const_iterator cend() const { return m_ModCommands.cend(); } CPattern(CPatternContainer& patCont) : m_rPatternContainer(patCont) {} CPattern(const CPattern &) = default; CPattern(CPattern &&) noexcept = default; protected: ModCommand& GetModCommand(size_t i) { return m_ModCommands[i]; } //Returns modcommand from (floor[i/channelCount], i%channelCount) ModCommand& GetModCommand(ROWINDEX r, CHANNELINDEX c) { return m_ModCommands[r * GetNumChannels() + c]; } const ModCommand& GetModCommand(ROWINDEX r, CHANNELINDEX c) const { return m_ModCommands[r * GetNumChannels() + c]; } protected: std::vector m_ModCommands; ROWINDEX m_Rows = 0; ROWINDEX m_RowsPerBeat = 0; // patterns-specific time signature. if != 0, this is implicitely set. ROWINDEX m_RowsPerMeasure = 0; // ditto TempoSwing m_tempoSwing; std::string m_PatternName; CPatternContainer& m_rPatternContainer; }; const char FileIdPattern[] = "mptP"; void ReadModPattern(std::istream& iStrm, CPattern& patc, const size_t nSize = 0); void WriteModPattern(std::ostream& oStrm, const CPattern& patc); // Class for conveniently writing an effect to the pattern. class EffectWriter { friend class CPattern; // Row advance mode enum RetryMode { rmIgnore, // If effect can't be written, abort. rmTryNextRow, // If effect can't be written, try next row. rmTryPreviousRow, // If effect can't be written, try previous row. }; public: // Constructors with effect commands EffectWriter(EffectCommand cmd, ModCommand::PARAM param) : m_command(cmd), m_param(param), m_isVolEffect(false) { Init(); } EffectWriter(VolumeCommand cmd, ModCommand::VOL param) : m_volcmd(cmd), m_vol(param), m_isVolEffect(true) { Init(); } // Additional constructors: // Set row in which writing should start EffectWriter &Row(ROWINDEX row) { m_row = row; return *this; } // Set channel to which writing should be restricted to EffectWriter &Channel(CHANNELINDEX chn) { m_channel = chn; return *this; } // Allow multiple effects of the same kind to be written in the same row. EffectWriter &AllowMultiple() { m_allowMultiple = true; return *this; } // Set retry mode. EffectWriter &RetryNextRow() { m_retryMode = rmTryNextRow; return *this; } EffectWriter &RetryPreviousRow() { m_retryMode = rmTryPreviousRow; return *this; } protected: RetryMode m_retryMode; ROWINDEX m_row; CHANNELINDEX m_channel; union { EffectCommand m_command; VolumeCommand m_volcmd; }; union { ModCommand::PARAM m_param; ModCommand::VOL m_vol; }; bool m_retry : 1; bool m_allowMultiple : 1; bool m_isVolEffect : 1; // Common data initialisation void Init() { m_row = 0; m_channel = CHANNELINDEX_INVALID; // Any channel m_retryMode = rmIgnore; // If effect couldn't be written, abort. m_retry = true; m_allowMultiple = false; // Stop if same type of effect is encountered } }; OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Resampler.h0000644000175000017500000000712414111407313020175 00000000000000/* * Resampler.h * ----------- * Purpose: Holds the tables for all available resamplers. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "WindowedFIR.h" #include "Mixer.h" #include "MixerSettings.h" #include "Paula.h" OPENMPT_NAMESPACE_BEGIN #ifdef LIBOPENMPT_BUILD // All these optimizations are not applicable to the tracker // because cutoff and firtype are configurable there. // Cache resampler tables across resampler object creation. // A C++11-style function-static singleton is holding the cached values. #define MPT_RESAMPLER_TABLES_CACHED // Prime the tables cache when the library is loaded. // Caching gets triggered via a global object that primes the cache during // construction. // This is only really useful with MPT_RESAMPLER_TABLES_CACHED. //#define MPT_RESAMPLER_TABLES_CACHED_ONSTARTUP #endif // LIBOPENMPT_BUILD #define SINC_WIDTH 8 #define SINC_PHASES_BITS 12 #define SINC_PHASES (1<(chn), chnState[chn].nPatternLoopCount); #endif } } m_hash = hash; } RowVisitor::RowVisitor(const CSoundFile &sndFile, SEQUENCEINDEX sequence) : m_sndFile(sndFile) , m_sequence(sequence) { Initialize(true); } void RowVisitor::MoveVisitedRowsFrom(RowVisitor &other) noexcept { m_visitedRows = std::move(other.m_visitedRows); m_visitedLoopStates = std::move(other.m_visitedLoopStates); } const ModSequence &RowVisitor::Order() const { if(m_sequence >= m_sndFile.Order.GetNumSequences()) return m_sndFile.Order(); else return m_sndFile.Order(m_sequence); } // Resize / clear the row vector. // If reset is true, the vector is not only resized to the required dimensions, but also completely cleared (i.e. all visited rows are reset). void RowVisitor::Initialize(bool reset) { auto &order = Order(); const ORDERINDEX endOrder = order.GetLengthTailTrimmed(); m_visitedRows.resize(endOrder); if(reset) { m_visitedLoopStates.clear(); m_rowsSpentInLoops = 0; } std::vector loopCount; std::vector visitedPatterns(m_sndFile.Patterns.GetNumPatterns(), ORDERINDEX_INVALID); for(ORDERINDEX ord = 0; ord < endOrder; ord++) { const PATTERNINDEX pat = order[ord]; const ROWINDEX numRows = VisitedRowsVectorSize(pat); auto &visitedRows = m_visitedRows[ord]; if(reset) visitedRows.assign(numRows, false); else visitedRows.resize(numRows, false); if(!order.IsValidPat(ord)) continue; const ROWINDEX startRow = std::min(static_cast(reset ? 0 : visitedRows.size()), numRows); auto insertionHint = m_visitedLoopStates.end(); if(visitedPatterns[pat] != ORDERINDEX_INVALID) { // We visited this pattern before, copy over the results const auto begin = m_visitedLoopStates.lower_bound({visitedPatterns[pat], startRow}); const auto end = (begin != m_visitedLoopStates.end()) ? m_visitedLoopStates.lower_bound({visitedPatterns[pat], numRows}) : m_visitedLoopStates.end(); for(auto pos = begin; pos != end; ++pos) { LoopStateSet loopStates; loopStates.reserve(pos->second.capacity()); insertionHint = ++m_visitedLoopStates.insert_or_assign(insertionHint, {ord, pos->first.second}, std::move(loopStates)); } continue; } // Pre-allocate loop count state const auto &pattern = m_sndFile.Patterns[pat]; loopCount.assign(pattern.GetNumChannels(), 0); for(ROWINDEX i = numRows; i != startRow; i--) { const ROWINDEX row = i - 1; uint32 maxLoopStates = 1; auto m = pattern.GetRow(row); // Break condition: If it's more than 16, it's probably wrong :) exact loop count depends on how loops overlap. for(CHANNELINDEX chn = 0; chn < pattern.GetNumChannels() && maxLoopStates < 16; chn++, m++) { auto count = loopCount[chn]; if((m->command == CMD_S3MCMDEX && (m->param & 0xF0) == 0xB0) || (m->command == CMD_MODCMDEX && (m->param & 0xF0) == 0x60)) { loopCount[chn] = (m->param & 0x0F); if(loopCount[chn]) count = loopCount[chn]; } if(count) maxLoopStates *= (count + 1); } if(maxLoopStates > 1) { LoopStateSet loopStates; loopStates.reserve(maxLoopStates); insertionHint = m_visitedLoopStates.insert_or_assign(insertionHint, {ord, row}, std::move(loopStates)); } } // Only use this order as a blueprint for other orders using the same pattern if we fully parsed the pattern. if(startRow == 0) visitedPatterns[pat] = ord; } } // Mark an order/row combination as visited and returns true if it was visited before. bool RowVisitor::Visit(ORDERINDEX ord, ROWINDEX row, const ChannelStates &chnState, bool ignoreRow) { auto &order = Order(); if(ord >= order.size() || row >= VisitedRowsVectorSize(order[ord])) return false; // The module might have been edited in the meantime - so we have to extend this a bit. if(ord >= m_visitedRows.size() || row >= m_visitedRows[ord].size()) { Initialize(false); // If it's still past the end of the vector, this means that ord >= order.GetLengthTailTrimmed(), i.e. we are trying to play an empty order. if(ord >= m_visitedRows.size()) return false; } MPT_ASSERT(chnState.size() >= m_sndFile.GetNumChannels()); LoopState newState{chnState.first(m_sndFile.GetNumChannels()), ignoreRow}; const auto rowLoopState = m_visitedLoopStates.find({ord, row}); const bool oldHadLoops = (rowLoopState != m_visitedLoopStates.end() && !rowLoopState->second.empty()); const bool newHasLoops = newState.HasLoops(); const bool wasVisited = m_visitedRows[ord][row]; // Check if new state is part of row state already. If so, we visited this row already and thus the module must be looping if(!oldHadLoops && !newHasLoops && wasVisited) return true; if(oldHadLoops && mpt::contains(rowLoopState->second, newState)) return true; if(newHasLoops) m_rowsSpentInLoops++; if(oldHadLoops || newHasLoops) { // Convert to set representation if it isn't already if(!oldHadLoops && wasVisited) m_visitedLoopStates[{ord, row}].emplace_back(); m_visitedLoopStates[{ord, row}].emplace_back(std::move(newState)); } m_visitedRows[ord][row] = true; return false; } // Get the needed vector size for a given pattern. ROWINDEX RowVisitor::VisitedRowsVectorSize(PATTERNINDEX pattern) const noexcept { if(m_sndFile.Patterns.IsValidPat(pattern)) return m_sndFile.Patterns[pattern].GetNumRows(); else return 1; // Non-existing patterns consist of a "fake" row. } // Find the first row that has not been played yet. // The order and row is stored in the order and row variables on success, on failure they contain invalid values. // If onlyUnplayedPatterns is true (default), only completely unplayed patterns are considered, otherwise a song can start on any unplayed row. // Function returns true on success. bool RowVisitor::GetFirstUnvisitedRow(ORDERINDEX &ord, ROWINDEX &row, bool onlyUnplayedPatterns) const { const auto &order = Order(); const ORDERINDEX endOrder = order.GetLengthTailTrimmed(); for(ord = 0; ord < endOrder; ord++) { if(!order.IsValidPat(ord)) continue; if(ord >= m_visitedRows.size()) { // Not yet initialized => unvisited row = 0; return true; } const auto &visitedRows = m_visitedRows[ord]; const auto firstUnplayedRow = std::find(visitedRows.begin(), visitedRows.end(), onlyUnplayedPatterns); if(onlyUnplayedPatterns && firstUnplayedRow == visitedRows.end()) { // No row of this pattern has been played yet. row = 0; return true; } else if(!onlyUnplayedPatterns) { // Return the first unplayed row in this pattern if(firstUnplayedRow != visitedRows.end()) { row = static_cast(std::distance(visitedRows.begin(), firstUnplayedRow)); return true; } if(visitedRows.size() < m_sndFile.Patterns[order[ord]].GetNumRows()) { // History is not fully initialized row = static_cast(visitedRows.size()); return true; } } } // Didn't find anything :( ord = ORDERINDEX_INVALID; row = ROWINDEX_INVALID; return false; } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/RowVisitor.h0000644000175000017500000000764414052666041020412 00000000000000/* * RowVisitor.h * ------------ * Purpose: Class for recording which rows of a song has already been visited, used for detecting when a module starts to loop. * Notes : See implementation file. * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "mpt/base/span.hpp" #include "Snd_defs.h" #include OPENMPT_NAMESPACE_BEGIN #if defined(MPT_BUILD_DEBUG) || defined(MPT_BUILD_FUZZER) #define MPT_VERIFY_ROWVISITOR_LOOPSTATE #endif // MPT_BUILD_DEBUG || MPT_BUILD_FUZZER class CSoundFile; class ModSequence; struct ModChannel; class RowVisitor { protected: using ChannelStates = mpt::span; class LoopState { static constexpr uint64 FNV1a_BASIS = 14695981039346656037ull; static constexpr uint64 FNV1a_PRIME = 1099511628211ull; uint64 m_hash = FNV1a_BASIS; #ifdef MPT_VERIFY_ROWVISITOR_LOOPSTATE std::vector> m_counts; // Actual loop counts to verify equality of hash-based implementation #endif public: LoopState() = default; LoopState(const ChannelStates &chnState, const bool ignoreRow); LoopState(const LoopState &) = default; LoopState(LoopState&&) = default; LoopState &operator=(const LoopState &) = default; LoopState &operator=(LoopState&&) = default; [[nodiscard]] bool operator==(const LoopState &other) const noexcept { #ifdef MPT_VERIFY_ROWVISITOR_LOOPSTATE if((m_counts == other.m_counts) != (m_hash == other.m_hash)) std::abort(); #endif return m_hash == other.m_hash; } [[nodiscard]] bool HasLoops() const noexcept { #ifdef MPT_VERIFY_ROWVISITOR_LOOPSTATE if(m_counts.empty() != (m_hash == FNV1a_BASIS)) std::abort(); #endif return m_hash != FNV1a_BASIS; } }; using LoopStateSet = std::vector; // Stores for every (order, row) combination in the sequence if it has been visited or not. std::vector> m_visitedRows; // Map for each row that's part of a pattern loop which loop states have been visited. Held in a separate data structure because it is sparse data in typical modules. std::map, LoopStateSet> m_visitedLoopStates; const CSoundFile &m_sndFile; ROWINDEX m_rowsSpentInLoops = 0; const SEQUENCEINDEX m_sequence; public: RowVisitor(const CSoundFile &sndFile, SEQUENCEINDEX sequence = SEQUENCEINDEX_INVALID); void MoveVisitedRowsFrom(RowVisitor &other) noexcept; // Resize / Clear the row vector. // If reset is true, the vector is not only resized to the required dimensions, but also completely cleared (i.e. all visited rows are unset). void Initialize(bool reset); // Mark an order/row combination as visited and returns true if it was visited before. bool Visit(ORDERINDEX ord, ROWINDEX row, const ChannelStates &chnState, bool ignoreRow); // Find the first row that has not been played yet. // The order and row is stored in the order and row variables on success, on failure they contain invalid values. // If onlyUnplayedPatterns is true (default), only completely unplayed patterns are considered, otherwise a song can start anywhere. // Function returns true on success. [[nodiscard]] bool GetFirstUnvisitedRow(ORDERINDEX &order, ROWINDEX &row, bool onlyUnplayedPatterns) const; // Pattern loops can stack up exponentially, which can cause an effectively infinite amount of time to be spent on evaluating them. // If this function returns true, module evaluation should be aborted because the pattern loops appear to be too complex. [[nodiscard]] bool ModuleTooComplex(ROWINDEX threshold) const noexcept { return m_rowsSpentInLoops >= threshold; } void ResetComplexity() { m_rowsSpentInLoops = 0; } protected: // Get the needed vector size for a given pattern. [[nodiscard]] ROWINDEX VisitedRowsVectorSize(PATTERNINDEX pattern) const noexcept; [[nodiscard]] const ModSequence &Order() const; }; OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/S3MTools.cpp0000644000175000017500000001012514147260247020230 00000000000000/* * S3MTools.cpp * ------------ * Purpose: Definition of S3M file structures and helper functions * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Loaders.h" #include "S3MTools.h" #include "../common/mptStringBuffer.h" OPENMPT_NAMESPACE_BEGIN // Convert an S3M sample header to OpenMPT's internal sample header. void S3MSampleHeader::ConvertToMPT(ModSample &mptSmp, bool isST3) const { mptSmp.Initialize(MOD_TYPE_S3M); mptSmp.filename = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, filename); if(sampleType == typePCM || sampleType == typeNone) { // Sample Length and Loops if(sampleType == typePCM) { mptSmp.nLength = length; mptSmp.nLoopStart = std::min(static_cast(loopStart), mptSmp.nLength - 1); mptSmp.nLoopEnd = std::min(static_cast(loopEnd), mptSmp.nLength); mptSmp.uFlags.set(CHN_LOOP, (flags & smpLoop) != 0); } if(mptSmp.nLoopEnd < 2 || mptSmp.nLoopStart >= mptSmp.nLoopEnd || mptSmp.nLoopEnd - mptSmp.nLoopStart < 1) { mptSmp.nLoopStart = mptSmp.nLoopEnd = 0; mptSmp.uFlags.reset(); } } else if(sampleType == typeAdMel) { OPLPatch patch; std::memcpy(patch.data() + 0, mpt::as_raw_memory(length).data(), 4); std::memcpy(patch.data() + 4, mpt::as_raw_memory(loopStart).data(), 4); std::memcpy(patch.data() + 8, mpt::as_raw_memory(loopEnd).data(), 4); mptSmp.SetAdlib(true, patch); } // Volume / Panning mptSmp.nVolume = std::min(defaultVolume.get(), uint8(64)) * 4; // C-5 frequency mptSmp.nC5Speed = c5speed; if(isST3) { // ST3 ignores or clamps the high 16 bits depending on the instrument type if(sampleType == typeAdMel) mptSmp.nC5Speed &= 0xFFFF; else LimitMax(mptSmp.nC5Speed, uint16_max); } if(mptSmp.nC5Speed == 0) mptSmp.nC5Speed = 8363; else if(mptSmp.nC5Speed < 1024) mptSmp.nC5Speed = 1024; } // Convert OpenMPT's internal sample header to an S3M sample header. SmpLength S3MSampleHeader::ConvertToS3M(const ModSample &mptSmp) { SmpLength smpLength = 0; mpt::String::WriteBuf(mpt::String::maybeNullTerminated, filename) = mptSmp.filename; memcpy(magic, "SCRS", 4); if(mptSmp.uFlags[CHN_ADLIB]) { memcpy(magic, "SCRI", 4); sampleType = typeAdMel; std::memcpy(mpt::as_raw_memory(length ).data(), mptSmp.adlib.data() + 0, 4); std::memcpy(mpt::as_raw_memory(loopStart).data(), mptSmp.adlib.data() + 4, 4); std::memcpy(mpt::as_raw_memory(loopEnd ).data(), mptSmp.adlib.data() + 8, 4); } else if(mptSmp.HasSampleData()) { sampleType = typePCM; length = mpt::saturate_cast(mptSmp.nLength); loopStart = mpt::saturate_cast(mptSmp.nLoopStart); loopEnd = mpt::saturate_cast(mptSmp.nLoopEnd); smpLength = length; flags = (mptSmp.uFlags[CHN_LOOP] ? smpLoop : 0); if(mptSmp.uFlags[CHN_16BIT]) { flags |= smp16Bit; } if(mptSmp.uFlags[CHN_STEREO]) { flags |= smpStereo; } } else { sampleType = typeNone; } defaultVolume = static_cast(std::min(static_cast(mptSmp.nVolume / 4), uint16(64))); if(mptSmp.nC5Speed != 0) { c5speed = mptSmp.nC5Speed; } else { c5speed = ModSample::TransposeToFrequency(mptSmp.RelativeTone, mptSmp.nFineTune); } return smpLength; } // Retrieve the internal sample format flags for this sample. SampleIO S3MSampleHeader::GetSampleFormat(bool signedSamples) const { if(pack == S3MSampleHeader::pADPCM && !(flags & S3MSampleHeader::smp16Bit) && !(flags & S3MSampleHeader::smpStereo)) { // MODPlugin :( return SampleIO(SampleIO::_8bit, SampleIO::mono, SampleIO::littleEndian, SampleIO::ADPCM); } else { return SampleIO( (flags & S3MSampleHeader::smp16Bit) ? SampleIO::_16bit : SampleIO::_8bit, (flags & S3MSampleHeader::smpStereo) ? SampleIO::stereoSplit : SampleIO::mono, SampleIO::littleEndian, signedSamples ? SampleIO::signedPCM : SampleIO::unsignedPCM); } } // Calculate the sample position in file uint32 S3MSampleHeader::GetSampleOffset() const { return (dataPointer[1] << 4) | (dataPointer[2] << 12) | (dataPointer[0] << 20); } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/S3MTools.h0000644000175000017500000001202214147757151017700 00000000000000/* * S3MTools.h * ---------- * Purpose: Definition of S3M file structures and helper functions * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "../soundlib/ModSample.h" #include "../soundlib/SampleIO.h" OPENMPT_NAMESPACE_BEGIN // S3M File Header struct S3MFileHeader { // Magic Bytes enum S3MMagic { idEOF = 0x1A, idS3MType = 0x10, idPanning = 0xFC, }; // Tracker Versions in the cwtv field enum S3MTrackerVersions { trackerMask = 0xF000, versionMask = 0x0FFF, trkScreamTracker = 0x1000, trkImagoOrpheus = 0x2000, trkImpulseTracker = 0x3000, trkSchismTracker = 0x4000, trkOpenMPT = 0x5000, trkBeRoTracker = 0x6000, trkCreamTracker = 0x7000, trkAkord = 0x0208, trkST3_00 = 0x1300, trkST3_20 = 0x1320, trkST3_01 = 0x1301, trkIT2_07 = 0x3207, trkIT2_14 = 0x3214, trkBeRoTrackerOld = 0x4100, // Used from 2004 to 2012 trkCamoto = 0xCA00, }; // Flags enum S3MHeaderFlags { st2Vibrato = 0x01, // Vibrato is twice as deep. Cannot be enabled from UI. zeroVolOptim = 0x08, // Volume 0 optimisations amigaLimits = 0x10, // Enforce Amiga limits fastVolumeSlides = 0x40, // Fast volume slides (like in ST3.00) }; // S3M Format Versions enum S3MFormatVersion { oldVersion = 0x01, // Old Version, signed samples newVersion = 0x02, // New Version, unsigned samples }; char name[28]; // Song Title uint8le dosEof; // Supposed to be 0x1A, but even ST3 seems to ignore this sometimes (see STRSHINE.S3M by Purple Motion) uint8le fileType; // File Type, 0x10 = ST3 module char reserved1[2]; // Reserved uint16le ordNum; // Number of order items uint16le smpNum; // Number of sample parapointers uint16le patNum; // Number of pattern parapointers uint16le flags; // Flags, see S3MHeaderFlags uint16le cwtv; // "Made With" Tracker ID, see S3MTrackerVersions uint16le formatVersion; // Format Version, see S3MFormatVersion char magic[4]; // "SCRM" magic bytes uint8le globalVol; // Default Global Volume (0...64) uint8le speed; // Default Speed (1...254) uint8le tempo; // Default Tempo (33...255) uint8le masterVolume; // Sample Volume (0...127, stereo if high bit is set) uint8le ultraClicks; // Number of channels used for ultra click removal uint8le usePanningTable; // 0xFC => read extended panning table uint16le reserved2; // Schism Tracker and OpenMPT use this for their extended version information uint32le reserved3; // Impulse Tracker hides its edit timer here uint16le reserved4; uint16le special; // Pointer to special custom data (unused) uint8le channels[32]; // Channel setup }; MPT_BINARY_STRUCT(S3MFileHeader, 96) // S3M Sample Header struct S3MSampleHeader { enum SampleType { typeNone = 0, typePCM = 1, typeAdMel = 2, }; enum SampleFlags { smpLoop = 0x01, smpStereo = 0x02, smp16Bit = 0x04, }; enum SamplePacking { pUnpacked = 0x00, // PCM pDP30ADPCM = 0x01, // Unused packing type pADPCM = 0x04, // MODPlugin ADPCM :( }; uint8le sampleType; // Sample type, see SampleType char filename[12]; // Sample filename uint8le dataPointer[3]; // Pointer to sample data (divided by 16) uint32le length; // Sample length, in samples uint32le loopStart; // Loop start, in samples uint32le loopEnd; // Loop end, in samples uint8le defaultVolume; // Default volume (0...64) char reserved1; // Reserved uint8le pack; // Packing algorithm, SamplePacking uint8le flags; // Sample flags uint32le c5speed; // Middle-C frequency char reserved2[4]; // Reserved uint16le gusAddress; // Sample address in GUS memory (used for fingerprinting) uint16le sb512; // SoundBlaster loop expansion stuff uint32le lastUsedPos; // More SoundBlaster stuff char name[28]; // Sample name char magic[4]; // "SCRS" magic bytes ("SCRI" for Adlib instruments) // Convert an S3M sample header to OpenMPT's internal sample header. void ConvertToMPT(ModSample &mptSmp, bool isST3 = false) const; // Convert OpenMPT's internal sample header to an S3M sample header. SmpLength ConvertToS3M(const ModSample &mptSmp); // Retrieve the internal sample format flags for this sample. SampleIO GetSampleFormat(bool signedSamples) const; // Calculate the sample position in file uint32 GetSampleOffset() const; }; MPT_BINARY_STRUCT(S3MSampleHeader, 80) // Pattern decoding flags enum S3MPattern { s3mEndOfRow = 0x00, s3mChannelMask = 0x1F, s3mNotePresent = 0x20, s3mVolumePresent = 0x40, s3mEffectPresent = 0x80, s3mAnyPresent = 0xE0, s3mNoteOff = 0xFE, s3mNoteNone = 0xFF, }; OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/SampleCopy.h0000644000175000017500000000342414053010430020311 00000000000000/* * SampleCopy.h * ------------ * Purpose: Functions for copying sample data. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "openmpt/soundbase/SampleConvert.hpp" #include "openmpt/soundbase/SampleDecode.hpp" OPENMPT_NAMESPACE_BEGIN // Copy a sample data buffer. // targetBuffer: Buffer in which the sample should be copied into. // numSamples: Number of samples of size T that should be copied. targetBuffer is expected to be able to hold "numSamples * incTarget" samples. // incTarget: Number of samples by which the target data pointer is increased each time. // sourceBuffer: Buffer from which the samples should be read. // sourceSize: Size of source buffer, in bytes. // incSource: Number of samples by which the source data pointer is increased each time. // // Template arguments: // SampleConversion: Functor of type SampleConversionFunctor to apply sample conversion (see above for existing functors). template size_t CopySample(typename SampleConversion::output_t *MPT_RESTRICT outBuf, size_t numSamples, size_t incTarget, const typename SampleConversion::input_t *MPT_RESTRICT inBuf, size_t sourceSize, size_t incSource, SampleConversion conv = SampleConversion()) { const size_t sampleSize = incSource * SampleConversion::input_inc * sizeof(typename SampleConversion::input_t); LimitMax(numSamples, sourceSize / sampleSize); const size_t copySize = numSamples * sampleSize; SampleConversion sampleConv(conv); while(numSamples--) { *outBuf = sampleConv(inBuf); outBuf += incTarget; inBuf += incSource * SampleConversion::input_inc; } return copySize; } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/SampleFormats.cpp0000644000175000017500000022171514147260247021373 00000000000000/* * SampleFormats.cpp * ----------------- * Purpose: Code for loading various more or less common sample and instrument formats. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Sndfile.h" #include "mod_specifications.h" #ifdef MODPLUG_TRACKER #include "../mptrack/Moddoc.h" #include "Dlsbank.h" #endif // MODPLUG_TRACKER #include "../soundlib/AudioCriticalSection.h" #ifndef MODPLUG_NO_FILESAVE #include "mpt/io/base.hpp" #include "mpt/io/io.hpp" #include "mpt/io/io_stdstream.hpp" #include "../common/mptFileIO.h" #endif // !MODPLUG_NO_FILESAVE #include "../common/misc_util.h" #include "openmpt/base/Endian.hpp" #include "Tagging.h" #include "ITTools.h" #include "XMTools.h" #include "S3MTools.h" #include "WAVTools.h" #include "../common/version.h" #include "Loaders.h" #include "../common/FileReader.h" #include "../soundlib/ModSampleCopy.h" #include #include OPENMPT_NAMESPACE_BEGIN using namespace mpt::uuid_literals; bool CSoundFile::ReadSampleFromFile(SAMPLEINDEX nSample, FileReader &file, bool mayNormalize, bool includeInstrumentFormats) { if(!nSample || nSample >= MAX_SAMPLES) return false; if(!ReadWAVSample(nSample, file, mayNormalize) && !(includeInstrumentFormats && ReadXISample(nSample, file)) && !(includeInstrumentFormats && ReadITISample(nSample, file)) && !ReadW64Sample(nSample, file) && !ReadCAFSample(nSample, file) && !ReadAIFFSample(nSample, file, mayNormalize) && !ReadITSSample(nSample, file) && !(includeInstrumentFormats && ReadPATSample(nSample, file)) && !ReadIFFSample(nSample, file) && !ReadS3ISample(nSample, file) && !ReadSBISample(nSample, file) && !ReadAUSample(nSample, file, mayNormalize) && !ReadBRRSample(nSample, file) && !ReadFLACSample(nSample, file) && !ReadOpusSample(nSample, file) && !ReadVorbisSample(nSample, file) && !ReadMP3Sample(nSample, file, false) && !ReadMediaFoundationSample(nSample, file) ) { return false; } if(nSample > GetNumSamples()) { m_nSamples = nSample; } if(Samples[nSample].uFlags[CHN_ADLIB]) { InitOPL(); } return true; } bool CSoundFile::ReadInstrumentFromFile(INSTRUMENTINDEX nInstr, FileReader &file, bool mayNormalize) { if ((!nInstr) || (nInstr >= MAX_INSTRUMENTS)) return false; if(!ReadITIInstrument(nInstr, file) && !ReadXIInstrument(nInstr, file) && !ReadPATInstrument(nInstr, file) && !ReadSFZInstrument(nInstr, file) // Generic read && !ReadSampleAsInstrument(nInstr, file, mayNormalize)) { bool ok = false; #ifdef MODPLUG_TRACKER CDLSBank bank; if(bank.Open(file)) { ok = bank.ExtractInstrument(*this, nInstr, 0, 0); } #endif // MODPLUG_TRACKER if(!ok) return false; } if(nInstr > GetNumInstruments()) m_nInstruments = nInstr; return true; } bool CSoundFile::ReadSampleAsInstrument(INSTRUMENTINDEX nInstr, FileReader &file, bool mayNormalize) { // Scanning free sample SAMPLEINDEX nSample = GetNextFreeSample(nInstr); // may also return samples which are only referenced by the current instrument if(nSample == SAMPLEINDEX_INVALID) { return false; } // Loading Instrument ModInstrument *pIns = new (std::nothrow) ModInstrument(nSample); if(pIns == nullptr) { return false; } if(!ReadSampleFromFile(nSample, file, mayNormalize, false)) { delete pIns; return false; } // Remove all samples which are only referenced by the old instrument, except for the one we just loaded our new sample into. RemoveInstrumentSamples(nInstr, nSample); // Replace the instrument DestroyInstrument(nInstr, doNoDeleteAssociatedSamples); Instruments[nInstr] = pIns; #if defined(MPT_ENABLE_FILEIO) && defined(MPT_EXTERNAL_SAMPLES) SetSamplePath(nSample, file.GetOptionalFileName().value_or(P_(""))); #endif return true; } bool CSoundFile::DestroyInstrument(INSTRUMENTINDEX nInstr, deleteInstrumentSamples removeSamples) { if(nInstr == 0 || nInstr >= MAX_INSTRUMENTS || !Instruments[nInstr]) return true; if(removeSamples == deleteAssociatedSamples) { RemoveInstrumentSamples(nInstr); } CriticalSection cs; ModInstrument *pIns = Instruments[nInstr]; Instruments[nInstr] = nullptr; for(auto &chn : m_PlayState.Chn) { if(chn.pModInstrument == pIns) chn.pModInstrument = nullptr; } delete pIns; return true; } // Remove all unused samples from the given nInstr and keep keepSample if provided bool CSoundFile::RemoveInstrumentSamples(INSTRUMENTINDEX nInstr, SAMPLEINDEX keepSample) { if(Instruments[nInstr] == nullptr) { return false; } std::vector keepSamples(GetNumSamples() + 1, true); // Check which samples are used by the instrument we are going to nuke. auto referencedSamples = Instruments[nInstr]->GetSamples(); for(auto sample : referencedSamples) { if(sample <= GetNumSamples()) { keepSamples[sample] = false; } } // If we want to keep a specific sample, do so. if(keepSample != SAMPLEINDEX_INVALID) { if(keepSample <= GetNumSamples()) { keepSamples[keepSample] = true; } } // Check if any of those samples are referenced by other instruments as well, in which case we want to keep them of course. for(INSTRUMENTINDEX nIns = 1; nIns <= GetNumInstruments(); nIns++) if (Instruments[nIns] != nullptr && nIns != nInstr) { Instruments[nIns]->GetSamples(keepSamples); } // Now nuke the selected samples. RemoveSelectedSamples(keepSamples); return true; } //////////////////////////////////////////////////////////////////////////////// // // I/O From another song // bool CSoundFile::ReadInstrumentFromSong(INSTRUMENTINDEX targetInstr, const CSoundFile &srcSong, INSTRUMENTINDEX sourceInstr) { if ((!sourceInstr) || (sourceInstr > srcSong.GetNumInstruments()) || (targetInstr >= MAX_INSTRUMENTS) || (!srcSong.Instruments[sourceInstr])) { return false; } if (m_nInstruments < targetInstr) m_nInstruments = targetInstr; ModInstrument *pIns = new (std::nothrow) ModInstrument(); if(pIns == nullptr) { return false; } DestroyInstrument(targetInstr, deleteAssociatedSamples); Instruments[targetInstr] = pIns; *pIns = *srcSong.Instruments[sourceInstr]; std::vector sourceSample; // Sample index in source song std::vector targetSample; // Sample index in target song SAMPLEINDEX targetIndex = 0; // Next index for inserting sample for(auto &sample : pIns->Keyboard) { const SAMPLEINDEX sourceIndex = sample; if(sourceIndex > 0 && sourceIndex <= srcSong.GetNumSamples()) { const auto entry = std::find(sourceSample.cbegin(), sourceSample.cend(), sourceIndex); if(entry == sourceSample.end()) { // Didn't consider this sample yet, so add it to our map. targetIndex = GetNextFreeSample(targetInstr, targetIndex + 1); if(targetIndex <= GetModSpecifications().samplesMax) { sourceSample.push_back(sourceIndex); targetSample.push_back(targetIndex); sample = targetIndex; } else { sample = 0; } } else { // Sample reference has already been created, so only need to update the sample map. sample = *(entry - sourceSample.begin() + targetSample.begin()); } } else { // Invalid or no source sample sample = 0; } } #ifdef MODPLUG_TRACKER if(pIns->filename.empty() && srcSong.GetpModDoc() != nullptr && &srcSong != this) { pIns->filename = srcSong.GetpModDoc()->GetPathNameMpt().GetFullFileName().ToLocale(); } #endif pIns->Convert(srcSong.GetType(), GetType()); // Copy all referenced samples over for(size_t i = 0; i < targetSample.size(); i++) { ReadSampleFromSong(targetSample[i], srcSong, sourceSample[i]); } return true; } bool CSoundFile::ReadSampleFromSong(SAMPLEINDEX targetSample, const CSoundFile &srcSong, SAMPLEINDEX sourceSample) { if(!sourceSample || sourceSample > srcSong.GetNumSamples() || (targetSample >= GetModSpecifications().samplesMax && targetSample > GetNumSamples())) { return false; } DestroySampleThreadsafe(targetSample); const ModSample &sourceSmp = srcSong.GetSample(sourceSample); ModSample &targetSmp = GetSample(targetSample); if(GetNumSamples() < targetSample) m_nSamples = targetSample; targetSmp = sourceSmp; m_szNames[targetSample] = srcSong.m_szNames[sourceSample]; if(sourceSmp.HasSampleData()) { if(targetSmp.CopyWaveform(sourceSmp)) targetSmp.PrecomputeLoops(*this, false); // Remember on-disk path (for MPTM files), but don't implicitely enable on-disk storage // (we really don't want this for e.g. duplicating samples or splitting stereo samples) #ifdef MPT_EXTERNAL_SAMPLES SetSamplePath(targetSample, srcSong.GetSamplePath(sourceSample)); #endif targetSmp.uFlags.reset(SMP_KEEPONDISK); } #ifdef MODPLUG_TRACKER if((targetSmp.filename.empty()) && srcSong.GetpModDoc() != nullptr && &srcSong != this) { targetSmp.filename = mpt::ToCharset(GetCharsetInternal(), srcSong.GetpModDoc()->GetTitle()); } #endif if(targetSmp.uFlags[CHN_ADLIB] && !SupportsOPL()) { AddToLog(LogInformation, U_("OPL instruments are not supported by this format.")); } targetSmp.Convert(srcSong.GetType(), GetType()); if(targetSmp.uFlags[CHN_ADLIB]) { InitOPL(); } return true; } //////////////////////////////////////////////////////////////////////// // IMA ADPCM Support for WAV files static bool IMAADPCMUnpack16(int16 *target, SmpLength sampleLen, FileReader file, uint16 blockAlign, uint32 numChannels) { static constexpr int8 IMAIndexTab[8] = { -1, -1, -1, -1, 2, 4, 6, 8 }; static constexpr int16 IMAUnpackTable[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 }; if(target == nullptr || blockAlign < 4u * numChannels) return false; SmpLength samplePos = 0; sampleLen *= numChannels; while(file.CanRead(4u * numChannels) && samplePos < sampleLen) { FileReader block = file.ReadChunk(blockAlign); FileReader::PinnedView blockView = block.GetPinnedView(); const std::byte *data = blockView.data(); const uint32 blockSize = static_cast(blockView.size()); for(uint32 chn = 0; chn < numChannels; chn++) { // Block header int32 value = block.ReadInt16LE(); int32 nIndex = block.ReadUint8(); Limit(nIndex, 0, 89); block.Skip(1); SmpLength smpPos = samplePos + chn; uint32 dataPos = (numChannels + chn) * 4; // Block data while(smpPos <= (sampleLen - 8) && dataPos <= (blockSize - 4)) { for(uint32 i = 0; i < 8; i++) { uint8 delta = mpt::byte_cast(data[dataPos]); if(i & 1) { delta >>= 4; dataPos++; } else { delta &= 0x0F; } int32 v = IMAUnpackTable[nIndex] >> 3; if (delta & 1) v += IMAUnpackTable[nIndex] >> 2; if (delta & 2) v += IMAUnpackTable[nIndex] >> 1; if (delta & 4) v += IMAUnpackTable[nIndex]; if (delta & 8) value -= v; else value += v; nIndex += IMAIndexTab[delta & 7]; Limit(nIndex, 0, 88); Limit(value, -32768, 32767); target[smpPos] = static_cast(value); smpPos += numChannels; } dataPos += (numChannels - 1) * 4u; } } samplePos += ((blockSize - (numChannels * 4u)) * 2u); } return true; } //////////////////////////////////////////////////////////////////////////////// // WAV Open bool CSoundFile::ReadWAVSample(SAMPLEINDEX nSample, FileReader &file, bool mayNormalize, FileReader *wsmpChunk) { WAVReader wavFile(file); static constexpr WAVFormatChunk::SampleFormats SupportedFormats[] = {WAVFormatChunk::fmtPCM, WAVFormatChunk::fmtFloat, WAVFormatChunk::fmtIMA_ADPCM, WAVFormatChunk::fmtMP3, WAVFormatChunk::fmtALaw, WAVFormatChunk::fmtULaw}; if(!wavFile.IsValid() || wavFile.GetNumChannels() == 0 || wavFile.GetNumChannels() > 2 || (wavFile.GetBitsPerSample() == 0 && wavFile.GetSampleFormat() != WAVFormatChunk::fmtMP3) || (wavFile.GetBitsPerSample() < 32 && wavFile.GetSampleFormat() == WAVFormatChunk::fmtFloat) || (wavFile.GetBitsPerSample() > 64) || !mpt::contains(SupportedFormats, wavFile.GetSampleFormat())) { return false; } DestroySampleThreadsafe(nSample); m_szNames[nSample] = ""; ModSample &sample = Samples[nSample]; sample.Initialize(); sample.nLength = wavFile.GetSampleLength(); sample.nC5Speed = wavFile.GetSampleRate(); wavFile.ApplySampleSettings(sample, GetCharsetInternal(), m_szNames[nSample]); FileReader sampleChunk = wavFile.GetSampleData(); SampleIO sampleIO( SampleIO::_8bit, (wavFile.GetNumChannels() > 1) ? SampleIO::stereoInterleaved : SampleIO::mono, SampleIO::littleEndian, SampleIO::signedPCM); if(wavFile.GetSampleFormat() == WAVFormatChunk::fmtIMA_ADPCM && wavFile.GetNumChannels() <= 2) { // IMA ADPCM 4:1 LimitMax(sample.nLength, MAX_SAMPLE_LENGTH); sample.uFlags.set(CHN_16BIT); sample.uFlags.set(CHN_STEREO, wavFile.GetNumChannels() == 2); if(!sample.AllocateSample()) { return false; } IMAADPCMUnpack16(sample.sample16(), sample.nLength, sampleChunk, wavFile.GetBlockAlign(), wavFile.GetNumChannels()); sample.PrecomputeLoops(*this, false); } else if(wavFile.GetSampleFormat() == WAVFormatChunk::fmtMP3) { // MP3 in WAV bool loadedMP3 = ReadMP3Sample(nSample, sampleChunk, false, true) || ReadMediaFoundationSample(nSample, sampleChunk, true); if(!loadedMP3) { return false; } } else if(!wavFile.IsExtensibleFormat() && wavFile.MayBeCoolEdit16_8() && wavFile.GetSampleFormat() == WAVFormatChunk::fmtPCM && wavFile.GetBitsPerSample() == 32 && wavFile.GetBlockAlign() == wavFile.GetNumChannels() * 4) { // Syntrillium Cool Edit hack to store IEEE 32bit floating point // Format is described as 32bit integer PCM contained in 32bit blocks and an WAVEFORMATEX extension size of 2 which contains a single 16 bit little endian value of 1. // (This is parsed in WAVTools.cpp and returned via MayBeCoolEdit16_8()). // The data actually stored in this case is little endian 32bit floating point PCM with 2**15 full scale. // Cool Edit calls this format "16.8 float". sampleIO |= SampleIO::_32bit; sampleIO |= SampleIO::floatPCM15; sampleIO.ReadSample(sample, sampleChunk); } else if(!wavFile.IsExtensibleFormat() && wavFile.GetSampleFormat() == WAVFormatChunk::fmtPCM && wavFile.GetBitsPerSample() == 24 && wavFile.GetBlockAlign() == wavFile.GetNumChannels() * 4) { // Syntrillium Cool Edit hack to store IEEE 32bit floating point // Format is described as 24bit integer PCM contained in 32bit blocks. // The data actually stored in this case is little endian 32bit floating point PCM with 2**23 full scale. // Cool Edit calls this format "24.0 float". sampleIO |= SampleIO::_32bit; sampleIO |= SampleIO::floatPCM23; sampleIO.ReadSample(sample, sampleChunk); } else if(wavFile.GetSampleFormat() == WAVFormatChunk::fmtALaw || wavFile.GetSampleFormat() == WAVFormatChunk::fmtULaw) { // a-law / u-law sampleIO |= SampleIO::_16bit; sampleIO |= wavFile.GetSampleFormat() == WAVFormatChunk::fmtALaw ? SampleIO::aLaw : SampleIO::uLaw; sampleIO.ReadSample(sample, sampleChunk); } else { // PCM / Float SampleIO::Bitdepth bitDepth; switch((wavFile.GetBitsPerSample() - 1) / 8u) { default: case 0: bitDepth = SampleIO::_8bit; break; case 1: bitDepth = SampleIO::_16bit; break; case 2: bitDepth = SampleIO::_24bit; break; case 3: bitDepth = SampleIO::_32bit; break; case 7: bitDepth = SampleIO::_64bit; break; } sampleIO |= bitDepth; if(wavFile.GetBitsPerSample() <= 8) sampleIO |= SampleIO::unsignedPCM; if(wavFile.GetSampleFormat() == WAVFormatChunk::fmtFloat) sampleIO |= SampleIO::floatPCM; if(mayNormalize) sampleIO.MayNormalize(); sampleIO.ReadSample(sample, sampleChunk); } if(wsmpChunk != nullptr) { // DLS WSMP chunk *wsmpChunk = wavFile.GetWsmpChunk(); } sample.Convert(MOD_TYPE_IT, GetType()); sample.PrecomputeLoops(*this, false); return true; } /////////////////////////////////////////////////////////////// // Save WAV #ifndef MODPLUG_NO_FILESAVE bool CSoundFile::SaveWAVSample(SAMPLEINDEX nSample, std::ostream &f) const { const ModSample &sample = Samples[nSample]; if(sample.uFlags[CHN_ADLIB]) return false; mpt::IO::OFile ff(f); WAVWriter file(ff); file.WriteFormat(sample.GetSampleRate(GetType()), sample.GetElementarySampleSize() * 8, sample.GetNumChannels(), WAVFormatChunk::fmtPCM); // Write sample data file.StartChunk(RIFFChunk::iddata); file.Skip(SampleIO( sample.uFlags[CHN_16BIT] ? SampleIO::_16bit : SampleIO::_8bit, sample.uFlags[CHN_STEREO] ? SampleIO::stereoInterleaved : SampleIO::mono, SampleIO::littleEndian, sample.uFlags[CHN_16BIT] ? SampleIO::signedPCM : SampleIO::unsignedPCM) .WriteSample(f, sample)); file.WriteLoopInformation(sample); file.WriteExtraInformation(sample, GetType()); if(sample.HasCustomCuePoints()) { file.WriteCueInformation(sample); } FileTags tags; tags.SetEncoder(); tags.title = mpt::ToUnicode(GetCharsetInternal(), m_szNames[nSample]); file.WriteMetatags(tags); file.Finalize(); return true; } #endif // MODPLUG_NO_FILESAVE ///////////////// // Sony Wave64 // struct Wave64FileHeader { mpt::GUIDms GuidRIFF; uint64le FileSize; mpt::GUIDms GuidWAVE; }; MPT_BINARY_STRUCT(Wave64FileHeader, 40) struct Wave64ChunkHeader { mpt::GUIDms GuidChunk; uint64le Size; }; MPT_BINARY_STRUCT(Wave64ChunkHeader, 24) struct Wave64Chunk { Wave64ChunkHeader header; FileReader::off_t GetLength() const { uint64 length = header.Size; if(length < sizeof(Wave64ChunkHeader)) { length = 0; } else { length -= sizeof(Wave64ChunkHeader); } return mpt::saturate_cast(length); } mpt::UUID GetID() const { return mpt::UUID(header.GuidChunk); } }; MPT_BINARY_STRUCT(Wave64Chunk, 24) static void Wave64TagFromLISTINFO(mpt::ustring & dst, uint16 codePage, const FileReader::ChunkList & infoChunk, RIFFChunk::ChunkIdentifiers id) { if(!infoChunk.ChunkExists(id)) { return; } FileReader textChunk = infoChunk.GetChunk(id); if(!textChunk.IsValid()) { return; } std::string str; textChunk.ReadString(str, textChunk.GetLength()); str = mpt::replace(str, std::string("\r\n"), std::string("\n")); str = mpt::replace(str, std::string("\r"), std::string("\n")); dst = mpt::ToUnicode(codePage, mpt::Charset::Windows1252, str); } bool CSoundFile::ReadW64Sample(SAMPLEINDEX nSample, FileReader &file, bool mayNormalize) { file.Rewind(); constexpr mpt::UUID guidRIFF = "66666972-912E-11CF-A5D6-28DB04C10000"_uuid; constexpr mpt::UUID guidWAVE = "65766177-ACF3-11D3-8CD1-00C04F8EDB8A"_uuid; constexpr mpt::UUID guidLIST = "7473696C-912F-11CF-A5D6-28DB04C10000"_uuid; constexpr mpt::UUID guidFMT = "20746D66-ACF3-11D3-8CD1-00C04F8EDB8A"_uuid; //constexpr mpt::UUID guidFACT = "74636166-ACF3-11D3-8CD1-00C04F8EDB8A"_uuid; constexpr mpt::UUID guidDATA = "61746164-ACF3-11D3-8CD1-00C04F8EDB8A"_uuid; //constexpr mpt::UUID guidLEVL = "6C76656C-ACF3-11D3-8CD1-00C04F8EDB8A"_uuid; //constexpr mpt::UUID guidJUNK = "6b6E756A-ACF3-11D3-8CD1-00C04f8EDB8A"_uuid; //constexpr mpt::UUID guidBEXT = "74786562-ACF3-11D3-8CD1-00C04F8EDB8A"_uuid; //constexpr mpt::UUID guiMARKER = "ABF76256-392D-11D2-86C7-00C04F8EDB8A"_uuid; //constexpr mpt::UUID guiSUMMARYLIST = "925F94BC-525A-11D2-86DC-00C04F8EDB8A"_uuid; constexpr mpt::UUID guidCSET = "54455343-ACF3-11D3-8CD1-00C04F8EDB8A"_uuid; Wave64FileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return false; } if(mpt::UUID(fileHeader.GuidRIFF) != guidRIFF) { return false; } if(mpt::UUID(fileHeader.GuidWAVE) != guidWAVE) { return false; } if(fileHeader.FileSize != file.GetLength()) { return false; } FileReader chunkFile = file; auto chunkList = chunkFile.ReadChunks(8); if(!chunkList.ChunkExists(guidFMT)) { return false; } FileReader formatChunk = chunkList.GetChunk(guidFMT); WAVFormatChunk format; if(!formatChunk.ReadStruct(format)) { return false; } uint16 sampleFormat = format.format; if(format.format == WAVFormatChunk::fmtExtensible) { WAVFormatChunkExtension formatExt; if(!formatChunk.ReadStruct(formatExt)) { return false; } sampleFormat = static_cast(mpt::UUID(formatExt.subFormat).GetData1()); } if(format.sampleRate == 0) { return false; } if(format.numChannels == 0) { return false; } if(format.numChannels > 2) { return false; } if(sampleFormat != WAVFormatChunk::fmtPCM && sampleFormat != WAVFormatChunk::fmtFloat) { return false; } if(sampleFormat == WAVFormatChunk::fmtFloat && format.bitsPerSample != 32 && format.bitsPerSample != 64) { return false; } if(sampleFormat == WAVFormatChunk::fmtPCM && format.bitsPerSample > 64) { return false; } SampleIO::Bitdepth bitDepth; switch((format.bitsPerSample - 1) / 8u) { default: case 0: bitDepth = SampleIO::_8bit ; break; case 1: bitDepth = SampleIO::_16bit; break; case 2: bitDepth = SampleIO::_24bit; break; case 3: bitDepth = SampleIO::_32bit; break; case 7: bitDepth = SampleIO::_64bit; break; } SampleIO sampleIO( bitDepth, (format.numChannels > 1) ? SampleIO::stereoInterleaved : SampleIO::mono, SampleIO::littleEndian, (sampleFormat == WAVFormatChunk::fmtFloat) ? SampleIO::floatPCM : SampleIO::signedPCM); if(format.bitsPerSample <= 8) { sampleIO |= SampleIO::unsignedPCM; } if(mayNormalize) { sampleIO.MayNormalize(); } FileTags tags; uint16 codePage = 28591; // mpt::Charset::ISO8859_1 FileReader csetChunk = chunkList.GetChunk(guidCSET); if(csetChunk.IsValid()) { if(csetChunk.CanRead(2)) { codePage = csetChunk.ReadUint16LE(); } } if(chunkList.ChunkExists(guidLIST)) { FileReader listChunk = chunkList.GetChunk(guidLIST); if(listChunk.ReadMagic("INFO")) { auto infoChunk = listChunk.ReadChunks(2); Wave64TagFromLISTINFO(tags.title, codePage, infoChunk, RIFFChunk::idINAM); Wave64TagFromLISTINFO(tags.encoder, codePage, infoChunk, RIFFChunk::idISFT); //Wave64TagFromLISTINFO(void, codePage, infoChunk, RIFFChunk::idICOP); Wave64TagFromLISTINFO(tags.artist, codePage, infoChunk, RIFFChunk::idIART); Wave64TagFromLISTINFO(tags.album, codePage, infoChunk, RIFFChunk::idIPRD); Wave64TagFromLISTINFO(tags.comments, codePage, infoChunk, RIFFChunk::idICMT); //Wave64TagFromLISTINFO(void, codePage, infoChunk, RIFFChunk::idIENG); //Wave64TagFromLISTINFO(void, codePage, infoChunk, RIFFChunk::idISBJ); Wave64TagFromLISTINFO(tags.genre, codePage, infoChunk, RIFFChunk::idIGNR); //Wave64TagFromLISTINFO(void, codePage, infoChunk, RIFFChunk::idICRD); Wave64TagFromLISTINFO(tags.year, codePage, infoChunk, RIFFChunk::idYEAR); Wave64TagFromLISTINFO(tags.trackno, codePage, infoChunk, RIFFChunk::idTRCK); Wave64TagFromLISTINFO(tags.url, codePage, infoChunk, RIFFChunk::idTURL); //Wave64TagFromLISTINFO(tags.bpm, codePage, infoChunk, void); } } if(!chunkList.ChunkExists(guidDATA)) { return false; } FileReader audioData = chunkList.GetChunk(guidDATA); SmpLength length = mpt::saturate_cast(audioData.GetLength() / (sampleIO.GetEncodedBitsPerSample()/8)); ModSample &mptSample = Samples[nSample]; DestroySampleThreadsafe(nSample); mptSample.Initialize(); mptSample.nLength = length; mptSample.nC5Speed = format.sampleRate; sampleIO.ReadSample(mptSample, audioData); m_szNames[nSample] = mpt::ToCharset(GetCharsetInternal(), GetSampleNameFromTags(tags)); mptSample.Convert(MOD_TYPE_IT, GetType()); mptSample.PrecomputeLoops(*this, false); return true; } #ifndef MODPLUG_NO_FILESAVE /////////////////////////////////////////////////////////////// // Save RAW bool CSoundFile::SaveRAWSample(SAMPLEINDEX nSample, std::ostream &f) const { const ModSample &sample = Samples[nSample]; SampleIO( sample.uFlags[CHN_16BIT] ? SampleIO::_16bit : SampleIO::_8bit, sample.uFlags[CHN_STEREO] ? SampleIO::stereoInterleaved : SampleIO::mono, SampleIO::littleEndian, SampleIO::signedPCM) .WriteSample(f, sample); return true; } #endif // MODPLUG_NO_FILESAVE ///////////////////////////////////////////////////////////// // GUS Patches struct GF1PatchFileHeader { char magic[8]; // "GF1PATCH" char version[4]; // "100", or "110" char id[10]; // "ID#000002" char copyright[60]; // Copyright uint8le numInstr; // Number of instruments in patch uint8le voices; // Number of voices, usually 14 uint8le channels; // Number of wav channels that can be played concurently to the patch uint16le numSamples; // Total number of waveforms for all the .PAT uint16le volume; // Master volume uint32le dataSize; char reserved2[36]; }; MPT_BINARY_STRUCT(GF1PatchFileHeader, 129) struct GF1Instrument { uint16le id; // Instrument id: 0-65535 char name[16]; // Name of instrument. Gravis doesn't seem to use it uint32le size; // Number of bytes for the instrument with header. (To skip to next instrument) uint8 layers; // Number of layers in instrument: 1-4 char reserved[40]; }; MPT_BINARY_STRUCT(GF1Instrument, 63) struct GF1SampleHeader { char name[7]; // null terminated string. name of the wave. uint8le fractions; // Start loop point fraction in 4 bits + End loop point fraction in the 4 other bits. uint32le length; // total size of wavesample. limited to 65535 now by the drivers, not the card. uint32le loopstart; // start loop position in the wavesample uint32le loopend; // end loop position in the wavesample uint16le freq; // Rate at which the wavesample has been sampled uint32le low_freq; // check note.h for the correspondance. uint32le high_freq; // check note.h for the correspondance. uint32le root_freq; // check note.h for the correspondance. int16le finetune; // fine tune. -512 to +512, EXCLUDING 0 cause it is a multiplier. 512 is one octave off, and 1 is a neutral value uint8le balance; // Balance: 0-15. 0=full left, 15 = full right uint8le env_rate[6]; // attack rates uint8le env_volume[6]; // attack volumes uint8le tremolo_sweep, tremolo_rate, tremolo_depth; uint8le vibrato_sweep, vibrato_rate, vibrato_depth; uint8le flags; int16le scale_frequency; // Note uint16le scale_factor; // 0...2048 (1024 is normal) or 0...2 char reserved[36]; }; MPT_BINARY_STRUCT(GF1SampleHeader, 96) // -- GF1 Envelopes -- // // It can be represented like this (the envelope is totally bogus, it is // just to show the concept): // // | // | /----` | | // | /------/ `\ | | | | | // | / \ | | | | | // | / \ | | | | | // |/ \ | | | | | // ---------------------------- | | | | | | // <---> attack rate 0 0 1 2 3 4 5 amplitudes // <----> attack rate 1 // <> attack rate 2 // <--> attack rate 3 // <> attack rate 4 // <-----> attack rate 5 // // -- GF1 Flags -- // // bit 0: 8/16 bit // bit 1: Signed/Unsigned // bit 2: off/on looping // bit 3: off/on bidirectionnal looping // bit 4: off/on backward looping // bit 5: off/on sustaining (3rd point in env.) // bit 6: off/on envelopes // bit 7: off/on clamped release (6th point, env) struct GF1Layer { uint8le previous; // If !=0 the wavesample to use is from the previous layer. The waveheader is still needed uint8le id; // Layer id: 0-3 uint32le size; // data size in bytes in the layer, without the header. to skip to next layer for example: uint8le samples; // number of wavesamples char reserved[40]; }; MPT_BINARY_STRUCT(GF1Layer, 47) static double PatchFreqToNote(uint32 nFreq) { return std::log(nFreq / 2044.0) * (12.0 * 1.44269504088896340736); // 1.0/std::log(2.0) } static int32 PatchFreqToNoteInt(uint32 nFreq) { return mpt::saturate_round(PatchFreqToNote(nFreq)); } static void PatchToSample(CSoundFile *that, SAMPLEINDEX nSample, GF1SampleHeader &sampleHeader, FileReader &file) { ModSample &sample = that->GetSample(nSample); file.ReadStruct(sampleHeader); sample.Initialize(); if(sampleHeader.flags & 4) sample.uFlags.set(CHN_LOOP); if(sampleHeader.flags & 8) sample.uFlags.set(CHN_PINGPONGLOOP); if(sampleHeader.flags & 16) sample.uFlags.set(CHN_REVERSE); sample.nLength = sampleHeader.length; sample.nLoopStart = sampleHeader.loopstart; sample.nLoopEnd = sampleHeader.loopend; sample.nC5Speed = sampleHeader.freq; sample.nPan = (sampleHeader.balance * 256 + 8) / 15; if(sample.nPan > 256) sample.nPan = 128; else sample.uFlags.set(CHN_PANNING); sample.nVibType = VIB_SINE; sample.nVibSweep = sampleHeader.vibrato_sweep; sample.nVibDepth = sampleHeader.vibrato_depth; sample.nVibRate = sampleHeader.vibrato_rate / 4; if(sampleHeader.scale_factor) { sample.Transpose((84.0 - PatchFreqToNote(sampleHeader.root_freq)) / 12.0); } SampleIO sampleIO( SampleIO::_8bit, SampleIO::mono, SampleIO::littleEndian, (sampleHeader.flags & 2) ? SampleIO::unsignedPCM : SampleIO::signedPCM); if(sampleHeader.flags & 1) { sampleIO |= SampleIO::_16bit; sample.nLength /= 2; sample.nLoopStart /= 2; sample.nLoopEnd /= 2; } sampleIO.ReadSample(sample, file); sample.Convert(MOD_TYPE_IT, that->GetType()); sample.PrecomputeLoops(*that, false); that->m_szNames[nSample] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, sampleHeader.name); } bool CSoundFile::ReadPATSample(SAMPLEINDEX nSample, FileReader &file) { file.Rewind(); GF1PatchFileHeader fileHeader; GF1Instrument instrHeader; // We only support one instrument GF1Layer layerHeader; if(!file.ReadStruct(fileHeader) || memcmp(fileHeader.magic, "GF1PATCH", 8) || (memcmp(fileHeader.version, "110\0", 4) && memcmp(fileHeader.version, "100\0", 4)) || memcmp(fileHeader.id, "ID#000002\0", 10) || !fileHeader.numInstr || !fileHeader.numSamples || !file.ReadStruct(instrHeader) //|| !instrHeader.layers // DOO.PAT has 0 layers || !file.ReadStruct(layerHeader) || !layerHeader.samples) { return false; } DestroySampleThreadsafe(nSample); GF1SampleHeader sampleHeader; PatchToSample(this, nSample, sampleHeader, file); if(instrHeader.name[0] > ' ') { m_szNames[nSample] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, instrHeader.name); } return true; } // PAT Instrument bool CSoundFile::ReadPATInstrument(INSTRUMENTINDEX nInstr, FileReader &file) { file.Rewind(); GF1PatchFileHeader fileHeader; GF1Instrument instrHeader; // We only support one instrument GF1Layer layerHeader; if(!file.ReadStruct(fileHeader) || memcmp(fileHeader.magic, "GF1PATCH", 8) || (memcmp(fileHeader.version, "110\0", 4) && memcmp(fileHeader.version, "100\0", 4)) || memcmp(fileHeader.id, "ID#000002\0", 10) || !fileHeader.numInstr || !fileHeader.numSamples || !file.ReadStruct(instrHeader) //|| !instrHeader.layers // DOO.PAT has 0 layers || !file.ReadStruct(layerHeader) || !layerHeader.samples) { return false; } ModInstrument *pIns = new (std::nothrow) ModInstrument(); if(pIns == nullptr) { return false; } DestroyInstrument(nInstr, deleteAssociatedSamples); if (nInstr > m_nInstruments) m_nInstruments = nInstr; Instruments[nInstr] = pIns; pIns->name = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, instrHeader.name); pIns->nFadeOut = 2048; if(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) { pIns->nNNA = NewNoteAction::NoteOff; pIns->nDNA = DuplicateNoteAction::NoteFade; } SAMPLEINDEX nextSample = 0; int32 nMinSmpNote = 0xFF; SAMPLEINDEX nMinSmp = 0; for(uint8 smp = 0; smp < layerHeader.samples; smp++) { // Find a free sample nextSample = GetNextFreeSample(nInstr, nextSample + 1); if(nextSample == SAMPLEINDEX_INVALID) break; if(m_nSamples < nextSample) m_nSamples = nextSample; if(!nMinSmp) nMinSmp = nextSample; // Load it GF1SampleHeader sampleHeader; PatchToSample(this, nextSample, sampleHeader, file); int32 nMinNote = (sampleHeader.low_freq > 100) ? PatchFreqToNoteInt(sampleHeader.low_freq) : 0; int32 nMaxNote = (sampleHeader.high_freq > 100) ? PatchFreqToNoteInt(sampleHeader.high_freq) : static_cast(NOTE_MAX); int32 nBaseNote = (sampleHeader.root_freq > 100) ? PatchFreqToNoteInt(sampleHeader.root_freq) : -1; if(!sampleHeader.scale_factor && layerHeader.samples == 1) { nMinNote = 0; nMaxNote = NOTE_MAX; } // Fill Note Map for(int32 k = 0; k < NOTE_MAX; k++) { if(k == nBaseNote || (!pIns->Keyboard[k] && k >= nMinNote && k <= nMaxNote)) { if(!sampleHeader.scale_factor) pIns->NoteMap[k] = NOTE_MIDDLEC; pIns->Keyboard[k] = nextSample; if(k < nMinSmpNote) { nMinSmpNote = k; nMinSmp = nextSample; } } } } if(nMinSmp) { // Fill note map and missing samples for(uint8 k = 0; k < NOTE_MAX; k++) { if(!pIns->NoteMap[k]) pIns->NoteMap[k] = k + 1; if(!pIns->Keyboard[k]) { pIns->Keyboard[k] = nMinSmp; } else { nMinSmp = pIns->Keyboard[k]; } } } pIns->Sanitize(MOD_TYPE_IT); pIns->Convert(MOD_TYPE_IT, GetType()); return true; } ///////////////////////////////////////////////////////////// // S3I Samples bool CSoundFile::ReadS3ISample(SAMPLEINDEX nSample, FileReader &file) { file.Rewind(); S3MSampleHeader sampleHeader; if(!file.ReadStruct(sampleHeader) || (sampleHeader.sampleType != S3MSampleHeader::typePCM && sampleHeader.sampleType != S3MSampleHeader::typeAdMel) || (memcmp(sampleHeader.magic, "SCRS", 4) && memcmp(sampleHeader.magic, "SCRI", 4)) || !file.Seek(sampleHeader.GetSampleOffset())) { return false; } DestroySampleThreadsafe(nSample); ModSample &sample = Samples[nSample]; sampleHeader.ConvertToMPT(sample); m_szNames[nSample] = mpt::String::ReadBuf(mpt::String::nullTerminated, sampleHeader.name); if(sampleHeader.sampleType < S3MSampleHeader::typeAdMel) sampleHeader.GetSampleFormat(false).ReadSample(sample, file); else if(SupportsOPL()) InitOPL(); else AddToLog(LogInformation, U_("OPL instruments are not supported by this format.")); sample.Convert(MOD_TYPE_S3M, GetType()); sample.PrecomputeLoops(*this, false); return true; } #ifndef MODPLUG_NO_FILESAVE bool CSoundFile::SaveS3ISample(SAMPLEINDEX smp, std::ostream &f) const { const ModSample &sample = Samples[smp]; S3MSampleHeader sampleHeader; MemsetZero(sampleHeader); SmpLength length = sampleHeader.ConvertToS3M(sample); mpt::String::WriteBuf(mpt::String::nullTerminated, sampleHeader.name) = m_szNames[smp]; mpt::String::WriteBuf(mpt::String::maybeNullTerminated, sampleHeader.reserved2) = mpt::ToCharset(mpt::Charset::UTF8, Version::Current().GetOpenMPTVersionString()); if(length) sampleHeader.dataPointer[1] = sizeof(S3MSampleHeader) >> 4; mpt::IO::Write(f, sampleHeader); if(length) sampleHeader.GetSampleFormat(false).WriteSample(f, sample, length); return true; } #endif // MODPLUG_NO_FILESAVE ///////////////////////////////////////////////////////////// // SBI OPL patch files bool CSoundFile::ReadSBISample(SAMPLEINDEX sample, FileReader &file) { file.Rewind(); if(!file.ReadMagic("SBI\x1A") || !file.CanRead(32 + sizeof(OPLPatch)) || file.CanRead(64)) // Arbitrary threshold to reject files that are unlikely to be SBI files return false; if(!SupportsOPL()) { AddToLog(LogInformation, U_("OPL instruments are not supported by this format.")); return true; } DestroySampleThreadsafe(sample); InitOPL(); ModSample &mptSmp = Samples[sample]; mptSmp.Initialize(MOD_TYPE_S3M); file.ReadString(m_szNames[sample], 32); OPLPatch patch; file.ReadArray(patch); mptSmp.SetAdlib(true, patch); mptSmp.Convert(MOD_TYPE_S3M, GetType()); return true; } ///////////////////////////////////////////////////////////// // XI Instruments bool CSoundFile::ReadXIInstrument(INSTRUMENTINDEX nInstr, FileReader &file) { file.Rewind(); XIInstrumentHeader fileHeader; if(!file.ReadStruct(fileHeader) || memcmp(fileHeader.signature, "Extended Instrument: ", 21) || fileHeader.version != XIInstrumentHeader::fileVersion || fileHeader.eof != 0x1A) { return false; } ModInstrument *pIns = new (std::nothrow) ModInstrument(); if(pIns == nullptr) { return false; } DestroyInstrument(nInstr, deleteAssociatedSamples); if(nInstr > m_nInstruments) { m_nInstruments = nInstr; } Instruments[nInstr] = pIns; fileHeader.ConvertToMPT(*pIns); // Translate sample map and find available sample slots std::vector sampleMap(fileHeader.numSamples); SAMPLEINDEX maxSmp = 0; for(size_t i = 0 + 12; i < 96 + 12; i++) { if(pIns->Keyboard[i] >= fileHeader.numSamples) { continue; } if(sampleMap[pIns->Keyboard[i]] == 0) { // Find slot for this sample maxSmp = GetNextFreeSample(nInstr, maxSmp + 1); if(maxSmp != SAMPLEINDEX_INVALID) { sampleMap[pIns->Keyboard[i]] = maxSmp; } } pIns->Keyboard[i] = sampleMap[pIns->Keyboard[i]]; } if(m_nSamples < maxSmp) { m_nSamples = maxSmp; } std::vector sampleFlags(fileHeader.numSamples); // Read sample headers for(SAMPLEINDEX i = 0; i < fileHeader.numSamples; i++) { XMSample sampleHeader; if(!file.ReadStruct(sampleHeader) || !sampleMap[i]) { continue; } ModSample &mptSample = Samples[sampleMap[i]]; sampleHeader.ConvertToMPT(mptSample); fileHeader.instrument.ApplyAutoVibratoToMPT(mptSample); mptSample.Convert(MOD_TYPE_XM, GetType()); if(GetType() != MOD_TYPE_XM && fileHeader.numSamples == 1) { // No need to pan that single sample, thank you... mptSample.uFlags &= ~CHN_PANNING; } mptSample.filename = mpt::String::ReadBuf(mpt::String::spacePadded, sampleHeader.name); m_szNames[sampleMap[i]] = mpt::String::ReadBuf(mpt::String::spacePadded, sampleHeader.name); sampleFlags[i] = sampleHeader.GetSampleFormat(); } // Read sample data for(SAMPLEINDEX i = 0; i < fileHeader.numSamples; i++) { if(sampleMap[i]) { sampleFlags[i].ReadSample(Samples[sampleMap[i]], file); Samples[sampleMap[i]].PrecomputeLoops(*this, false); } } // Read MPT crap ReadExtendedInstrumentProperties(pIns, file); pIns->Convert(MOD_TYPE_XM, GetType()); pIns->Sanitize(GetType()); return true; } #ifndef MODPLUG_NO_FILESAVE bool CSoundFile::SaveXIInstrument(INSTRUMENTINDEX nInstr, std::ostream &f) const { ModInstrument *pIns = Instruments[nInstr]; if(pIns == nullptr) { return false; } // Create file header XIInstrumentHeader header; header.ConvertToXM(*pIns, false); const std::vector samples = header.instrument.GetSampleList(*pIns, false); if(samples.size() > 0 && samples[0] <= GetNumSamples()) { // Copy over auto-vibrato settings of first sample header.instrument.ApplyAutoVibratoToXM(Samples[samples[0]], GetType()); } mpt::IO::Write(f, header); std::vector sampleFlags(samples.size()); // XI Sample Headers for(SAMPLEINDEX i = 0; i < samples.size(); i++) { XMSample xmSample; if(samples[i] <= GetNumSamples()) { xmSample.ConvertToXM(Samples[samples[i]], GetType(), false); } else { MemsetZero(xmSample); } sampleFlags[i] = xmSample.GetSampleFormat(); mpt::String::WriteBuf(mpt::String::spacePadded, xmSample.name) = m_szNames[samples[i]]; mpt::IO::Write(f, xmSample); } // XI Sample Data for(SAMPLEINDEX i = 0; i < samples.size(); i++) { if(samples[i] <= GetNumSamples()) { sampleFlags[i].WriteSample(f, Samples[samples[i]]); } } // Write 'MPTX' extension tag mpt::IO::WriteText(f, "XTPM"); WriteInstrumentHeaderStructOrField(pIns, f); // Write full extended header. return true; } #endif // MODPLUG_NO_FILESAVE // Read first sample from XI file into a sample slot bool CSoundFile::ReadXISample(SAMPLEINDEX nSample, FileReader &file) { file.Rewind(); XIInstrumentHeader fileHeader; if(!file.ReadStruct(fileHeader) || !file.CanRead(sizeof(XMSample)) || memcmp(fileHeader.signature, "Extended Instrument: ", 21) || fileHeader.version != XIInstrumentHeader::fileVersion || fileHeader.eof != 0x1A || fileHeader.numSamples == 0) { return false; } if(m_nSamples < nSample) { m_nSamples = nSample; } uint16 numSamples = fileHeader.numSamples; FileReader::off_t samplePos = sizeof(XIInstrumentHeader) + numSamples * sizeof(XMSample); // Preferrably read the middle-C sample auto sample = fileHeader.instrument.sampleMap[48]; if(sample >= fileHeader.numSamples) sample = 0; XMSample sampleHeader; while(sample--) { file.ReadStruct(sampleHeader); samplePos += sampleHeader.length; } file.ReadStruct(sampleHeader); // Gotta skip 'em all! file.Seek(samplePos); DestroySampleThreadsafe(nSample); ModSample &mptSample = Samples[nSample]; sampleHeader.ConvertToMPT(mptSample); if(GetType() != MOD_TYPE_XM) { // No need to pan that single sample, thank you... mptSample.uFlags.reset(CHN_PANNING); } fileHeader.instrument.ApplyAutoVibratoToMPT(mptSample); mptSample.Convert(MOD_TYPE_XM, GetType()); mptSample.filename = mpt::String::ReadBuf(mpt::String::spacePadded, sampleHeader.name); m_szNames[nSample] = mpt::String::ReadBuf(mpt::String::spacePadded, sampleHeader.name); // Read sample data sampleHeader.GetSampleFormat().ReadSample(Samples[nSample], file); Samples[nSample].PrecomputeLoops(*this, false); return true; } /////////////// // Apple CAF // struct CAFFileHeader { uint32be mFileType; uint16be mFileVersion; uint16be mFileFlags; }; MPT_BINARY_STRUCT(CAFFileHeader, 8) struct CAFChunkHeader { uint32be mChunkType; int64be mChunkSize; }; MPT_BINARY_STRUCT(CAFChunkHeader, 12) struct CAFChunk { enum ChunkIdentifiers { iddesc = MagicBE("desc"), iddata = MagicBE("data"), idstrg = MagicBE("strg"), idinfo = MagicBE("info") }; CAFChunkHeader header; FileReader::off_t GetLength() const { int64 length = header.mChunkSize; if(length == -1) { length = std::numeric_limits::max(); // spec } if(length < 0) { length = std::numeric_limits::max(); // heuristic } return mpt::saturate_cast(length); } ChunkIdentifiers GetID() const { return static_cast(header.mChunkType.get()); } }; MPT_BINARY_STRUCT(CAFChunk, 12) enum { CAFkAudioFormatLinearPCM = MagicBE("lpcm"), CAFkAudioFormatAppleIMA4 = MagicBE("ima4"), CAFkAudioFormatMPEG4AAC = MagicBE("aac "), CAFkAudioFormatMACE3 = MagicBE("MAC3"), CAFkAudioFormatMACE6 = MagicBE("MAC6"), CAFkAudioFormatULaw = MagicBE("ulaw"), CAFkAudioFormatALaw = MagicBE("alaw"), CAFkAudioFormatMPEGLayer1 = MagicBE(".mp1"), CAFkAudioFormatMPEGLayer2 = MagicBE(".mp2"), CAFkAudioFormatMPEGLayer3 = MagicBE(".mp3"), CAFkAudioFormatAppleLossless = MagicBE("alac") }; enum { CAFkCAFLinearPCMFormatFlagIsFloat = (1L << 0), CAFkCAFLinearPCMFormatFlagIsLittleEndian = (1L << 1) }; struct CAFAudioFormat { float64be mSampleRate; uint32be mFormatID; uint32be mFormatFlags; uint32be mBytesPerPacket; uint32be mFramesPerPacket; uint32be mChannelsPerFrame; uint32be mBitsPerChannel; }; MPT_BINARY_STRUCT(CAFAudioFormat, 32) static void CAFSetTagFromInfoKey(mpt::ustring & dst, const std::map & infoMap, const std::string & key) { auto item = infoMap.find(key); if(item == infoMap.end()) { return; } if(item->second.empty()) { return; } dst = mpt::ToUnicode(mpt::Charset::UTF8, item->second); } bool CSoundFile::ReadCAFSample(SAMPLEINDEX nSample, FileReader &file, bool mayNormalize) { file.Rewind(); CAFFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return false; } if(fileHeader.mFileType != MagicBE("caff")) { return false; } if(fileHeader.mFileVersion != 1) { return false; } auto chunkList = file.ReadChunks(0); CAFAudioFormat audioFormat; if(!chunkList.GetChunk(CAFChunk::iddesc).ReadStruct(audioFormat)) { return false; } if(audioFormat.mSampleRate <= 0.0) { return false; } if(audioFormat.mChannelsPerFrame == 0) { return false; } if(audioFormat.mChannelsPerFrame > 2) { return false; } if(!mpt::in_range(mpt::saturate_round(audioFormat.mSampleRate))) { return false; } uint32 sampleRate = static_cast(mpt::saturate_round(audioFormat.mSampleRate)); if(sampleRate <= 0) { return false; } SampleIO sampleIO; if(audioFormat.mFormatID == CAFkAudioFormatLinearPCM) { if(audioFormat.mFramesPerPacket != 1) { return false; } if(audioFormat.mBytesPerPacket == 0) { return false; } if(audioFormat.mBitsPerChannel == 0) { return false; } if(audioFormat.mFormatFlags & CAFkCAFLinearPCMFormatFlagIsFloat) { if(audioFormat.mBitsPerChannel != 32 && audioFormat.mBitsPerChannel != 64) { return false; } if(audioFormat.mBytesPerPacket != audioFormat.mChannelsPerFrame * audioFormat.mBitsPerChannel/8) { return false; } } if(audioFormat.mBytesPerPacket % audioFormat.mChannelsPerFrame != 0) { return false; } if(audioFormat.mBytesPerPacket / audioFormat.mChannelsPerFrame != 1 && audioFormat.mBytesPerPacket / audioFormat.mChannelsPerFrame != 2 && audioFormat.mBytesPerPacket / audioFormat.mChannelsPerFrame != 3 && audioFormat.mBytesPerPacket / audioFormat.mChannelsPerFrame != 4 && audioFormat.mBytesPerPacket / audioFormat.mChannelsPerFrame != 8 ) { return false; } SampleIO::Channels channels = (audioFormat.mChannelsPerFrame == 2) ? SampleIO::stereoInterleaved : SampleIO::mono; SampleIO::Endianness endianness = (audioFormat.mFormatFlags & CAFkCAFLinearPCMFormatFlagIsLittleEndian) ? SampleIO::littleEndian : SampleIO::bigEndian; SampleIO::Encoding encoding = (audioFormat.mFormatFlags & CAFkCAFLinearPCMFormatFlagIsFloat) ? SampleIO::floatPCM : SampleIO::signedPCM; SampleIO::Bitdepth bitdepth = static_cast((audioFormat.mBytesPerPacket / audioFormat.mChannelsPerFrame) * 8); sampleIO = SampleIO(bitdepth, channels, endianness, encoding); } else { return false; } if(mayNormalize) { sampleIO.MayNormalize(); } /* std::map stringMap; // UTF-8 if(chunkList.ChunkExists(CAFChunk::idstrg)) { FileReader stringsChunk = chunkList.GetChunk(CAFChunk::idstrg); uint32 numEntries = stringsChunk.ReadUint32BE(); if(stringsChunk.Skip(12 * numEntries)) { FileReader stringData = stringsChunk.ReadChunk(stringsChunk.BytesLeft()); stringsChunk.Seek(4); for(uint32 entry = 0; entry < numEntries && stringsChunk.CanRead(12); entry++) { uint32 stringID = stringsChunk.ReadUint32BE(); int64 offset = stringsChunk.ReadIntBE(); if(offset >= 0 && mpt::in_range(offset)) { stringData.Seek(mpt::saturate_cast(offset)); std::string str; if(stringData.ReadNullString(str)) { stringMap[stringID] = str; } } } } } */ std::map infoMap; // UTF-8 if(chunkList.ChunkExists(CAFChunk::idinfo)) { FileReader informationChunk = chunkList.GetChunk(CAFChunk::idinfo); uint32 numEntries = informationChunk.ReadUint32BE(); for(uint32 entry = 0; entry < numEntries && informationChunk.CanRead(2); entry++) { std::string key; std::string value; if(!informationChunk.ReadNullString(key)) { break; } if(!informationChunk.ReadNullString(value)) { break; } if(!key.empty() && !value.empty()) { infoMap[key] = value; } } } FileTags tags; CAFSetTagFromInfoKey(tags.bpm, infoMap, "tempo"); //CAFSetTagFromInfoKey(void, infoMap, "key signature"); //CAFSetTagFromInfoKey(void, infoMap, "time signature"); CAFSetTagFromInfoKey(tags.artist, infoMap, "artist"); CAFSetTagFromInfoKey(tags.album, infoMap, "album"); CAFSetTagFromInfoKey(tags.trackno, infoMap, "track number"); CAFSetTagFromInfoKey(tags.year, infoMap, "year"); //CAFSetTagFromInfoKey(void, infoMap, "composer"); //CAFSetTagFromInfoKey(void, infoMap, "lyricist"); CAFSetTagFromInfoKey(tags.genre, infoMap, "genre"); CAFSetTagFromInfoKey(tags.title, infoMap, "title"); //CAFSetTagFromInfoKey(void, infoMap, "recorded date"); CAFSetTagFromInfoKey(tags.comments, infoMap, "comments"); //CAFSetTagFromInfoKey(void, infoMap, "copyright"); //CAFSetTagFromInfoKey(void, infoMap, "source encoder"); CAFSetTagFromInfoKey(tags.encoder, infoMap, "encoding application"); //CAFSetTagFromInfoKey(void, infoMap, "nominal bit rate"); //CAFSetTagFromInfoKey(void, infoMap, "channel layout"); //CAFSetTagFromInfoKey(tags.url, infoMap, void); if(!chunkList.ChunkExists(CAFChunk::iddata)) { return false; } FileReader dataChunk = chunkList.GetChunk(CAFChunk::iddata); dataChunk.Skip(4); // edit count FileReader audioData = dataChunk.ReadChunk(dataChunk.BytesLeft()); SmpLength length = mpt::saturate_cast((audioData.GetLength() / audioFormat.mBytesPerPacket) * audioFormat.mFramesPerPacket); ModSample &mptSample = Samples[nSample]; DestroySampleThreadsafe(nSample); mptSample.Initialize(); mptSample.nLength = length; mptSample.nC5Speed = sampleRate; sampleIO.ReadSample(mptSample, audioData); m_szNames[nSample] = mpt::ToCharset(GetCharsetInternal(), GetSampleNameFromTags(tags)); mptSample.Convert(MOD_TYPE_IT, GetType()); mptSample.PrecomputeLoops(*this, false); return true; } ///////////////////////////////////////////////////////////////////////////////////////// // AIFF File I/O // AIFF header struct AIFFHeader { char magic[4]; // FORM uint32be length; // Size of the file, not including magic and length char type[4]; // AIFF or AIFC }; MPT_BINARY_STRUCT(AIFFHeader, 12) // General IFF Chunk header struct AIFFChunk { // 32-Bit chunk identifiers enum ChunkIdentifiers { idCOMM = MagicBE("COMM"), idSSND = MagicBE("SSND"), idINST = MagicBE("INST"), idMARK = MagicBE("MARK"), idNAME = MagicBE("NAME"), }; uint32be id; // See ChunkIdentifiers uint32be length; // Chunk size without header size_t GetLength() const { return length; } ChunkIdentifiers GetID() const { return static_cast(id.get()); } }; MPT_BINARY_STRUCT(AIFFChunk, 8) // "Common" chunk (in AIFC, a compression ID and compression name follows this header, but apart from that it's identical) struct AIFFCommonChunk { uint16be numChannels; uint32be numSampleFrames; uint16be sampleSize; uint8be sampleRate[10]; // Sample rate in 80-Bit floating point // Convert sample rate to integer uint32 GetSampleRate() const { uint32 mantissa = (sampleRate[2] << 24) | (sampleRate[3] << 16) | (sampleRate[4] << 8) | (sampleRate[5] << 0); uint32 last = 0; uint8 exp = 30 - sampleRate[1]; while(exp--) { last = mantissa; mantissa >>= 1; } if(last & 1) mantissa++; return mantissa; } }; MPT_BINARY_STRUCT(AIFFCommonChunk, 18) // Sound chunk struct AIFFSoundChunk { uint32be offset; uint32be blockSize; }; MPT_BINARY_STRUCT(AIFFSoundChunk, 8) // Marker struct AIFFMarker { uint16be id; uint32be position; // Position in sample uint8be nameLength; // Not counting eventually existing padding byte in name string }; MPT_BINARY_STRUCT(AIFFMarker, 7) // Instrument loop struct AIFFInstrumentLoop { enum PlayModes { noLoop = 0, loopNormal = 1, loopBidi = 2, }; uint16be playMode; uint16be beginLoop; // Marker index uint16be endLoop; // Marker index }; MPT_BINARY_STRUCT(AIFFInstrumentLoop, 6) struct AIFFInstrumentChunk { uint8be baseNote; uint8be detune; uint8be lowNote; uint8be highNote; uint8be lowVelocity; uint8be highVelocity; uint16be gain; AIFFInstrumentLoop sustainLoop; AIFFInstrumentLoop releaseLoop; }; MPT_BINARY_STRUCT(AIFFInstrumentChunk, 20) bool CSoundFile::ReadAIFFSample(SAMPLEINDEX nSample, FileReader &file, bool mayNormalize) { file.Rewind(); // Verify header AIFFHeader fileHeader; if(!file.ReadStruct(fileHeader) || memcmp(fileHeader.magic, "FORM", 4) || (memcmp(fileHeader.type, "AIFF", 4) && memcmp(fileHeader.type, "AIFC", 4))) { return false; } auto chunks = file.ReadChunks(2); // Read COMM chunk FileReader commChunk(chunks.GetChunk(AIFFChunk::idCOMM)); AIFFCommonChunk sampleInfo; if(!commChunk.ReadStruct(sampleInfo)) { return false; } // Is this a proper sample? if(sampleInfo.numSampleFrames == 0 || sampleInfo.numChannels < 1 || sampleInfo.numChannels > 2 || sampleInfo.sampleSize < 1 || sampleInfo.sampleSize > 64) { return false; } // Read compression type in AIFF-C files. uint8 compression[4] = { 'N', 'O', 'N', 'E' }; SampleIO::Endianness endian = SampleIO::bigEndian; if(!memcmp(fileHeader.type, "AIFC", 4)) { if(!commChunk.ReadArray(compression)) { return false; } if(!memcmp(compression, "twos", 4)) { endian = SampleIO::littleEndian; } } // Read SSND chunk FileReader soundChunk(chunks.GetChunk(AIFFChunk::idSSND)); AIFFSoundChunk sampleHeader; if(!soundChunk.ReadStruct(sampleHeader)) { return false; } SampleIO::Bitdepth bitDepth; switch((sampleInfo.sampleSize - 1) / 8) { default: case 0: bitDepth = SampleIO::_8bit; break; case 1: bitDepth = SampleIO::_16bit; break; case 2: bitDepth = SampleIO::_24bit; break; case 3: bitDepth = SampleIO::_32bit; break; case 7: bitDepth = SampleIO::_64bit; break; } SampleIO sampleIO(bitDepth, (sampleInfo.numChannels == 2) ? SampleIO::stereoInterleaved : SampleIO::mono, endian, SampleIO::signedPCM); if(!memcmp(compression, "fl32", 4) || !memcmp(compression, "FL32", 4) || !memcmp(compression, "fl64", 4) || !memcmp(compression, "FL64", 4)) { sampleIO |= SampleIO::floatPCM; } else if(!memcmp(compression, "alaw", 4) || !memcmp(compression, "ALAW", 4)) { sampleIO |= SampleIO::aLaw; sampleIO |= SampleIO::_16bit; } else if(!memcmp(compression, "ulaw", 4) || !memcmp(compression, "ULAW", 4)) { sampleIO |= SampleIO::uLaw; sampleIO |= SampleIO::_16bit; } else if(!memcmp(compression, "raw ", 4)) { sampleIO |= SampleIO::unsignedPCM; } if(mayNormalize) { sampleIO.MayNormalize(); } if(soundChunk.CanRead(sampleHeader.offset)) { soundChunk.Skip(sampleHeader.offset); } ModSample &mptSample = Samples[nSample]; DestroySampleThreadsafe(nSample); mptSample.Initialize(); mptSample.nLength = sampleInfo.numSampleFrames; mptSample.nC5Speed = sampleInfo.GetSampleRate(); sampleIO.ReadSample(mptSample, soundChunk); // Read MARK and INST chunk to extract sample loops FileReader markerChunk(chunks.GetChunk(AIFFChunk::idMARK)); AIFFInstrumentChunk instrHeader; if(markerChunk.IsValid() && chunks.GetChunk(AIFFChunk::idINST).ReadStruct(instrHeader)) { uint16 numMarkers = markerChunk.ReadUint16BE(); std::vector markers; markers.reserve(numMarkers); for(size_t i = 0; i < numMarkers; i++) { AIFFMarker marker; if(!markerChunk.ReadStruct(marker)) { break; } markers.push_back(marker); markerChunk.Skip(marker.nameLength + ((marker.nameLength % 2u) == 0 ? 1 : 0)); } if(instrHeader.sustainLoop.playMode != AIFFInstrumentLoop::noLoop) { mptSample.uFlags.set(CHN_SUSTAINLOOP); mptSample.uFlags.set(CHN_PINGPONGSUSTAIN, instrHeader.sustainLoop.playMode == AIFFInstrumentLoop::loopBidi); } if(instrHeader.releaseLoop.playMode != AIFFInstrumentLoop::noLoop) { mptSample.uFlags.set(CHN_LOOP); mptSample.uFlags.set(CHN_PINGPONGLOOP, instrHeader.releaseLoop.playMode == AIFFInstrumentLoop::loopBidi); } // Read markers for(const auto &m : markers) { if(m.id == instrHeader.sustainLoop.beginLoop) mptSample.nSustainStart = m.position; if(m.id == instrHeader.sustainLoop.endLoop) mptSample.nSustainEnd = m.position; if(m.id == instrHeader.releaseLoop.beginLoop) mptSample.nLoopStart = m.position; if(m.id == instrHeader.releaseLoop.endLoop) mptSample.nLoopEnd = m.position; } mptSample.SanitizeLoops(); } // Extract sample name FileReader nameChunk(chunks.GetChunk(AIFFChunk::idNAME)); if(nameChunk.IsValid()) { nameChunk.ReadString(m_szNames[nSample], nameChunk.GetLength()); } else { m_szNames[nSample] = ""; } mptSample.Convert(MOD_TYPE_IT, GetType()); mptSample.PrecomputeLoops(*this, false); return true; } static bool AUIsAnnotationLineWithField(const std::string &line) { std::size_t pos = line.find('='); if(pos == std::string::npos) { return false; } if(pos == 0) { return false; } const auto field = std::string_view(line).substr(0, pos); // Scan for invalid chars for(auto c : field) { if(!mpt::is_in_range(c, 'a', 'z') && !mpt::is_in_range(c, 'A', 'Z') && !mpt::is_in_range(c, '0', '9') && c != '-' && c != '_') { return false; } } return true; } static std::string AUTrimFieldFromAnnotationLine(const std::string &line) { if(!AUIsAnnotationLineWithField(line)) { return line; } std::size_t pos = line.find('='); return line.substr(pos + 1); } static std::string AUGetAnnotationFieldFromLine(const std::string &line) { if(!AUIsAnnotationLineWithField(line)) { return std::string(); } std::size_t pos = line.find('='); return line.substr(0, pos); } bool CSoundFile::ReadAUSample(SAMPLEINDEX nSample, FileReader &file, bool mayNormalize) { file.Rewind(); // Verify header const auto magic = file.ReadArray(); const bool bigEndian = !std::memcmp(magic.data(), ".snd", 4); const bool littleEndian = !std::memcmp(magic.data(), "dns.", 4); if(!bigEndian && !littleEndian) return false; auto readUint32 = std::bind(bigEndian ? &FileReader::ReadUint32BE : &FileReader::ReadUint32LE, file); uint32 dataOffset = readUint32(); // must be divisible by 8 according to spec, however, there are files that ignore this requirement uint32 dataSize = readUint32(); uint32 encoding = readUint32(); uint32 sampleRate = readUint32(); uint32 channels = readUint32(); // According to spec, a minimum 8 byte annotation field after the header fields is required, // however, there are files in the wild that violate this requirement. // Thus, check for 24 instead of 32 here. if(dataOffset < 24) // data offset points inside header { return false; } if(channels < 1 || channels > 2) return false; SampleIO sampleIO(SampleIO::_8bit, channels == 1 ? SampleIO::mono : SampleIO::stereoInterleaved, bigEndian ? SampleIO::bigEndian : SampleIO::littleEndian, SampleIO::signedPCM); switch(encoding) { case 1: sampleIO |= SampleIO::_16bit; // u-law sampleIO |= SampleIO::uLaw; break; case 2: break; // 8-bit linear PCM case 3: sampleIO |= SampleIO::_16bit; break; // 16-bit linear PCM case 4: sampleIO |= SampleIO::_24bit; break; // 24-bit linear PCM case 5: sampleIO |= SampleIO::_32bit; break; // 32-bit linear PCM case 6: sampleIO |= SampleIO::_32bit; // 32-bit IEEE floating point sampleIO |= SampleIO::floatPCM; break; case 7: sampleIO |= SampleIO::_64bit; // 64-bit IEEE floating point sampleIO |= SampleIO::floatPCM; break; case 27: sampleIO |= SampleIO::_16bit; // a-law sampleIO |= SampleIO::aLaw; break; default: return false; } if(!file.LengthIsAtLeast(dataOffset)) { return false; } FileTags tags; // This reads annotation metadata as written by OpenMPT, sox, ffmpeg. // Additionally, we fall back to just reading the whole field as a single comment. // We only read up to the first \0 byte. file.Seek(24); std::string annotation; file.ReadString(annotation, dataOffset - 24); annotation = mpt::replace(annotation, std::string("\r\n"), std::string("\n")); annotation = mpt::replace(annotation, std::string("\r"), std::string("\n")); mpt::Charset charset = mpt::IsUTF8(annotation) ? mpt::Charset::UTF8 : mpt::Charset::ISO8859_1; const auto lines = mpt::String::Split(annotation, "\n"); bool hasFields = false; for(const auto &line : lines) { if(AUIsAnnotationLineWithField(line)) { hasFields = true; break; } } if(hasFields) { std::map> linesPerField; std::string lastField = "comment"; for(const auto &line : lines) { if(AUIsAnnotationLineWithField(line)) { lastField = mpt::ToLowerCaseAscii(mpt::trim(AUGetAnnotationFieldFromLine(line))); } linesPerField[lastField].push_back(AUTrimFieldFromAnnotationLine(line)); } tags.title = mpt::ToUnicode(charset, mpt::String::Combine(linesPerField["title" ], std::string("\n"))); tags.artist = mpt::ToUnicode(charset, mpt::String::Combine(linesPerField["artist" ], std::string("\n"))); tags.album = mpt::ToUnicode(charset, mpt::String::Combine(linesPerField["album" ], std::string("\n"))); tags.trackno = mpt::ToUnicode(charset, mpt::String::Combine(linesPerField["track" ], std::string("\n"))); tags.genre = mpt::ToUnicode(charset, mpt::String::Combine(linesPerField["genre" ], std::string("\n"))); tags.comments = mpt::ToUnicode(charset, mpt::String::Combine(linesPerField["comment"], std::string("\n"))); } else { // Most applications tend to write their own name here, // thus there is little use in interpreting the string as a title. annotation = mpt::trim_right(annotation, std::string("\r\n")); tags.comments = mpt::ToUnicode(charset, annotation); } file.Seek(dataOffset); ModSample &mptSample = Samples[nSample]; DestroySampleThreadsafe(nSample); mptSample.Initialize(); SmpLength length = mpt::saturate_cast(file.BytesLeft()); if(dataSize != 0xFFFFFFFF) LimitMax(length, dataSize); mptSample.nLength = (length * 8u) / (sampleIO.GetEncodedBitsPerSample() * channels); mptSample.nC5Speed = sampleRate; m_szNames[nSample] = mpt::ToCharset(GetCharsetInternal(), GetSampleNameFromTags(tags)); if(mayNormalize) { sampleIO.MayNormalize(); } sampleIO.ReadSample(mptSample, file); mptSample.Convert(MOD_TYPE_IT, GetType()); mptSample.PrecomputeLoops(*this, false); return true; } ///////////////////////////////////////////////////////////////////////////////////////// // ITS Samples bool CSoundFile::ReadITSSample(SAMPLEINDEX nSample, FileReader &file, bool rewind) { if(rewind) { file.Rewind(); } ITSample sampleHeader; if(!file.ReadStruct(sampleHeader) || memcmp(sampleHeader.id, "IMPS", 4)) { return false; } DestroySampleThreadsafe(nSample); ModSample &sample = Samples[nSample]; file.Seek(sampleHeader.ConvertToMPT(sample)); m_szNames[nSample] = mpt::String::ReadBuf(mpt::String::spacePaddedNull, sampleHeader.name); if(sample.uFlags[CHN_ADLIB]) { OPLPatch patch; file.ReadArray(patch); sample.SetAdlib(true, patch); InitOPL(); if(!SupportsOPL()) { AddToLog(LogInformation, U_("OPL instruments are not supported by this format.")); } } else if(!sample.uFlags[SMP_KEEPONDISK]) { sampleHeader.GetSampleFormat().ReadSample(sample, file); } else { // External sample size_t strLen; file.ReadVarInt(strLen); #ifdef MPT_EXTERNAL_SAMPLES std::string filenameU8; file.ReadString(filenameU8, strLen); mpt::PathString filename = mpt::PathString::FromUTF8(filenameU8); if(!filename.empty()) { if(file.GetOptionalFileName()) { filename = filename.RelativePathToAbsolute(file.GetOptionalFileName()->GetPath()); } if(!LoadExternalSample(nSample, filename)) { AddToLog(LogWarning, U_("Unable to load sample: ") + filename.ToUnicode()); } } else { sample.uFlags.reset(SMP_KEEPONDISK); } #else file.Skip(strLen); #endif // MPT_EXTERNAL_SAMPLES } sample.Convert(MOD_TYPE_IT, GetType()); sample.PrecomputeLoops(*this, false); return true; } bool CSoundFile::ReadITISample(SAMPLEINDEX nSample, FileReader &file) { ITInstrument instrumentHeader; file.Rewind(); if(!file.ReadStruct(instrumentHeader) || memcmp(instrumentHeader.id, "IMPI", 4)) { return false; } file.Rewind(); ModInstrument dummy; ITInstrToMPT(file, dummy, instrumentHeader.trkvers); // Old SchismTracker versions set nos=0 const SAMPLEINDEX nsamples = std::max(static_cast(instrumentHeader.nos), *std::max_element(std::begin(dummy.Keyboard), std::end(dummy.Keyboard))); if(!nsamples) return false; // Preferrably read the middle-C sample auto sample = dummy.Keyboard[NOTE_MIDDLEC - NOTE_MIN]; if(sample > 0) sample--; else sample = 0; file.Seek(file.GetPosition() + sample * sizeof(ITSample)); return ReadITSSample(nSample, file, false); } bool CSoundFile::ReadITIInstrument(INSTRUMENTINDEX nInstr, FileReader &file) { ITInstrument instrumentHeader; SAMPLEINDEX smp = 0; file.Rewind(); if(!file.ReadStruct(instrumentHeader) || memcmp(instrumentHeader.id, "IMPI", 4)) { return false; } if(nInstr > GetNumInstruments()) m_nInstruments = nInstr; ModInstrument *pIns = new (std::nothrow) ModInstrument(); if(pIns == nullptr) { return false; } DestroyInstrument(nInstr, deleteAssociatedSamples); Instruments[nInstr] = pIns; file.Rewind(); ITInstrToMPT(file, *pIns, instrumentHeader.trkvers); // Old SchismTracker versions set nos=0 const SAMPLEINDEX nsamples = std::max(static_cast(instrumentHeader.nos), *std::max_element(std::begin(pIns->Keyboard), std::end(pIns->Keyboard))); // In order to properly compute the position, in file, of eventual extended settings // such as "attack" we need to keep the "real" size of the last sample as those extra // setting will follow this sample in the file FileReader::off_t extraOffset = file.GetPosition(); // Reading Samples std::vector samplemap(nsamples, 0); for(SAMPLEINDEX i = 0; i < nsamples; i++) { smp = GetNextFreeSample(nInstr, smp + 1); if(smp == SAMPLEINDEX_INVALID) break; samplemap[i] = smp; const FileReader::off_t offset = file.GetPosition(); if(!ReadITSSample(smp, file, false)) smp--; extraOffset = std::max(extraOffset, file.GetPosition()); file.Seek(offset + sizeof(ITSample)); } if(GetNumSamples() < smp) m_nSamples = smp; // Adjust sample assignment for(auto &sample : pIns->Keyboard) { if(sample > 0 && sample <= nsamples) { sample = samplemap[sample - 1]; } } if(file.Seek(extraOffset)) { // Read MPT crap ReadExtendedInstrumentProperties(pIns, file); } pIns->Convert(MOD_TYPE_IT, GetType()); pIns->Sanitize(GetType()); return true; } #ifndef MODPLUG_NO_FILESAVE bool CSoundFile::SaveITIInstrument(INSTRUMENTINDEX nInstr, std::ostream &f, const mpt::PathString &filename, bool compress, bool allowExternal) const { ITInstrument iti; ModInstrument *pIns = Instruments[nInstr]; if((!pIns) || (filename.empty() && allowExternal)) return false; auto instSize = iti.ConvertToIT(*pIns, false, *this); // Create sample assignment table std::vector smptable; std::vector smpmap(GetNumSamples(), 0); for(size_t i = 0; i < NOTE_MAX; i++) { const SAMPLEINDEX smp = pIns->Keyboard[i]; if(smp && smp <= GetNumSamples()) { if(!smpmap[smp - 1]) { // We haven't considered this sample yet. smptable.push_back(smp); smpmap[smp - 1] = static_cast(smptable.size()); } iti.keyboard[i * 2 + 1] = smpmap[smp - 1]; } else { iti.keyboard[i * 2 + 1] = 0; } } iti.nos = static_cast(smptable.size()); smpmap.clear(); uint32 filePos = instSize; mpt::IO::WritePartial(f, iti, instSize); filePos += mpt::saturate_cast(smptable.size() * sizeof(ITSample)); // Writing sample headers + data std::vector sampleFlags; for(auto smp : smptable) { ITSample itss; itss.ConvertToIT(Samples[smp], GetType(), compress, compress, allowExternal); const bool isExternal = itss.cvt == ITSample::cvtExternalSample; mpt::String::WriteBuf(mpt::String::nullTerminated, itss.name) = m_szNames[smp]; itss.samplepointer = filePos; mpt::IO::Write(f, itss); // Write sample auto curPos = mpt::IO::TellWrite(f); mpt::IO::SeekAbsolute(f, filePos); if(!isExternal) { filePos += mpt::saturate_cast(itss.GetSampleFormat(0x0214).WriteSample(f, Samples[smp])); } else { #ifdef MPT_EXTERNAL_SAMPLES const std::string filenameU8 = GetSamplePath(smp).AbsolutePathToRelative(filename.GetPath()).ToUTF8(); const size_t strSize = filenameU8.size(); size_t intBytes = 0; if(mpt::IO::WriteVarInt(f, strSize, &intBytes)) { filePos += mpt::saturate_cast(intBytes + strSize); mpt::IO::WriteRaw(f, filenameU8.data(), strSize); } #endif // MPT_EXTERNAL_SAMPLES } mpt::IO::SeekAbsolute(f, curPos); } mpt::IO::SeekEnd(f); // Write 'MPTX' extension tag mpt::IO::WriteRaw(f, "XTPM", 4); WriteInstrumentHeaderStructOrField(pIns, f); // Write full extended header. return true; } #endif // MODPLUG_NO_FILESAVE /////////////////////////////////////////////////////////////////////////////////////////////////// // 8SVX / 16SVX / MAUD Samples // IFF File Header struct IFFHeader { char form[4]; // "FORM" uint32be size; char magic[4]; // "8SVX", "16SV", "MAUD" }; MPT_BINARY_STRUCT(IFFHeader, 12) // General IFF Chunk header struct IFFChunk { // 32-Bit chunk identifiers enum ChunkIdentifiers { // 8SVX / 16SV idVHDR = MagicBE("VHDR"), idBODY = MagicBE("BODY"), idCHAN = MagicBE("CHAN"), // MAUD idMHDR = MagicBE("MHDR"), idMDAT = MagicBE("MDAT"), idNAME = MagicBE("NAME"), }; uint32be id; // See ChunkIdentifiers uint32be length; // Chunk size without header size_t GetLength() const { if(length == 0) // Broken files return std::numeric_limits::max(); return length; } ChunkIdentifiers GetID() const { return static_cast(id.get()); } }; MPT_BINARY_STRUCT(IFFChunk, 8) struct IFFSampleHeader { uint32be oneShotHiSamples; // Samples in the high octave 1-shot part uint32be repeatHiSamples; // Samples in the high octave repeat part uint32be samplesPerHiCycle; // Samples/cycle in high octave, else 0 uint16be samplesPerSec; // Data sampling rate uint8be octave; // Octaves of waveforms uint8be compression; // Data compression technique used uint32be volume; }; MPT_BINARY_STRUCT(IFFSampleHeader, 20) bool CSoundFile::ReadIFFSample(SAMPLEINDEX nSample, FileReader &file) { file.Rewind(); IFFHeader fileHeader; if(!file.ReadStruct(fileHeader) || memcmp(fileHeader.form, "FORM", 4) || (memcmp(fileHeader.magic, "8SVX", 4) && memcmp(fileHeader.magic, "16SV", 4) && memcmp(fileHeader.magic, "MAUD", 4))) { return false; } const auto chunks = file.ReadChunks(2); FileReader sampleData; SampleIO sampleIO(SampleIO::_8bit, SampleIO::mono, SampleIO::bigEndian, SampleIO::signedPCM); uint32 numSamples = 0, sampleRate = 0, loopStart = 0, loopLength = 0, volume = 0; if(!memcmp(fileHeader.magic, "MAUD", 4)) { FileReader mhdrChunk = chunks.GetChunk(IFFChunk::idMHDR); sampleData = chunks.GetChunk(IFFChunk::idMDAT); if(!mhdrChunk.LengthIs(32) || !sampleData.IsValid()) { return false; } numSamples = mhdrChunk.ReadUint32BE(); const uint16 bitsPerSample = mhdrChunk.ReadUint16BE(); mhdrChunk.Skip(2); // bits per sample after decompression sampleRate = mhdrChunk.ReadUint32BE(); const auto [clockDivide, channelInformation, numChannels, compressionType] = mhdrChunk.ReadArray(); if(!clockDivide) return false; else sampleRate /= clockDivide; if(numChannels != (channelInformation + 1)) return false; if(numChannels == 2) sampleIO |= SampleIO::stereoInterleaved; if(bitsPerSample == 8 && compressionType == 0) sampleIO |= SampleIO::unsignedPCM; else if(bitsPerSample == 8 && compressionType == 2) sampleIO |= SampleIO::aLaw; else if(bitsPerSample == 8 && compressionType == 3) sampleIO |= SampleIO::uLaw; else if(bitsPerSample == 16 && compressionType == 0) sampleIO |= SampleIO::_16bit; else return false; } else { FileReader vhdrChunk = chunks.GetChunk(IFFChunk::idVHDR); FileReader chanChunk = chunks.GetChunk(IFFChunk::idCHAN); sampleData = chunks.GetChunk(IFFChunk::idBODY); IFFSampleHeader sampleHeader; if(!sampleData.IsValid() || !vhdrChunk.IsValid() || !vhdrChunk.ReadStruct(sampleHeader)) { return false; } const uint8 bytesPerSample = memcmp(fileHeader.magic, "8SVX", 4) ? 2 : 1; const uint8 numChannels = chanChunk.ReadUint32BE() == 6 ? 2 : 1; const uint8 bytesPerFrame = bytesPerSample * numChannels; // While this is an Amiga format, the 16SV version appears to be only used on PC, and only with little-endian sample data. if(bytesPerSample == 2) sampleIO = SampleIO(SampleIO::_16bit, SampleIO::mono, SampleIO::littleEndian, SampleIO::signedPCM); if(numChannels == 2) sampleIO |= SampleIO::stereoSplit; loopStart = sampleHeader.oneShotHiSamples / bytesPerFrame; loopLength = sampleHeader.repeatHiSamples / bytesPerFrame; sampleRate = sampleHeader.samplesPerSec; volume = sampleHeader.volume; numSamples = mpt::saturate_cast(sampleData.GetLength() / bytesPerFrame); } DestroySampleThreadsafe(nSample); ModSample &sample = Samples[nSample]; sample.Initialize(); sample.nLength = numSamples; sample.nLoopStart = loopStart; sample.nLoopEnd = sample.nLoopStart + loopLength; if((sample.nLoopStart + 4 < sample.nLoopEnd) && (sample.nLoopEnd <= sample.nLength)) sample.uFlags.set(CHN_LOOP); sample.nC5Speed = sampleRate; if(!sample.nC5Speed) sample.nC5Speed = 22050; sample.nVolume = static_cast(volume / 256); if(!sample.nVolume || sample.nVolume > 256) sample.nVolume = 256; sample.Convert(MOD_TYPE_IT, GetType()); FileReader nameChunk = chunks.GetChunk(IFFChunk::idNAME); if(nameChunk.IsValid()) nameChunk.ReadString(m_szNames[nSample], nameChunk.GetLength()); else m_szNames[nSample] = ""; sampleIO.ReadSample(sample, sampleData); sample.PrecomputeLoops(*this, false); return true; } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/SampleFormatBRR.cpp0000644000175000017500000000777114144043206021551 00000000000000/* * SampleFormatBRR.cpp * ------------------- * Purpose: BRR (SNES Bit Rate Reduction) sample format import. * Notes : This format has no magic bytes, so frame headers are thoroughly validated. * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Sndfile.h" #include "../common/FileReader.h" OPENMPT_NAMESPACE_BEGIN static void ProcessBRRSample(int32 sample, int16 *output, uint8 range, uint8 filter) { if(sample >= 8) sample -= 16; if(range <= 12) sample = mpt::rshift_signed(mpt::lshift_signed(sample, range), 1); else sample = (sample < 0) ? -2048 : 0; // Implementations do not fully agree on what to do in this case. This is what bsnes does. // Apply prediction filter // Note 1: It is okay that we may access data before the first sample point because this memory is reserved for interpolation // Note 2: The use of signed shift arithmetic is crucial for some samples (e.g. killer lead.brr, Mac2.brr) // Note 3: Divisors are twice of what is written in the respective comments, as all sample data is divided by 2 (again crucial for accuracy) static_assert(InterpolationLookaheadBufferSize >= 2); switch(filter) { case 1: // y(n) = x(n) + x(n-1) * 15/16 sample += mpt::rshift_signed(output[-1] * 15, 5); break; case 2: // y(n) = x(n) + x(n-1) * 61/32 - x(n-2) * 15/16 sample += mpt::rshift_signed(output[-1] * 61, 6) + mpt::rshift_signed(output[-2] * -15, 5); break; case 3: // y(n) = x(n) + x(n-1) * 115/64 - x(n-2) * 13/16 sample += mpt::rshift_signed(output[-1] * 115, 7) + mpt::rshift_signed(output[-2] * -13, 5); break; } sample = std::clamp(sample, int32(-32768), int32(32767)) * 2; if(sample > 32767) sample -= 65536; else if(sample < -32768) sample += 65536; output[0] = static_cast(sample); } bool CSoundFile::ReadBRRSample(SAMPLEINDEX sample, FileReader &file) { const auto fileSize = file.GetLength(); if(fileSize < 9 || fileSize > uint16_max) return false; const bool hasLoopInfo = (fileSize % 9) == 2; if((fileSize % 9) != 0 && !hasLoopInfo) return false; file.Rewind(); SmpLength loopStart = 0; if(hasLoopInfo) { loopStart = file.ReadUint16LE(); if(loopStart >= fileSize) return false; if((loopStart % 9) != 0) return false; } // First scan the file for validity and consistency // Note: There are some files with loop start set but ultimately the loop is never enabled. Cannot use this as a consistency check. // Very few files also have a filter set on the first block, so we cannot reject those either. bool enableLoop = false, first = true; while(!file.EndOfFile()) { const auto block = file.ReadArray(); const bool isLast = (block[0] & 0x01) != 0; const bool isLoop = (block[0] & 0x02) != 0; const uint8 range = block[0] >> 4u; if(isLast != file.EndOfFile()) return false; if(!first && enableLoop != isLoop) return false; // While a range of 13 is technically invalid as well, it can be found in the wild. if(range > 13) return false; enableLoop = isLoop; first = false; } file.Seek(hasLoopInfo ? 2 : 0); DestroySampleThreadsafe(sample); ModSample &mptSmp = Samples[sample]; mptSmp.Initialize(); mptSmp.uFlags = CHN_16BIT; mptSmp.nLength = mpt::saturate_cast((fileSize - (hasLoopInfo ? 2 : 0)) * 16 / 9); if(enableLoop) mptSmp.SetLoop(loopStart * 16 / 9, mptSmp.nLength, true, false, *this); mptSmp.nC5Speed = 32000; m_szNames[sample] = ""; if(!mptSmp.AllocateSample()) return false; int16 *output = mptSmp.sample16(); while(!file.EndOfFile()) { const auto block = file.ReadArray(); const uint8 range = block[0] >> 4u; const uint8 filter = (block[0] >> 2) & 0x03; for(int i = 0; i < 8; i++) { ProcessBRRSample(block[i + 1] >> 4u, output, range, filter); ProcessBRRSample(block[i + 1] & 0x0F, output + 1, range, filter); output += 2; } } mptSmp.Convert(MOD_TYPE_IT, GetType()); mptSmp.PrecomputeLoops(*this, false); return true; } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/SampleFormatFLAC.cpp0000644000175000017500000006044014072320411021615 00000000000000/* * SampleFormatFLAC.cpp * -------------------- * Purpose: FLAC sample import. * Notes : * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Sndfile.h" #ifdef MODPLUG_TRACKER #include "../mptrack/TrackerSettings.h" #endif //MODPLUG_TRACKER #ifndef MODPLUG_NO_FILESAVE #include "../common/mptFileIO.h" #endif #include "../common/misc_util.h" #include "Tagging.h" #include "Loaders.h" #include "WAVTools.h" #include "../common/FileReader.h" #include "modsmp_ctrl.h" #include "openmpt/soundbase/Copy.hpp" #include "openmpt/soundbase/SampleConvert.hpp" #include "openmpt/soundbase/SampleDecode.hpp" #include "../soundlib/SampleCopy.h" #include "../soundlib/ModSampleCopy.h" #include "mpt/io/base.hpp" #include "mpt/io/io.hpp" #include "mpt/io/io_stdstream.hpp" //#include "mpt/crc/crc.hpp" #include "OggStream.h" #ifdef MPT_WITH_OGG #if MPT_COMPILER_CLANG #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wreserved-id-macro" #endif // MPT_COMPILER_CLANG #include #if MPT_COMPILER_CLANG #pragma clang diagnostic pop #endif // MPT_COMPILER_CLANG #endif // MPT_WITH_OGG #ifdef MPT_WITH_FLAC #if MPT_COMPILER_CLANG #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wreserved-id-macro" #endif // MPT_COMPILER_CLANG #include #include #include #if MPT_COMPILER_CLANG #pragma clang diagnostic pop #endif // MPT_COMPILER_CLANG #endif // MPT_WITH_FLAC OPENMPT_NAMESPACE_BEGIN /////////////////////////////////////////////////////////////////////////////////////////////////// // FLAC Samples #ifdef MPT_WITH_FLAC struct FLACDecoder { FileReader &file; CSoundFile &sndFile; SAMPLEINDEX sample; bool ready; FLACDecoder(FileReader &f, CSoundFile &sf, SAMPLEINDEX smp) : file(f), sndFile(sf), sample(smp), ready(false) { } static FLAC__StreamDecoderReadStatus read_cb(const FLAC__StreamDecoder *, FLAC__byte buffer[], size_t *bytes, void *client_data) { FileReader &file = static_cast(client_data)->file; if(*bytes > 0) { FileReader::off_t readBytes = *bytes; LimitMax(readBytes, file.BytesLeft()); file.ReadRaw(mpt::byte_cast(mpt::span(buffer, readBytes))); *bytes = readBytes; if(*bytes == 0) return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; else return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; } else { return FLAC__STREAM_DECODER_READ_STATUS_ABORT; } } static FLAC__StreamDecoderSeekStatus seek_cb(const FLAC__StreamDecoder *, FLAC__uint64 absolute_byte_offset, void *client_data) { FileReader &file = static_cast(client_data)->file; if(!file.Seek(static_cast(absolute_byte_offset))) return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR; else return FLAC__STREAM_DECODER_SEEK_STATUS_OK; } static FLAC__StreamDecoderTellStatus tell_cb(const FLAC__StreamDecoder *, FLAC__uint64 *absolute_byte_offset, void *client_data) { FileReader &file = static_cast(client_data)->file; *absolute_byte_offset = file.GetPosition(); return FLAC__STREAM_DECODER_TELL_STATUS_OK; } static FLAC__StreamDecoderLengthStatus length_cb(const FLAC__StreamDecoder *, FLAC__uint64 *stream_length, void *client_data) { FileReader &file = static_cast(client_data)->file; *stream_length = file.GetLength(); return FLAC__STREAM_DECODER_LENGTH_STATUS_OK; } static FLAC__bool eof_cb(const FLAC__StreamDecoder *, void *client_data) { FileReader &file = static_cast(client_data)->file; return file.NoBytesLeft(); } static FLAC__StreamDecoderWriteStatus write_cb(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *const buffer[], void *client_data) { FLACDecoder &client = *static_cast(client_data); ModSample &sample = client.sndFile.GetSample(client.sample); if(frame->header.number.sample_number >= sample.nLength || !client.ready) { // We're reading beyond the sample size already, or we aren't even ready to decode yet! return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; } // Number of samples to be copied in this call const SmpLength copySamples = std::min(static_cast(frame->header.blocksize), static_cast(sample.nLength - frame->header.number.sample_number)); // Number of target channels const uint8 modChannels = sample.GetNumChannels(); // Offset (in samples) into target data const size_t offset = static_cast(frame->header.number.sample_number) * modChannels; // Source size in bytes const size_t srcSize = frame->header.blocksize * 4; // Source bit depth const unsigned int bps = frame->header.bits_per_sample; MPT_ASSERT((bps <= 8 && sample.GetElementarySampleSize() == 1) || (bps > 8 && sample.GetElementarySampleSize() == 2)); MPT_ASSERT(modChannels <= FLAC__stream_decoder_get_channels(decoder)); MPT_ASSERT(bps == FLAC__stream_decoder_get_bits_per_sample(decoder)); MPT_UNREFERENCED_PARAMETER(decoder); // decoder is unused if ASSERTs are compiled out // Do the sample conversion for(uint8 chn = 0; chn < modChannels; chn++) { if(bps <= 8) { int8 *sampleData8 = sample.sample8() + offset; CopySample, SC::DecodeIdentity > >(sampleData8 + chn, copySamples, modChannels, buffer[chn], srcSize, 1); } else if(bps <= 16) { int16 *sampleData16 = sample.sample16() + offset; CopySample, SC::DecodeIdentity > >(sampleData16 + chn, copySamples, modChannels, buffer[chn], srcSize, 1); } else if(bps <= 24) { int16 *sampleData16 = sample.sample16() + offset; CopySample, SC::DecodeIdentity > >(sampleData16 + chn, copySamples, modChannels, buffer[chn], srcSize, 1); } else if(bps <= 32) { int16 *sampleData16 = sample.sample16() + offset; CopySample, SC::DecodeIdentity > >(sampleData16 + chn, copySamples, modChannels, buffer[chn], srcSize, 1); } } return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; } static void metadata_cb(const FLAC__StreamDecoder *, const FLAC__StreamMetadata *metadata, void *client_data) { FLACDecoder &client = *static_cast(client_data); if(client.sample > client.sndFile.GetNumSamples()) { client.sndFile.m_nSamples = client.sample; } ModSample &sample = client.sndFile.GetSample(client.sample); if(metadata->type == FLAC__METADATA_TYPE_STREAMINFO && metadata->data.stream_info.total_samples != 0) { // Init sample information client.sndFile.DestroySampleThreadsafe(client.sample); client.sndFile.m_szNames[client.sample] = ""; sample.Initialize(); sample.uFlags.set(CHN_16BIT, metadata->data.stream_info.bits_per_sample > 8); sample.uFlags.set(CHN_STEREO, metadata->data.stream_info.channels > 1); sample.nLength = mpt::saturate_cast(metadata->data.stream_info.total_samples); LimitMax(sample.nLength, MAX_SAMPLE_LENGTH); sample.nC5Speed = metadata->data.stream_info.sample_rate; client.ready = (sample.AllocateSample() != 0); } else if(metadata->type == FLAC__METADATA_TYPE_APPLICATION && !memcmp(metadata->data.application.id, "riff", 4) && client.ready) { // Try reading RIFF loop points and other sample information FileReader data(mpt::as_span(metadata->data.application.data, metadata->length)); FileReader::ChunkList chunks = data.ReadChunks(2); // We're not really going to read a WAV file here because there will be only one RIFF chunk per metadata event, but we can still re-use the code for parsing RIFF metadata... WAVReader riffReader(data); riffReader.FindMetadataChunks(chunks); riffReader.ApplySampleSettings(sample, client.sndFile.GetCharsetInternal(), client.sndFile.m_szNames[client.sample]); } else if(metadata->type == FLAC__METADATA_TYPE_VORBIS_COMMENT && client.ready) { // Try reading Vorbis Comments for sample title, sample rate and loop points SmpLength loopStart = 0, loopLength = 0; for(FLAC__uint32 i = 0; i < metadata->data.vorbis_comment.num_comments; i++) { const char *tag = mpt::byte_cast(metadata->data.vorbis_comment.comments[i].entry); const FLAC__uint32 length = metadata->data.vorbis_comment.comments[i].length; if(length > 6 && !mpt::CompareNoCaseAscii(tag, "TITLE=", 6)) { client.sndFile.m_szNames[client.sample] = mpt::ToCharset(client.sndFile.GetCharsetInternal(), mpt::Charset::UTF8, mpt::String::ReadBuf(mpt::String::maybeNullTerminated, tag + 6, length - 6)); } else if(length > 11 && !mpt::CompareNoCaseAscii(tag, "SAMPLERATE=", 11)) { uint32 sampleRate = ConvertStrTo(tag + 11); if(sampleRate > 0) sample.nC5Speed = sampleRate; } else if(length > 10 && !mpt::CompareNoCaseAscii(tag, "LOOPSTART=", 10)) { loopStart = ConvertStrTo(tag + 10); } else if(length > 11 && !mpt::CompareNoCaseAscii(tag, "LOOPLENGTH=", 11)) { loopLength = ConvertStrTo(tag + 11); } } if(loopLength > 0) { sample.nLoopStart = loopStart; sample.nLoopEnd = loopStart + loopLength; sample.uFlags.set(CHN_LOOP); sample.SanitizeLoops(); } } } static void error_cb(const FLAC__StreamDecoder *, FLAC__StreamDecoderErrorStatus, void *) { } }; #endif // MPT_WITH_FLAC bool CSoundFile::ReadFLACSample(SAMPLEINDEX sample, FileReader &file) { #ifdef MPT_WITH_FLAC file.Rewind(); bool isOgg = false; #ifdef MPT_WITH_OGG uint32 oggFlacBitstreamSerial = 0; #endif // Check whether we are dealing with native FLAC, OggFlac or no FLAC at all. if(file.ReadMagic("fLaC")) { // ok isOgg = false; #ifdef MPT_WITH_OGG } else if(file.ReadMagic("OggS")) { // use libogg to find the first OggFlac stream header file.Rewind(); bool oggOK = false; bool needMoreData = true; constexpr long bufsize = 65536; std::size_t readSize = 0; char *buf = nullptr; ogg_sync_state oy; MemsetZero(oy); ogg_page og; MemsetZero(og); std::map oggStreams; ogg_packet op; MemsetZero(op); if(ogg_sync_init(&oy) != 0) { return false; } while(needMoreData) { if(file.NoBytesLeft()) { // stop at EOF oggOK = false; needMoreData = false; break; } buf = ogg_sync_buffer(&oy, bufsize); if(!buf) { oggOK = false; needMoreData = false; break; } readSize = file.ReadRaw(mpt::span(buf, bufsize)).size(); if(ogg_sync_wrote(&oy, static_cast(readSize)) != 0) { oggOK = false; needMoreData = false; break; } while(ogg_sync_pageout(&oy, &og) == 1) { if(!ogg_page_bos(&og)) { // we stop scanning when seeing the first noo-begin-of-stream page oggOK = false; needMoreData = false; break; } uint32 serial = ogg_page_serialno(&og); if(!oggStreams[serial]) { // previously unseen stream serial oggStreams[serial] = new ogg_stream_state(); MemsetZero(*(oggStreams[serial])); if(ogg_stream_init(oggStreams[serial], serial) != 0) { delete oggStreams[serial]; oggStreams.erase(serial); oggOK = false; needMoreData = false; break; } } if(ogg_stream_pagein(oggStreams[serial], &og) != 0) { // invalid page oggOK = false; needMoreData = false; break; } if(ogg_stream_packetout(oggStreams[serial], &op) != 1) { // partial or broken packet, continue with more data continue; } if(op.packetno != 0) { // non-begin-of-stream packet. // This should not appear on first page for any known ogg codec, // but deal gracefully with badly mused streams in that regard. continue; } FileReader packet(mpt::as_span(op.packet, op.bytes)); if(packet.ReadIntLE() == 0x7f && packet.ReadMagic("FLAC")) { // looks like OggFlac oggOK = true; oggFlacBitstreamSerial = serial; needMoreData = false; break; } } } while(oggStreams.size() > 0) { uint32 serial = oggStreams.begin()->first; ogg_stream_clear(oggStreams[serial]); delete oggStreams[serial]; oggStreams.erase(serial); } ogg_sync_clear(&oy); if(!oggOK) { return false; } isOgg = true; #else // !MPT_WITH_OGG } else if(file.CanRead(78) && file.ReadMagic("OggS")) { // first OggFlac page is exactly 78 bytes long // only support plain OggFlac here with the FLAC logical bitstream being the first one uint8 oggPageVersion = file.ReadIntLE(); uint8 oggPageHeaderType = file.ReadIntLE(); uint64 oggPageGranulePosition = file.ReadIntLE(); uint32 oggPageBitstreamSerialNumber = file.ReadIntLE(); uint32 oggPageSequenceNumber = file.ReadIntLE(); uint32 oggPageChecksum = file.ReadIntLE(); uint8 oggPageSegments = file.ReadIntLE(); uint8 oggPageSegmentLength = file.ReadIntLE(); if(oggPageVersion != 0) { // unknown Ogg version return false; } if(!(oggPageHeaderType & 0x02) || (oggPageHeaderType& 0x01)) { // not BOS or continuation return false; } if(oggPageGranulePosition != 0) { // not starting position return false; } if(oggPageSequenceNumber != 0) { // not first page return false; } // skip CRC check for now if(oggPageSegments != 1) { // first OggFlac page must contain exactly 1 segment return false; } if(oggPageSegmentLength != 51) { // segment length must be 51 bytes in OggFlac mapping return false; } if(file.ReadIntLE() != 0x7f) { // OggFlac mapping demands 0x7f packet type return false; } if(!file.ReadMagic("FLAC")) { // OggFlac magic return false; } if(file.ReadIntLE() != 0x01) { // OggFlac major version return false; } // by now, we are pretty confident that we are not parsing random junk isOgg = true; #endif // MPT_WITH_OGG } else { return false; } file.Rewind(); FLAC__StreamDecoder *decoder = FLAC__stream_decoder_new(); if(decoder == nullptr) { return false; } #ifdef MPT_WITH_OGG if(isOgg) { // force flac decoding of the logical bitstream that actually is OggFlac if(!FLAC__stream_decoder_set_ogg_serial_number(decoder, oggFlacBitstreamSerial)) { FLAC__stream_decoder_delete(decoder); return false; } } #endif // Give me all the metadata! FLAC__stream_decoder_set_metadata_respond_all(decoder); FLACDecoder client(file, *this, sample); // Init decoder FLAC__StreamDecoderInitStatus initStatus = isOgg ? FLAC__stream_decoder_init_ogg_stream(decoder, FLACDecoder::read_cb, FLACDecoder::seek_cb, FLACDecoder::tell_cb, FLACDecoder::length_cb, FLACDecoder::eof_cb, FLACDecoder::write_cb, FLACDecoder::metadata_cb, FLACDecoder::error_cb, &client) : FLAC__stream_decoder_init_stream(decoder, FLACDecoder::read_cb, FLACDecoder::seek_cb, FLACDecoder::tell_cb, FLACDecoder::length_cb, FLACDecoder::eof_cb, FLACDecoder::write_cb, FLACDecoder::metadata_cb, FLACDecoder::error_cb, &client) ; if(initStatus != FLAC__STREAM_DECODER_INIT_STATUS_OK) { FLAC__stream_decoder_delete(decoder); return false; } // Decode file FLAC__stream_decoder_process_until_end_of_stream(decoder); FLAC__stream_decoder_finish(decoder); FLAC__stream_decoder_delete(decoder); if(client.ready && Samples[sample].HasSampleData()) { Samples[sample].Convert(MOD_TYPE_IT, GetType()); Samples[sample].PrecomputeLoops(*this, false); return true; } #else MPT_UNREFERENCED_PARAMETER(sample); MPT_UNREFERENCED_PARAMETER(file); #endif // MPT_WITH_FLAC return false; } #ifdef MPT_WITH_FLAC // RAII-style helper struct for FLAC encoder struct FLAC__StreamEncoder_RAII { std::ostream &f; FLAC__StreamEncoder *encoder = nullptr; operator FLAC__StreamEncoder *() { return encoder; } FLAC__StreamEncoder_RAII(std::ostream &f_) : f(f_), encoder(FLAC__stream_encoder_new()) { } ~FLAC__StreamEncoder_RAII() { FLAC__stream_encoder_delete(encoder); } static FLAC__StreamEncoderWriteStatus StreamEncoderWriteCallback(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned current_frame, void *client_data) { mpt::ofstream & file = *reinterpret_cast(client_data); MPT_UNUSED_VARIABLE(encoder); MPT_UNUSED_VARIABLE(samples); MPT_UNUSED_VARIABLE(current_frame); if(!mpt::IO::WriteRaw(file, mpt::as_span(buffer, bytes))) { return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR; } return FLAC__STREAM_ENCODER_WRITE_STATUS_OK; } static FLAC__StreamEncoderSeekStatus StreamEncoderSeekCallback(const FLAC__StreamEncoder *encoder, FLAC__uint64 absolute_byte_offset, void *client_data) { mpt::ofstream & file = *reinterpret_cast(client_data); MPT_UNUSED_VARIABLE(encoder); if(!mpt::in_range(absolute_byte_offset)) { return FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR; } if(!mpt::IO::SeekAbsolute(file, static_cast(absolute_byte_offset))) { return FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR; } return FLAC__STREAM_ENCODER_SEEK_STATUS_OK; } static FLAC__StreamEncoderTellStatus StreamEncoderTellCallback(const FLAC__StreamEncoder *encoder, FLAC__uint64 *absolute_byte_offset, void *client_data) { mpt::ofstream & file = *reinterpret_cast(client_data); MPT_UNUSED_VARIABLE(encoder); mpt::IO::Offset pos = mpt::IO::TellWrite(file); if(pos < 0) { return FLAC__STREAM_ENCODER_TELL_STATUS_ERROR; } if(!mpt::in_range(pos)) { return FLAC__STREAM_ENCODER_TELL_STATUS_ERROR; } *absolute_byte_offset = static_cast(pos); return FLAC__STREAM_ENCODER_TELL_STATUS_OK; } }; class FLAC__StreamMetadata_RAII : public std::vector { public: FLAC__StreamMetadata_RAII(std::initializer_list init) : std::vector(init) { } ~FLAC__StreamMetadata_RAII() { for(auto m : *this) { FLAC__metadata_object_delete(m); } } }; #endif #ifndef MODPLUG_NO_FILESAVE bool CSoundFile::SaveFLACSample(SAMPLEINDEX nSample, std::ostream &f) const { #ifdef MPT_WITH_FLAC const ModSample &sample = Samples[nSample]; if(sample.uFlags[CHN_ADLIB]) return false; FLAC__StreamEncoder_RAII encoder(f); if(encoder == nullptr) return false; uint32 sampleRate = sample.GetSampleRate(GetType()); // First off, set up all the metadata... FLAC__StreamMetadata_RAII metadata = { FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT), FLAC__metadata_object_new(FLAC__METADATA_TYPE_APPLICATION), // MPT sample information FLAC__metadata_object_new(FLAC__METADATA_TYPE_APPLICATION), // Loop points FLAC__metadata_object_new(FLAC__METADATA_TYPE_APPLICATION), // Cue points }; unsigned numBlocks = 2; if(metadata[0]) { // Store sample name FLAC__StreamMetadata_VorbisComment_Entry entry; FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(&entry, "TITLE", mpt::ToCharset(mpt::Charset::UTF8, GetCharsetInternal(), m_szNames[nSample]).c_str()); FLAC__metadata_object_vorbiscomment_append_comment(metadata[0], entry, false); FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(&entry, "ENCODER", mpt::ToCharset(mpt::Charset::UTF8, Version::Current().GetOpenMPTVersionString()).c_str()); FLAC__metadata_object_vorbiscomment_append_comment(metadata[0], entry, false); if(sampleRate > FLAC__MAX_SAMPLE_RATE) { // FLAC only supports a sample rate of up to 655350 Hz. // Store the real sample rate in a custom Vorbis comment. FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(&entry, "SAMPLERATE", mpt::afmt::val(sampleRate).c_str()); FLAC__metadata_object_vorbiscomment_append_comment(metadata[0], entry, false); } } if(metadata[1]) { // Write MPT sample information memcpy(metadata[1]->data.application.id, "riff", 4); struct { RIFFChunk header; WAVExtraChunk mptInfo; } chunk; chunk.header.id = RIFFChunk::idxtra; chunk.header.length = sizeof(WAVExtraChunk); chunk.mptInfo.ConvertToWAV(sample, GetType()); const uint32 length = sizeof(RIFFChunk) + sizeof(WAVExtraChunk); FLAC__metadata_object_application_set_data(metadata[1], reinterpret_cast(&chunk), length, true); } if(metadata[numBlocks] && (sample.uFlags[CHN_LOOP | CHN_SUSTAINLOOP] || ModCommand::IsNote(sample.rootNote))) { // Store loop points / root note information memcpy(metadata[numBlocks]->data.application.id, "riff", 4); struct { RIFFChunk header; WAVSampleInfoChunk info; WAVSampleLoop loops[2]; } chunk; chunk.header.id = RIFFChunk::idsmpl; chunk.header.length = sizeof(WAVSampleInfoChunk); chunk.info.ConvertToWAV(sample.GetSampleRate(GetType()), sample.rootNote); if(sample.uFlags[CHN_SUSTAINLOOP]) { chunk.loops[chunk.info.numLoops++].ConvertToWAV(sample.nSustainStart, sample.nSustainEnd, sample.uFlags[CHN_PINGPONGSUSTAIN]); chunk.header.length += sizeof(WAVSampleLoop); } if(sample.uFlags[CHN_LOOP]) { chunk.loops[chunk.info.numLoops++].ConvertToWAV(sample.nLoopStart, sample.nLoopEnd, sample.uFlags[CHN_PINGPONGLOOP]); chunk.header.length += sizeof(WAVSampleLoop); } const uint32 length = sizeof(RIFFChunk) + chunk.header.length; FLAC__metadata_object_application_set_data(metadata[numBlocks], reinterpret_cast(&chunk), length, true); numBlocks++; } if(metadata[numBlocks] && sample.HasCustomCuePoints()) { // Store cue points memcpy(metadata[numBlocks]->data.application.id, "riff", 4); struct { RIFFChunk header; uint32le numPoints; WAVCuePoint cues[mpt::array_size::size]; } chunk{}; chunk.header.id = RIFFChunk::idcue_; chunk.header.length = 4 + sizeof(chunk.cues); chunk.numPoints = mpt::saturate_cast(std::size(sample.cues)); for(uint32 i = 0; i < std::size(sample.cues); i++) { chunk.cues[i].ConvertToWAV(i, sample.cues[i]); } const uint32 length = sizeof(RIFFChunk) + chunk.header.length; FLAC__metadata_object_application_set_data(metadata[numBlocks], reinterpret_cast(&chunk), length, true); numBlocks++; } // FLAC allows a maximum sample rate of 655350 Hz. // If the real rate is higher, we store it in a Vorbis comment above. LimitMax(sampleRate, FLAC__MAX_SAMPLE_RATE); if(!FLAC__format_sample_rate_is_subset(sampleRate)) { // FLAC only supports 10 Hz granularity for frequencies above 65535 Hz if the streamable subset is chosen. FLAC__stream_encoder_set_streamable_subset(encoder, false); } FLAC__stream_encoder_set_channels(encoder, sample.GetNumChannels()); FLAC__stream_encoder_set_bits_per_sample(encoder, sample.GetElementarySampleSize() * 8); FLAC__stream_encoder_set_sample_rate(encoder, sampleRate); FLAC__stream_encoder_set_total_samples_estimate(encoder, sample.nLength); FLAC__stream_encoder_set_metadata(encoder, metadata.data(), numBlocks); #ifdef MODPLUG_TRACKER FLAC__stream_encoder_set_compression_level(encoder, TrackerSettings::Instance().m_FLACCompressionLevel); #endif // MODPLUG_TRACKER bool success = FLAC__stream_encoder_init_stream(encoder, &FLAC__StreamEncoder_RAII::StreamEncoderWriteCallback, &FLAC__StreamEncoder_RAII::StreamEncoderSeekCallback, &FLAC__StreamEncoder_RAII::StreamEncoderTellCallback, nullptr, &encoder.f) == FLAC__STREAM_ENCODER_INIT_STATUS_OK; // Convert and encode sample data SmpLength framesRemain = sample.nLength, framesRead = 0; const uint8 numChannels = sample.GetNumChannels(); FLAC__int32 buffer[mpt::IO::BUFFERSIZE_TINY]; while(framesRemain && success) { const SmpLength copyFrames = std::min(framesRemain, mpt::saturate_cast(std::size(buffer) / numChannels)); // First, convert to a 32-bit integer buffer switch(sample.GetElementarySampleSize()) { case 1: std::copy(sample.sample8() + framesRead * numChannels, sample.sample8() + (framesRead + copyFrames) * numChannels, std::begin(buffer)); break; case 2: std::copy(sample.sample16() + framesRead * numChannels, sample.sample16() + (framesRead + copyFrames) * numChannels, std::begin(buffer)); break; default: MPT_ASSERT_NOTREACHED(); } // Now do the actual encoding success = FLAC__stream_encoder_process_interleaved(encoder, buffer, copyFrames) != static_cast(false); framesRead += copyFrames; framesRemain -= copyFrames; } FLAC__stream_encoder_finish(encoder); return success; #else MPT_UNREFERENCED_PARAMETER(nSample); MPT_UNREFERENCED_PARAMETER(f); return false; #endif // MPT_WITH_FLAC } #endif // MODPLUG_NO_FILESAVE OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/SampleFormatMediaFoundation.cpp0000644000175000017500000003117214072320411024156 00000000000000/* * SampleFormatMediaSoundation.cpp * ------------------------------- * Purpose: MediaFoundation sample import. * Notes : * Authors: Joern Heusipp * OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Sndfile.h" #ifndef MODPLUG_NO_FILESAVE #include "../common/mptFileIO.h" #endif #include "../common/misc_util.h" #include "Tagging.h" #include "Loaders.h" #include "../common/FileReader.h" #include "modsmp_ctrl.h" #include "openmpt/soundbase/Copy.hpp" #include "../soundlib/ModSampleCopy.h" #include "../common/ComponentManager.h" #if defined(MPT_WITH_MEDIAFOUNDATION) #include #include #include #include #include #include #include #endif // MPT_WITH_MEDIAFOUNDATION OPENMPT_NAMESPACE_BEGIN #if defined(MPT_WITH_MEDIAFOUNDATION) struct PropVariant : PROPVARIANT { PropVariant() { PropVariantInit(this); } ~PropVariant() { PropVariantClear(this); } }; // Implementing IMFByteStream is apparently not enough to stream raw bytes // data to MediaFoundation. // Additionally, one has to also implement a custom IMFAsyncResult for the // BeginRead/EndRead interface which allows transferring the number of read // bytes around. // To make things even worse, MediaFoundation fails to detect some AAC and MPEG // files if a non-file-based or read-only stream is used for opening. // The only sane option which remains if we do not have an on-disk filename // available: // 1 - write a temporary file // 2 - close it // 3 - open it using MediaFoundation. // We use FILE_ATTRIBUTE_TEMPORARY which will try to keep the file data in // memory just like regular allocated memory and reduce the overhead basically // to memcpy. static FileTags ReadMFMetadata(IMFMediaSource *mediaSource) { FileTags tags; CComPtr presentationDescriptor; if(!SUCCEEDED(mediaSource->CreatePresentationDescriptor(&presentationDescriptor))) { return tags; } DWORD streams = 0; if(!SUCCEEDED(presentationDescriptor->GetStreamDescriptorCount(&streams))) { return tags; } CComPtr metadataProvider; if(!SUCCEEDED(MFGetService(mediaSource, MF_METADATA_PROVIDER_SERVICE, IID_IMFMetadataProvider, (void**)&metadataProvider))) { return tags; } CComPtr metadata; if(!SUCCEEDED(metadataProvider->GetMFMetadata(presentationDescriptor, 0, 0, &metadata))) { return tags; } PropVariant varPropNames; if(!SUCCEEDED(metadata->GetAllPropertyNames(&varPropNames))) { return tags; } for(DWORD propIndex = 0; propIndex < varPropNames.calpwstr.cElems; ++propIndex) { PropVariant propVal; LPWSTR propName = varPropNames.calpwstr.pElems[propIndex]; if(S_OK != metadata->GetProperty(propName, &propVal)) { break; } std::wstring stringVal; #if !MPT_OS_WINDOWS_WINRT // WTF, no PropVariantToString() in WinRT std::vector wcharVal(256); for(;;) { HRESULT hrToString = PropVariantToString(propVal, wcharVal.data(), mpt::saturate_cast(wcharVal.size())); if(hrToString == S_OK) { stringVal = wcharVal.data(); break; } else if(hrToString == ERROR_INSUFFICIENT_BUFFER) { wcharVal.resize(mpt::exponential_grow(wcharVal.size())); } else { break; } } #endif // !MPT_OS_WINDOWS_WINRT if(stringVal.length() > 0) { if(propName == std::wstring(L"Author")) tags.artist = mpt::ToUnicode(stringVal); if(propName == std::wstring(L"Title")) tags.title = mpt::ToUnicode(stringVal); if(propName == std::wstring(L"WM/AlbumTitle")) tags.album = mpt::ToUnicode(stringVal); if(propName == std::wstring(L"WM/Track")) tags.trackno = mpt::ToUnicode(stringVal); if(propName == std::wstring(L"WM/Year")) tags.year = mpt::ToUnicode(stringVal); if(propName == std::wstring(L"WM/Genre")) tags.genre = mpt::ToUnicode(stringVal); } } return tags; } class ComponentMediaFoundation : public ComponentLibrary { MPT_DECLARE_COMPONENT_MEMBERS(ComponentMediaFoundation, "MediaFoundation") public: ComponentMediaFoundation() : ComponentLibrary(ComponentTypeSystem) { return; } bool DoInitialize() override { #if !MPT_OS_WINDOWS_WINRT if(!(true && AddLibrary("mf", mpt::LibraryPath::System(P_("mf"))) && AddLibrary("mfplat", mpt::LibraryPath::System(P_("mfplat"))) && AddLibrary("mfreadwrite", mpt::LibraryPath::System(P_("mfreadwrite"))) && AddLibrary("propsys", mpt::LibraryPath::System(P_("propsys"))) )) { return false; } #endif // !MPT_OS_WINDOWS_WINRT if(!SUCCEEDED(MFStartup(MF_VERSION))) { return false; } return true; } virtual ~ComponentMediaFoundation() { if(IsAvailable()) { MFShutdown(); } } }; #endif // MPT_WITH_MEDIAFOUNDATION #ifdef MODPLUG_TRACKER std::vector CSoundFile::GetMediaFoundationFileTypes() { std::vector result; #if defined(MPT_WITH_MEDIAFOUNDATION) ComponentHandle mf; if(!IsComponentAvailable(mf)) { return result; } std::map guidMap; HKEY hkHandlers = NULL; LSTATUS regResult = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows Media Foundation\\ByteStreamHandlers", 0, KEY_READ, &hkHandlers); if(regResult != ERROR_SUCCESS) { return result; } for(DWORD handlerIndex = 0; ; ++handlerIndex) { WCHAR handlerTypeBuf[256]; MemsetZero(handlerTypeBuf); regResult = RegEnumKeyW(hkHandlers, handlerIndex, handlerTypeBuf, 256); if(regResult != ERROR_SUCCESS) { break; } std::wstring handlerType = handlerTypeBuf; if(handlerType.length() < 1) { continue; } HKEY hkHandler = NULL; regResult = RegOpenKeyExW(hkHandlers, handlerTypeBuf, 0, KEY_READ, &hkHandler); if(regResult != ERROR_SUCCESS) { continue; } std::vector valueNameBuf(16384); std::vector valueData(16384); for(DWORD valueIndex = 0; ; ++valueIndex) { std::fill(valueNameBuf.begin(), valueNameBuf.end(), WCHAR{0}); DWORD valueNameBufLen = 16384; DWORD valueType = 0; std::fill(valueData.begin(), valueData.end(), BYTE{0}); DWORD valueDataLen = 16384; regResult = RegEnumValueW(hkHandler, valueIndex, valueNameBuf.data(), &valueNameBufLen, NULL, &valueType, valueData.data(), &valueDataLen); if(regResult != ERROR_SUCCESS) { break; } if(valueNameBufLen <= 0 || valueType != REG_SZ || valueDataLen <= 0) { continue; } std::wstring guid = std::wstring(valueNameBuf.data()); mpt::ustring description = mpt::ToUnicode(ParseMaybeNullTerminatedStringFromBufferWithSizeInBytes(valueData.data(), valueDataLen)); description = mpt::replace(description, U_("Byte Stream Handler"), U_("Files")); description = mpt::replace(description, U_("ByteStreamHandler"), U_("Files")); guidMap[guid] .ShortName(U_("mf")) .Description(description) ; if(handlerType[0] == L'.') { guidMap[guid].AddExtension(mpt::PathString::FromWide(handlerType.substr(1))); } else { guidMap[guid].AddMimeType(mpt::ToCharset(mpt::Charset::ASCII, handlerType)); } } RegCloseKey(hkHandler); hkHandler = NULL; } RegCloseKey(hkHandlers); hkHandlers = NULL; for(const auto &it : guidMap) { result.push_back(it.second); } #endif // MPT_WITH_MEDIAFOUNDATION return result; } #endif // MODPLUG_TRACKER bool CSoundFile::ReadMediaFoundationSample(SAMPLEINDEX sample, FileReader &file, bool mo3Decode) { #if !defined(MPT_WITH_MEDIAFOUNDATION) MPT_UNREFERENCED_PARAMETER(sample); MPT_UNREFERENCED_PARAMETER(file); MPT_UNREFERENCED_PARAMETER(mo3Decode); return false; #else ComponentHandle mf; if(!IsComponentAvailable(mf)) { return false; } file.Rewind(); // When using MF to decode MP3 samples in MO3 files, we need the mp3 file extension // for some of them or otherwise MF refuses to recognize them. mpt::PathString tmpfileExtension = (mo3Decode ? P_("mp3") : P_("tmp")); OnDiskFileWrapper diskfile(file, tmpfileExtension); if(!diskfile.IsValid()) { return false; } #define MPT_MF_CHECKED(x) do { \ HRESULT hr = (x); \ if(!SUCCEEDED(hr)) \ { \ return false; \ } \ } while(0) CComPtr sourceResolver; MPT_MF_CHECKED(MFCreateSourceResolver(&sourceResolver)); MF_OBJECT_TYPE objectType = MF_OBJECT_INVALID; CComPtr unknownMediaSource; MPT_MF_CHECKED(sourceResolver->CreateObjectFromURL(diskfile.GetFilename().ToWide().c_str(), MF_RESOLUTION_MEDIASOURCE | MF_RESOLUTION_CONTENT_DOES_NOT_HAVE_TO_MATCH_EXTENSION_OR_MIME_TYPE | MF_RESOLUTION_READ, NULL, &objectType, &unknownMediaSource)); if(objectType != MF_OBJECT_MEDIASOURCE) { return false; } CComPtr mediaSource; MPT_MF_CHECKED(unknownMediaSource->QueryInterface(&mediaSource)); FileTags tags = ReadMFMetadata(mediaSource); CComPtr sourceReader; MPT_MF_CHECKED(MFCreateSourceReaderFromMediaSource(mediaSource, NULL, &sourceReader)); CComPtr partialType; MPT_MF_CHECKED(MFCreateMediaType(&partialType)); MPT_MF_CHECKED(partialType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio)); MPT_MF_CHECKED(partialType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM)); MPT_MF_CHECKED(sourceReader->SetCurrentMediaType((DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, NULL, partialType)); CComPtr uncompressedAudioType; MPT_MF_CHECKED(sourceReader->GetCurrentMediaType((DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, &uncompressedAudioType)); MPT_MF_CHECKED(sourceReader->SetStreamSelection((DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, TRUE)); UINT32 numChannels = 0; MPT_MF_CHECKED(uncompressedAudioType->GetUINT32(MF_MT_AUDIO_NUM_CHANNELS, &numChannels)); UINT32 samplesPerSecond = 0; MPT_MF_CHECKED(uncompressedAudioType->GetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, &samplesPerSecond)); UINT32 bitsPerSample = 0; MPT_MF_CHECKED(uncompressedAudioType->GetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, &bitsPerSample)); if(numChannels <= 0 || numChannels > 2) { return false; } if(samplesPerSecond <= 0) { return false; } if(bitsPerSample != 8 && bitsPerSample != 16 && bitsPerSample != 24 && bitsPerSample != 32) { return false; } std::vector rawData; for(;;) { CComPtr mfSample; DWORD mfSampleFlags = 0; CComPtr buffer; MPT_MF_CHECKED(sourceReader->ReadSample((DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, NULL, &mfSampleFlags, NULL, &mfSample)); if(mfSampleFlags & MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED) { break; } if(mfSampleFlags & MF_SOURCE_READERF_ENDOFSTREAM) { break; } MPT_MF_CHECKED(mfSample->ConvertToContiguousBuffer(&buffer)); { BYTE *data = NULL; DWORD dataSize = 0; MPT_MF_CHECKED(buffer->Lock(&data, NULL, &dataSize)); mpt::append(rawData, mpt::byte_cast(data), mpt::byte_cast(data + dataSize)); MPT_MF_CHECKED(buffer->Unlock()); if(rawData.size() / numChannels / (bitsPerSample / 8) > MAX_SAMPLE_LENGTH) { break; } } } std::string sampleName = mpt::ToCharset(GetCharsetInternal(), GetSampleNameFromTags(tags)); if(rawData.size() / numChannels / (bitsPerSample / 8) > MAX_SAMPLE_LENGTH) { return false; } SmpLength length = mpt::saturate_cast(rawData.size() / numChannels / (bitsPerSample/8)); DestroySampleThreadsafe(sample); if(!mo3Decode) { m_szNames[sample] = sampleName; Samples[sample].Initialize(); Samples[sample].nC5Speed = samplesPerSecond; } Samples[sample].nLength = length; Samples[sample].uFlags.set(CHN_16BIT, bitsPerSample >= 16); Samples[sample].uFlags.set(CHN_STEREO, numChannels == 2); Samples[sample].AllocateSample(); if(!Samples[sample].HasSampleData()) { return false; } if(bitsPerSample == 24) { if(numChannels == 2) { CopyStereoInterleavedSample, SC::DecodeInt24<0, littleEndian24> > >(Samples[sample], rawData.data(), rawData.size()); } else { CopyMonoSample, SC::DecodeInt24<0, littleEndian24> > >(Samples[sample], rawData.data(), rawData.size()); } } else if(bitsPerSample == 32) { if(numChannels == 2) { CopyStereoInterleavedSample, SC::DecodeInt32<0, littleEndian32> > >(Samples[sample], rawData.data(), rawData.size()); } else { CopyMonoSample, SC::DecodeInt32<0, littleEndian32> > >(Samples[sample], rawData.data(), rawData.size()); } } else { // just copy std::copy(rawData.data(), rawData.data() + rawData.size(), mpt::byte_cast(Samples[sample].sampleb())); } #undef MPT_MF_CHECKED if(!mo3Decode) { Samples[sample].Convert(MOD_TYPE_IT, GetType()); Samples[sample].PrecomputeLoops(*this, false); } return true; #endif } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/SampleFormatMP3.cpp0000644000175000017500000004242114164356475021533 00000000000000/* * SampleFormatMP3.cpp * ------------------- * Purpose: MP3 sample import. * Notes : * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Sndfile.h" #ifndef MODPLUG_NO_FILESAVE #include "../common/mptFileIO.h" #endif #include "../common/misc_util.h" #include "Tagging.h" #include "Loaders.h" #include "../common/FileReader.h" #include "modsmp_ctrl.h" #include "openmpt/soundbase/Copy.hpp" #include "../soundlib/ModSampleCopy.h" #include "../common/ComponentManager.h" #ifdef MPT_ENABLE_MP3_SAMPLES #include "MPEGFrame.h" #endif // MPT_ENABLE_MP3_SAMPLES #if defined(MPT_WITH_MINIMP3) #include #endif // MPT_WITH_MINIMP3 // mpg123 must be last because of mpg123 large file support insanity #if defined(MPT_WITH_MPG123) #include #include #include #if MPT_OS_OPENBSD // This is kind-of a hack. // See . #if MPT_COMPILER_CLANG #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wreserved-id-macro" #endif #ifdef _FILE_OFFSET_BITS #undef _FILE_OFFSET_BITS #endif #if MPT_COMPILER_CLANG #pragma clang diagnostic pop #endif #endif #include #endif OPENMPT_NAMESPACE_BEGIN /////////////////////////////////////////////////////////////////////////////////////////////////// // MP3 Samples #if defined(MPT_WITH_MPG123) typedef off_t mpg123_off_t; typedef size_t mpg123_size_t; // Check for exactly _MSC_VER as libmpg123 does, in order to also catch clang-cl. #ifdef _MSC_VER // ssize_t definition in libmpg123.h.in should never have existed at all. // It got removed from libmpg23.h.in after 1.28.0 and before 1.28.1. typedef ptrdiff_t mpg123_ssize_t; #else typedef ssize_t mpg123_ssize_t; #endif class ComponentMPG123 : public ComponentBuiltin { MPT_DECLARE_COMPONENT_MEMBERS(ComponentMPG123, "") public: static mpg123_ssize_t FileReaderRead(void *fp, void *buf, mpg123_size_t count) { FileReader &file = *static_cast(fp); std::size_t readBytes = std::min(count, static_cast(file.BytesLeft())); file.ReadRaw(mpt::span(mpt::void_cast(buf), readBytes)); return readBytes; } static mpg123_off_t FileReaderLSeek(void *fp, mpg123_off_t offset, int whence) { FileReader &file = *static_cast(fp); FileReader::off_t oldpos = file.GetPosition(); if(whence == SEEK_CUR) file.Seek(file.GetPosition() + offset); else if(whence == SEEK_END) file.Seek(file.GetLength() + offset); else file.Seek(offset); MPT_MAYBE_CONSTANT_IF(!mpt::in_range(file.GetPosition())) { file.Seek(oldpos); return static_cast(-1); } return static_cast(file.GetPosition()); } public: ComponentMPG123() : ComponentBuiltin() { return; } bool DoInitialize() override { if(mpg123_init() != 0) { return false; } return true; } virtual ~ComponentMPG123() { if(IsAvailable()) { mpg123_exit(); } } }; static mpt::ustring ReadMPG123String(const mpg123_string &str) { mpt::ustring result; if(!str.p) { return result; } if(str.fill < 1) { return result; } result = mpt::ToUnicode(mpt::Charset::UTF8, std::string(str.p, str.p + str.fill - 1)); return result; } static mpt::ustring ReadMPG123String(const mpg123_string *str) { mpt::ustring result; if(!str) { return result; } result = ReadMPG123String(*str); return result; } template static mpt::ustring ReadMPG123String(const char (&str)[N]) { return mpt::ToUnicode(mpt::Charset::ISO8859_1, mpt::String::ReadBuf(mpt::String::spacePadded, str)); } #endif // MPT_WITH_MPG123 bool CSoundFile::ReadMP3Sample(SAMPLEINDEX sample, FileReader &file, bool raw, bool mo3Decode) { #if defined(MPT_WITH_MPG123) || defined(MPT_WITH_MINIMP3) // Check file for validity, or else mpg123 will happily munch many files that start looking vaguely resemble an MPEG stream mid-file. file.Rewind(); while(file.CanRead(4)) { uint8 magic[3]; file.ReadArray(magic); if(!memcmp(magic, "ID3", 3)) { // Skip ID3 tags uint8 header[7]; file.ReadArray(header); uint32 size = 0; for(int i = 3; i < 7; i++) { if(header[i] & 0x80) return false; size = (size << 7) | header[i]; } file.Skip(size); } else if(!memcmp(magic, "APE", 3) && file.ReadMagic("TAGEX")) { // Skip APE tags uint32 size = file.ReadUint32LE(); file.Skip(16 + size); } else if(!memcmp(magic, "\x00\x00\x00", 3) || !memcmp(magic, "\xFF\x00\x00", 3)) { // Some MP3 files are padded with zeroes... } else if(magic[0] == 0) { // This might be some padding, followed by an MPEG header, so try again. file.SkipBack(2); } else if(MPEGFrame::IsMPEGHeader(magic)) { // This is what we want! break; } else { // This, on the other hand, isn't. return false; } } #endif // MPT_WITH_MPG123 || MPT_WITH_MINIMP3 #if defined(MPT_WITH_MPG123) ComponentHandle mpg123; if(!IsComponentAvailable(mpg123)) { return false; } struct MPG123Handle { mpg123_handle *mh; MPG123Handle() : mh(mpg123_new(0, nullptr)) { } ~MPG123Handle() { mpg123_delete(mh); } operator mpg123_handle *() { return mh; } }; bool hasLameXingVbriHeader = false; if(!raw) { mpg123_off_t length_raw = 0; mpg123_off_t length_hdr = 0; // libmpg123 provides no way to determine whether it parsed ID3V2 or VBR tags. // Thus, we use a pre-scan with those disabled and compare the resulting length. // We ignore ID3V2 stream length here, althrough we parse the ID3V2 header. // libmpg123 only accounts for the VBR info frame if gapless &&!ignore_infoframe, // thus we switch both of those for comparison. { MPG123Handle mh; if(!mh) { return false; } file.Rewind(); if(mpg123_param(mh, MPG123_ADD_FLAGS, MPG123_QUIET, 0.0)) { return false; } if(mpg123_param(mh, MPG123_ADD_FLAGS, MPG123_AUTO_RESAMPLE, 0.0)) { return false; } if(mpg123_param(mh, MPG123_REMOVE_FLAGS, MPG123_GAPLESS, 0.0)) { return false; } if(mpg123_param(mh, MPG123_ADD_FLAGS, MPG123_IGNORE_INFOFRAME, 0.0)) { return false; } if(mpg123_param(mh, MPG123_REMOVE_FLAGS, MPG123_SKIP_ID3V2, 0.0)) { return false; } if(mpg123_param(mh, MPG123_ADD_FLAGS, MPG123_IGNORE_STREAMLENGTH, 0.0)) { return false; } if(mpg123_param(mh, MPG123_INDEX_SIZE, -1000, 0.0)) // auto-grow { return false; } if(mpg123_replace_reader_handle(mh, ComponentMPG123::FileReaderRead, ComponentMPG123::FileReaderLSeek, 0)) { return false; } if(mpg123_open_handle(mh, &file)) { return false; } if(mpg123_scan(mh)) { return false; } long rate = 0; int channels = 0; int encoding = 0; if(mpg123_getformat(mh, &rate, &channels, &encoding)) { return false; } if((channels != 1 && channels != 2) || (encoding & (MPG123_ENC_16 | MPG123_ENC_SIGNED)) != (MPG123_ENC_16 | MPG123_ENC_SIGNED)) { return false; } mpg123_frameinfo frameinfo; MemsetZero(frameinfo); if(mpg123_info(mh, &frameinfo)) { return false; } if(frameinfo.layer < 1 || frameinfo.layer > 3) { return false; } if(mpg123_param(mh, MPG123_FORCE_RATE, rate, 0.0)) { return false; } if(mpg123_param(mh, MPG123_ADD_FLAGS, (channels > 1) ? MPG123_FORCE_STEREO : MPG123_FORCE_MONO, 0.0)) { return false; } length_raw = mpg123_length(mh); } { MPG123Handle mh; if(!mh) { return false; } file.Rewind(); if(mpg123_param(mh, MPG123_ADD_FLAGS, MPG123_QUIET, 0.0)) { return false; } if(mpg123_param(mh, MPG123_ADD_FLAGS, MPG123_AUTO_RESAMPLE, 0.0)) { return false; } if(mpg123_param(mh, MPG123_ADD_FLAGS, MPG123_GAPLESS, 0.0)) { return false; } if(mpg123_param(mh, MPG123_REMOVE_FLAGS, MPG123_IGNORE_INFOFRAME, 0.0)) { return false; } if(mpg123_param(mh, MPG123_REMOVE_FLAGS, MPG123_SKIP_ID3V2, 0.0)) { return false; } if(mpg123_param(mh, MPG123_ADD_FLAGS, MPG123_IGNORE_STREAMLENGTH, 0.0)) { return false; } if(mpg123_param(mh, MPG123_INDEX_SIZE, -1000, 0.0)) // auto-grow { return false; } if(mpg123_replace_reader_handle(mh, ComponentMPG123::FileReaderRead, ComponentMPG123::FileReaderLSeek, 0)) { return false; } if(mpg123_open_handle(mh, &file)) { return false; } if(mpg123_scan(mh)) { return false; } long rate = 0; int channels = 0; int encoding = 0; if(mpg123_getformat(mh, &rate, &channels, &encoding)) { return false; } if((channels != 1 && channels != 2) || (encoding & (MPG123_ENC_16 | MPG123_ENC_SIGNED)) != (MPG123_ENC_16 | MPG123_ENC_SIGNED)) { return false; } mpg123_frameinfo frameinfo; MemsetZero(frameinfo); if(mpg123_info(mh, &frameinfo)) { return false; } if(frameinfo.layer < 1 || frameinfo.layer > 3) { return false; } if(mpg123_param(mh, MPG123_FORCE_RATE, rate, 0.0)) { return false; } if(mpg123_param(mh, MPG123_ADD_FLAGS, (channels > 1) ? MPG123_FORCE_STEREO : MPG123_FORCE_MONO, 0.0)) { return false; } length_hdr = mpg123_length(mh); } hasLameXingVbriHeader = (length_raw != length_hdr); } // Set up decoder... MPG123Handle mh; if(!mh) { return false; } file.Rewind(); if(mpg123_param(mh, MPG123_ADD_FLAGS, MPG123_QUIET, 0.0)) { return false; } if(mpg123_param(mh, MPG123_ADD_FLAGS, MPG123_AUTO_RESAMPLE, 0.0)) { return false; } if(mpg123_param(mh, raw ? MPG123_REMOVE_FLAGS : MPG123_ADD_FLAGS, MPG123_GAPLESS, 0.0)) { return false; } if(mpg123_param(mh, raw ? MPG123_ADD_FLAGS : MPG123_REMOVE_FLAGS, MPG123_IGNORE_INFOFRAME, 0.0)) { return false; } if(mpg123_param(mh, MPG123_REMOVE_FLAGS, MPG123_SKIP_ID3V2, 0.0)) { return false; } if(mpg123_param(mh, raw ? MPG123_ADD_FLAGS : MPG123_REMOVE_FLAGS, MPG123_IGNORE_STREAMLENGTH, 0.0)) { return false; } if(mpg123_param(mh, MPG123_INDEX_SIZE, -1000, 0.0)) // auto-grow { return false; } if(mpg123_replace_reader_handle(mh, ComponentMPG123::FileReaderRead, ComponentMPG123::FileReaderLSeek, 0)) { return false; } if(mpg123_open_handle(mh, &file)) { return false; } if(mpg123_scan(mh)) { return false; } long rate = 0; int channels = 0; int encoding = 0; if(mpg123_getformat(mh, &rate, &channels, &encoding)) { return false; } if((channels != 1 && channels != 2) || (encoding & (MPG123_ENC_16 | MPG123_ENC_SIGNED)) != (MPG123_ENC_16 | MPG123_ENC_SIGNED)) { return false; } mpg123_frameinfo frameinfo; MemsetZero(frameinfo); if(mpg123_info(mh, &frameinfo)) { return false; } if(frameinfo.layer < 1 || frameinfo.layer > 3) { return false; } // We force samplerate, channels and sampleformat, which in // combination with auto-resample (set above) will cause libmpg123 // to stay with the given format even for completely confused // MPG123_FRANKENSTEIN streams. // Note that we cannot rely on mpg123_length() for the way we // decode the mpeg streams because it depends on the actual frame // sample rate instead of the returned sample rate. if(mpg123_param(mh, MPG123_FORCE_RATE, rate, 0.0)) { return false; } if(mpg123_param(mh, MPG123_ADD_FLAGS, (channels > 1) ? MPG123_FORCE_STEREO : MPG123_FORCE_MONO, 0.0)) { return false; } std::vector data; // decoder delay std::size_t data_skip_frames = 0; if(!raw && !hasLameXingVbriHeader) { if(frameinfo.layer == 1) { data_skip_frames = 240 + 1; } else if(frameinfo.layer == 2) { data_skip_frames = 240 + 1; } else if(frameinfo.layer == 3) { data_skip_frames = 528 + 1; } } std::vector buf_bytes; std::vector buf_samples; bool decode_error = false; bool decode_done = false; while(!decode_error && !decode_done) { buf_bytes.resize(mpg123_outblock(mh)); buf_samples.resize(buf_bytes.size() / sizeof(int16)); mpg123_size_t buf_bytes_decoded = 0; int mpg123_read_result = mpg123_read(mh, mpt::byte_cast(buf_bytes.data()), buf_bytes.size(), &buf_bytes_decoded); std::memcpy(buf_samples.data(), buf_bytes.data(), buf_bytes_decoded); mpt::append(data, buf_samples.data(), buf_samples.data() + buf_bytes_decoded / sizeof(int16)); if((data.size() / channels) > MAX_SAMPLE_LENGTH) { break; } if(mpg123_read_result == MPG123_OK) { // continue } else if(mpg123_read_result == MPG123_NEW_FORMAT) { // continue } else if(mpg123_read_result == MPG123_DONE) { decode_done = true; } else { decode_error = true; } } if((data.size() / channels) > MAX_SAMPLE_LENGTH) { return false; } FileTags tags; mpg123_id3v1 *id3v1 = nullptr; mpg123_id3v2 *id3v2 = nullptr; if(mpg123_id3(mh, &id3v1, &id3v2) == MPG123_OK) { if(id3v2) { if(tags.title.empty()) tags.title = ReadMPG123String(id3v2->title); if(tags.artist.empty()) tags.artist = ReadMPG123String(id3v2->artist); if(tags.album.empty()) tags.album = ReadMPG123String(id3v2->album); if(tags.year.empty()) tags.year = ReadMPG123String(id3v2->year); if(tags.genre.empty()) tags.genre = ReadMPG123String(id3v2->genre); if(tags.comments.empty()) tags.comments = ReadMPG123String(id3v2->comment); } if(id3v1) { if(tags.title.empty()) tags.title = ReadMPG123String(id3v1->title); if(tags.artist.empty()) tags.artist = ReadMPG123String(id3v1->artist); if(tags.album.empty()) tags.album = ReadMPG123String(id3v1->album); if(tags.year.empty()) tags.year = ReadMPG123String(id3v1->year); if(tags.comments.empty()) tags.comments = ReadMPG123String(id3v1->comment); } } mpt::ustring sampleName = GetSampleNameFromTags(tags); DestroySampleThreadsafe(sample); if(!mo3Decode) { m_szNames[sample] = mpt::ToCharset(GetCharsetInternal(), sampleName); Samples[sample].Initialize(); Samples[sample].nC5Speed = rate; } Samples[sample].nLength = mpt::saturate_cast((data.size() / channels) - data_skip_frames); Samples[sample].uFlags.set(CHN_16BIT); Samples[sample].uFlags.set(CHN_STEREO, channels == 2); Samples[sample].AllocateSample(); if(Samples[sample].HasSampleData()) { std::memcpy(Samples[sample].sampleb(), data.data() + (data_skip_frames * channels), (data.size() - (data_skip_frames * channels)) * sizeof(int16)); } if(!mo3Decode) { Samples[sample].Convert(MOD_TYPE_IT, GetType()); Samples[sample].PrecomputeLoops(*this, false); } return Samples[sample].HasSampleData(); #elif defined(MPT_WITH_MINIMP3) MPT_UNREFERENCED_PARAMETER(raw); file.Rewind(); FileReader::PinnedView rawDataView = file.GetPinnedView(); int64 bytes_left = rawDataView.size(); const uint8 *stream_pos = mpt::byte_cast(rawDataView.data()); std::vector raw_sample_data; mp3dec_t mp3; std::memset(&mp3, 0, sizeof(mp3dec_t)); mp3dec_init(&mp3); int rate = 0; int channels = 0; mp3dec_frame_info_t info; std::memset(&info, 0, sizeof(mp3dec_frame_info_t)); do { int16 sample_buf[MINIMP3_MAX_SAMPLES_PER_FRAME]; int frame_samples = mp3dec_decode_frame(&mp3, stream_pos, mpt::saturate_cast(bytes_left), sample_buf, &info); if(frame_samples < 0 || info.frame_bytes < 0) break; // internal error in minimp3 if(frame_samples > 0 && info.frame_bytes == 0) break; // internal error in minimp3 if(frame_samples == 0 && info.frame_bytes == 0) break; // end of stream, no progress if(frame_samples == 0 && info.frame_bytes > 0) do { } while(0); // decoder skipped non-mp3 data if(frame_samples > 0 && info.frame_bytes > 0) do { } while(0); // normal if(info.frame_bytes > 0) { if(rate != 0 && rate != info.hz) break; // inconsistent stream if(channels != 0 && channels != info.channels) break; // inconsistent stream rate = info.hz; channels = info.channels; if(rate <= 0) break; // broken stream if(channels != 1 && channels != 2) break; // broken stream stream_pos += std::clamp(info.frame_bytes, 0, mpt::saturate_cast(bytes_left)); bytes_left -= std::clamp(info.frame_bytes, 0, mpt::saturate_cast(bytes_left)); if(frame_samples > 0) { try { mpt::append(raw_sample_data, sample_buf, sample_buf + frame_samples * channels); } catch(mpt::out_of_memory e) { mpt::delete_out_of_memory(e); break; } } } if((raw_sample_data.size() / channels) > MAX_SAMPLE_LENGTH) { break; } } while(bytes_left > 0); if(rate == 0 || channels == 0 || raw_sample_data.empty()) { return false; } if((raw_sample_data.size() / channels) > MAX_SAMPLE_LENGTH) { return false; } DestroySampleThreadsafe(sample); if(!mo3Decode) { m_szNames[sample] = ""; Samples[sample].Initialize(); Samples[sample].nC5Speed = rate; } Samples[sample].nLength = mpt::saturate_cast(raw_sample_data.size() / channels); Samples[sample].uFlags.set(CHN_16BIT); Samples[sample].uFlags.set(CHN_STEREO, channels == 2); Samples[sample].AllocateSample(); if(Samples[sample].HasSampleData()) { std::copy(raw_sample_data.begin(), raw_sample_data.end(), Samples[sample].sample16()); } if(!mo3Decode) { Samples[sample].Convert(MOD_TYPE_IT, GetType()); Samples[sample].PrecomputeLoops(*this, false); } return Samples[sample].HasSampleData(); #else MPT_UNREFERENCED_PARAMETER(sample); MPT_UNREFERENCED_PARAMETER(file); MPT_UNREFERENCED_PARAMETER(raw); MPT_UNREFERENCED_PARAMETER(mo3Decode); #endif // MPT_WITH_MPG123 || MPT_WITH_MINIMP3 return false; } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/SampleFormatOpus.cpp0000644000175000017500000001203014072320411022026 00000000000000/* * SampleFormatOpus.cpp * -------------------- * Purpose: Opus sample import. * Notes : * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Sndfile.h" #ifndef MODPLUG_NO_FILESAVE #include "../common/mptFileIO.h" #endif #include "../common/misc_util.h" #include "Tagging.h" #include "Loaders.h" #include "../common/FileReader.h" #include "modsmp_ctrl.h" #include "openmpt/soundbase/Copy.hpp" #include "../soundlib/ModSampleCopy.h" //#include "mpt/crc/crc.hpp" #include "OggStream.h" #ifdef MPT_WITH_OGG #if MPT_COMPILER_CLANG #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wreserved-id-macro" #endif // MPT_COMPILER_CLANG #include #if MPT_COMPILER_CLANG #pragma clang diagnostic pop #endif // MPT_COMPILER_CLANG #endif // MPT_WITH_OGG #if defined(MPT_WITH_OPUSFILE) #include #endif // MPT_WITH_OPUSFILE OPENMPT_NAMESPACE_BEGIN //////////////////////////////////////////////////////////////////////////////// // Opus #if defined(MPT_WITH_OPUSFILE) static mpt::ustring UStringFromOpus(const char *str) { return str ? mpt::ToUnicode(mpt::Charset::UTF8, str) : mpt::ustring(); } static FileTags GetOpusFileTags(OggOpusFile *of) { FileTags tags; const OpusTags *ot = op_tags(of, -1); if(!ot) { return tags; } tags.encoder = UStringFromOpus(opus_tags_query(ot, "ENCODER", 0)); tags.title = UStringFromOpus(opus_tags_query(ot, "TITLE", 0)); tags.comments = UStringFromOpus(opus_tags_query(ot, "DESCRIPTION", 0)); tags.bpm = UStringFromOpus(opus_tags_query(ot, "BPM", 0)); // non-standard tags.artist = UStringFromOpus(opus_tags_query(ot, "ARTIST", 0)); tags.album = UStringFromOpus(opus_tags_query(ot, "ALBUM", 0)); tags.trackno = UStringFromOpus(opus_tags_query(ot, "TRACKNUMBER", 0)); tags.year = UStringFromOpus(opus_tags_query(ot, "DATE", 0)); tags.url = UStringFromOpus(opus_tags_query(ot, "CONTACT", 0)); tags.genre = UStringFromOpus(opus_tags_query(ot, "GENRE", 0)); return tags; } #endif // MPT_WITH_OPUSFILE bool CSoundFile::ReadOpusSample(SAMPLEINDEX sample, FileReader &file) { file.Rewind(); #if defined(MPT_WITH_OPUSFILE) int rate = 0; int channels = 0; std::vector raw_sample_data; std::string sampleName; FileReader initial = file.GetChunk(65536); // 512 is recommended by libopusfile if(op_test(NULL, initial.GetRawData().data(), initial.GetLength()) != 0) { return false; } OggOpusFile *of = op_open_memory(file.GetRawData().data(), file.GetLength(), NULL); if(!of) { return false; } rate = 48000; channels = op_channel_count(of, -1); if(rate <= 0 || channels <= 0) { op_free(of); of = NULL; return false; } if(channels > 2 || op_link_count(of) != 1) { // We downmix multichannel to stereo as recommended by Opus specification in // case we are not able to handle > 2 channels. // We also decode chained files as stereo even if they start with a mono // stream, which simplifies handling of link boundaries for us. channels = 2; } sampleName = mpt::ToCharset(GetCharsetInternal(), GetSampleNameFromTags(GetOpusFileTags(of))); if(auto length = op_pcm_total(of, 0); length != OP_EINVAL) raw_sample_data.reserve(std::min(MAX_SAMPLE_LENGTH, mpt::saturate_cast(length)) * channels); std::vector decodeBuf(120 * 48000 / 1000); // 120ms (max Opus packet), 48kHz bool eof = false; while(!eof) { int framesRead = 0; if(channels == 2) { framesRead = op_read_stereo(of, &(decodeBuf[0]), static_cast(decodeBuf.size())); } else if(channels == 1) { framesRead = op_read(of, &(decodeBuf[0]), static_cast(decodeBuf.size()), NULL); } if(framesRead > 0) { mpt::append(raw_sample_data, decodeBuf.begin(), decodeBuf.begin() + (framesRead * channels)); } else if(framesRead == 0) { eof = true; } else if(framesRead == OP_HOLE) { // continue } else { // other errors are fatal, stop decoding eof = true; } if((raw_sample_data.size() / channels) > MAX_SAMPLE_LENGTH) { break; } } op_free(of); of = NULL; if(raw_sample_data.empty()) { return false; } DestroySampleThreadsafe(sample); ModSample &mptSample = Samples[sample]; mptSample.Initialize(); mptSample.nC5Speed = rate; mptSample.nLength = std::min(MAX_SAMPLE_LENGTH, mpt::saturate_cast(raw_sample_data.size() / channels)); mptSample.uFlags.set(CHN_16BIT); mptSample.uFlags.set(CHN_STEREO, channels == 2); if(!mptSample.AllocateSample()) { return false; } if(raw_sample_data.size() / channels > MAX_SAMPLE_LENGTH) { AddToLog(LogWarning, U_("Sample has been truncated!")); } std::copy(raw_sample_data.begin(), raw_sample_data.begin() + mptSample.nLength * channels, mptSample.sample16()); mptSample.Convert(MOD_TYPE_IT, GetType()); mptSample.PrecomputeLoops(*this, false); m_szNames[sample] = sampleName; return true; #else // !MPT_WITH_OPUSFILE MPT_UNREFERENCED_PARAMETER(sample); MPT_UNREFERENCED_PARAMETER(file); return false; #endif // MPT_WITH_OPUSFILE } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/SampleFormatSFZ.cpp0000644000175000017500000012026714145002062021556 00000000000000/* * SampleFormatSFZ.cpp * ------------------- * Purpose: Loading and saving SFZ instruments. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Sndfile.h" #ifdef MODPLUG_TRACKER #include "../mptrack/TrackerSettings.h" #endif // MODPLUG_TRACKER #ifndef MODPLUG_NO_FILESAVE #include "../common/mptFileIO.h" #endif // !MODPLUG_NO_FILESAVE #include "modsmp_ctrl.h" #include "mpt/base/numbers.hpp" #include OPENMPT_NAMESPACE_BEGIN #ifdef MPT_EXTERNAL_SAMPLES template static bool SFZStartsWith(const std::string_view &l, const char(&r)[N]) { return l.substr(0, N - 1) == r; } template static bool SFZEndsWith(const std::string_view &l, const char (&r)[N]) { return l.size() >= (N - 1) && l.substr(l.size() - (N - 1), N - 1) == r; } static bool SFZIsNumeric(const std::string_view &str) { return std::find_if(str.begin(), str.end(), [](char c) { return c < '0' || c > '9'; }) == str.end(); } struct SFZControl { std::string defaultPath; int8 octaveOffset = 0, noteOffset = 0; void Parse(const std::string_view key, const std::string &value) { if(key == "default_path") defaultPath = value; else if(key == "octave_offset") octaveOffset = ConvertStrTo(value); else if(key == "note_offset") noteOffset = ConvertStrTo(value); } }; struct SFZFlexEG { using PointIndex = decltype(InstrumentEnvelope().nLoopStart); std::vector> points; double amplitude = 0; // percentage (100 = full volume range) double pan = 0; // percentage (100 = full pan range) double pitch = 0; // in cents double cutoff = 0; // in cents PointIndex sustain = 0; void Parse(std::string_view key, const std::string &value) { key = key.substr(key.find('_') + 1); const double v = ConvertStrTo(value); const bool isTime = SFZStartsWith(key, "time"), isLevel = SFZStartsWith(key, "level"); std::string_view pointStr; if(isTime) pointStr = key.substr(4); else if(isLevel) pointStr = key.substr(5); if(!pointStr.empty() && SFZIsNumeric(pointStr)) { PointIndex point = ConvertStrTo(std::string(pointStr)); if(point >= points.size() && point < MAX_ENVPOINTS) points.resize(point + 1); if(point < points.size()) { if(isTime) points[point].first = v; else points[point].second = v; } return; } if(key == "points") points.resize(std::min(static_cast(v), static_cast(MAX_ENVPOINTS))); else if(key == "sustain") sustain = mpt::saturate_round(v); else if(key == "amplitude" || key == "ampeg") amplitude = v; else if(key == "pan") pan = v; else if(key == "pitch") pitch = v; else if(key == "cutoff") cutoff = v; } void ConvertToMPT(ModInstrument *ins, const CSoundFile &sndFile) const { if(amplitude) ConvertToMPT(ins, sndFile, ENV_VOLUME, amplitude / 100.0, 0.0, 1.0); if(pan) ConvertToMPT(ins, sndFile, ENV_PANNING, pan / 100.0, -1.0, 1.0); if(pitch) ConvertToMPT(ins, sndFile, ENV_PITCH, pitch / 1600.0, -1.0, 1.0); if(cutoff) ConvertToMPT(ins, sndFile, ENV_PITCH, cutoff, 0.0, 1.0, true); } void ConvertToMPT(ModInstrument *ins, const CSoundFile &sndFile, EnvelopeType envType, double scale, double minVal, double maxVal, bool forceFilter = false) const { const double tickDuration = sndFile.m_PlayState.m_nSamplesPerTick / static_cast(sndFile.GetSampleRate()); if(tickDuration <= 0 || points.empty() || scale == 0.0) return; auto &env = ins->GetEnvelope(envType); std::function conversionFunc = Identity; if(forceFilter && envType == ENV_PITCH) { env.dwFlags.set(ENV_FILTER); conversionFunc = FilterConversionFunc(*ins, sndFile); } env.clear(); env.reserve(points.size()); const auto ToValue = std::bind(SFZFlexEG::ToValue, std::placeholders::_1, scale, minVal, maxVal, conversionFunc); int32 prevTick = -1; // If the first envelope point's time is greater than 0, we fade in from a neutral value if(points.front().first > 0) { env.push_back({0, ToValue(0.0)}); prevTick = 0; } for(const auto &point : points) { const auto tick = mpt::saturate_cast(prevTick + ToTicks(point.first, tickDuration)); const auto value = ToValue(point.second); env.push_back({tick, value}); prevTick = tick; if(tick == Util::MaxValueOfType(tick)) break; } if(sustain < env.size()) { env.nSustainStart = env.nSustainEnd = sustain; env.dwFlags.set(ENV_SUSTAIN); } else { env.dwFlags.reset(ENV_SUSTAIN); } env.dwFlags.set(ENV_ENABLED); if(envType == ENV_VOLUME && env.nSustainEnd > 0) env.nReleaseNode = env.nSustainEnd; } protected: static EnvelopeNode::tick_t ToTicks(double duration, double tickDuration) { return std::max(EnvelopeNode::tick_t(1), mpt::saturate_round(duration / tickDuration)); } static EnvelopeNode::value_t ToValue(double value, double scale, double minVal, double maxVal, const std::function &conversionFunc) { value = conversionFunc((value * scale - minVal) / (maxVal - minVal)) * ENVELOPE_MAX + ENVELOPE_MIN; Limit(value, ENVELOPE_MIN, ENVELOPE_MAX); return mpt::saturate_round(value); } static double Identity(double v) noexcept { return v; } static double CentsToFilterCutoff(double v, const CSoundFile &sndFile, int envBaseCutoff, uint32 envBaseFreq) { const auto freq = envBaseFreq * std::pow(2.0, v / 1200.0); return Util::muldivr(sndFile.FrequencyToCutOff(freq), 127, envBaseCutoff) / 127.0; } static std::function FilterConversionFunc(const ModInstrument &ins, const CSoundFile &sndFile) { const auto envBaseCutoff = ins.IsCutoffEnabled() ? ins.GetCutoff() : 127; const auto envBaseFreq = sndFile.CutOffToFrequency(envBaseCutoff); return std::bind(CentsToFilterCutoff, std::placeholders::_1, std::cref(sndFile), envBaseCutoff, envBaseFreq); } }; struct SFZEnvelope { double startLevel = 0, delay = 0, attack = 0, hold = 0; double decay = 0, sustainLevel = 100, release = 0, depth = 0; void Parse(std::string_view key, const std::string &value) { key = key.substr(key.find('_') + 1); double v = ConvertStrTo(value); if(key == "depth") Limit(v, -12000.0, 12000.0); else if(key == "start" || key == "sustain") Limit(v, -100.0, 100.0); else Limit(v, 0.0, 100.0); if(key == "start") startLevel = v; else if(key == "delay") delay = v; else if(key == "attack") attack = v; else if(key == "hold") hold = v; else if(key == "decay") decay = v; else if(key == "sustain") sustainLevel = v; else if(key == "release") release = v; else if(key == "depth") depth = v; } void ConvertToMPT(ModInstrument *ins, const CSoundFile &sndFile, EnvelopeType envType, bool forceFilter = false) const { SFZFlexEG eg; if(envType == ENV_VOLUME) eg.amplitude = 1.0; else if(envType == ENV_PITCH && !forceFilter) eg.pitch = depth / 100.0; else if(envType == ENV_PITCH && forceFilter) eg.cutoff = depth / 100.0; auto &env = eg.points; if(attack > 0 || delay > 0) { env.push_back({0.0, startLevel}); if(delay > 0) env.push_back({delay, env.back().second}); env.push_back({attack, 100.0}); } if(hold > 0) { if(env.empty()) env.push_back({0.0, 100.0}); env.push_back({hold, env.back().second}); } if(env.empty()) env.push_back({0.0, 100.0}); if(env.back().second != sustainLevel) env.push_back({decay, sustainLevel}); if(sustainLevel != 0) { eg.sustain = static_cast(env.size() - 1); env.push_back({release, 0.0}); } else { eg.sustain = std::numeric_limits::max(); } eg.ConvertToMPT(ins, sndFile); } }; struct SFZRegion { enum class LoopMode { kUnspecified, kContinuous, kOneShot, kSustain, kNoLoop }; enum class LoopType { kUnspecified, kForward, kBackward, kAlternate, }; size_t filenameOffset = 0; std::string filename, name; SFZEnvelope ampEnv, pitchEnv, filterEnv; std::vector flexEGs; SmpLength loopStart = 0, loopEnd = 0; SmpLength end = MAX_SAMPLE_LENGTH, offset = 0; LoopMode loopMode = LoopMode::kUnspecified; LoopType loopType = LoopType::kUnspecified; double loopCrossfade = 0.0; double cutoff = 0; // in Hz double resonance = 0; // 0...40dB double filterRandom = 0; // 0...9600 cents double volume = 0; // -144dB...+6dB double amplitude = 100.0; // 0...100 double pitchBend = 200; // -9600...9600 cents double pitchLfoFade = 0; // 0...100 seconds double pitchLfoDepth = 0; // -1200...12000 double pitchLfoFreq = 0; // 0...20 Hz double panning = -128; // -100...+100 double finetune = 0; // in cents int8 transpose = 0; uint8 keyLo = 0, keyHi = 127, keyRoot = 60; FilterMode filterType = FilterMode::Unchanged; uint8 polyphony = 255; bool useSampleKeyRoot = false; bool invertPhase = false; template static void Read(const std::string &valueStr, T &value, Tc valueMin = std::numeric_limits::min(), Tc valueMax = std::numeric_limits::max()) { double valueF = ConvertStrTo(valueStr); if constexpr(std::numeric_limits::is_integer) { valueF = mpt::round(valueF); } Limit(valueF, static_cast(valueMin), static_cast(valueMax)); value = static_cast(valueF); } static uint8 ReadKey(const std::string &value, const SFZControl &control) { if(value.empty()) return 0; int key = 0; if(value[0] >= '0' && value[0] <= '9') { // MIDI key key = ConvertStrTo(value); } else if(value.length() < 2) { return 0; } else { // Scientific pitch static constexpr int8 keys[] = { 9, 11, 0, 2, 4, 5, 7 }; static_assert(std::size(keys) == 'g' - 'a' + 1); auto keyC = value[0]; if(keyC >= 'A' && keyC <= 'G') key = keys[keyC - 'A']; if(keyC >= 'a' && keyC <= 'g') key = keys[keyC - 'a']; else return 0; uint8 octaveOffset = 1; if(value[1] == '#') { key++; octaveOffset = 2; } else if(value[1] == 'b' || value[1] == 'B') { key--; octaveOffset = 2; } if(octaveOffset >= value.length()) return 0; int8 octave = ConvertStrTo(value.c_str() + octaveOffset); key += (octave + 1) * 12; } key += control.octaveOffset * 12 + control.noteOffset; return static_cast(Clamp(key, 0, 127)); } void Parse(const std::string_view key, const std::string &value, const SFZControl &control) { if(key == "sample") { filename = control.defaultPath + value; filenameOffset = control.defaultPath.size(); } else if(key == "region_label") name = value; else if(key == "lokey") keyLo = ReadKey(value, control); else if(key == "hikey") keyHi = ReadKey(value, control); else if(key == "pitch_keycenter") { keyRoot = ReadKey(value, control); useSampleKeyRoot = (value == "sample"); } else if(key == "key") { keyLo = keyHi = keyRoot = ReadKey(value, control); useSampleKeyRoot = false; } else if(key == "bend_up" || key == "bendup") Read(value, pitchBend, -9600.0, 9600.0); else if(key == "pitchlfo_fade") Read(value, pitchLfoFade, 0.0, 100.0); else if(key == "pitchlfo_depth") Read(value, pitchLfoDepth, -12000.0, 12000.0); else if(key == "pitchlfo_freq") Read(value, pitchLfoFreq, 0.0, 20.0); else if(key == "volume") Read(value, volume, -144.0, 6.0); else if(key == "amplitude") Read(value, amplitude, 0.0, 100.0); else if(key == "pan") Read(value, panning, -100.0, 100.0); else if(key == "transpose") Read(value, transpose, -127, 127); else if(key == "tune") Read(value, finetune, -100.0, 100.0); else if(key == "end") Read(value, end, SmpLength(0), MAX_SAMPLE_LENGTH); else if(key == "offset") Read(value, offset, SmpLength(0), MAX_SAMPLE_LENGTH); else if(key == "loop_start" || key == "loopstart") Read(value, loopStart, SmpLength(0), MAX_SAMPLE_LENGTH); else if(key == "loop_end" || key == "loopend") Read(value, loopEnd, SmpLength(0), MAX_SAMPLE_LENGTH); else if(key == "loop_crossfade" || key == "loopcrossfade") Read(value, loopCrossfade, 0.0, DBL_MAX); else if(key == "loop_mode" || key == "loopmode") { if(value == "loop_continuous") loopMode = LoopMode::kContinuous; else if(value == "one_shot") loopMode = LoopMode::kOneShot; else if(value == "loop_sustain") loopMode = LoopMode::kSustain; else if(value == "no_loop") loopMode = LoopMode::kNoLoop; } else if(key == "loop_type" || key == "looptype") { if(value == "forward") loopType = LoopType::kForward; else if(value == "backward") loopType = LoopType::kBackward; else if(value == "alternate") loopType = LoopType::kAlternate; } else if(key == "cutoff") Read(value, cutoff, 0.0, 96000.0); else if(key == "fil_random") Read(value, filterRandom, 0.0, 9600.0); else if(key == "resonance") Read(value, resonance, 0.0, 40.0); else if(key == "polyphony") Read(value, polyphony, 0, 255); else if(key == "phase") invertPhase = (value == "invert"); else if(key == "fil_type" || key == "filtype") { if(value == "lpf_1p" || value == "lpf_2p" || value == "lpf_4p" || value == "lpf_6p") filterType = FilterMode::LowPass; else if(value == "hpf_1p" || value == "hpf_2p" || value == "hpf_4p" || value == "hpf_6p") filterType = FilterMode::HighPass; // Alternatives: bpf_2p, brf_2p } else if(SFZStartsWith(key, "ampeg_")) ampEnv.Parse(key, value); else if(SFZStartsWith(key, "fileg_")) filterEnv.Parse(key, value); else if(SFZStartsWith(key, "pitcheg_")) pitchEnv.Parse(key, value); else if(SFZStartsWith(key, "eg") && SFZIsNumeric(key.substr(2, 2)) && key.substr(4, 1) == "_") { uint8 eg = ConvertStrTo(std::string(key.substr(2, 2))); if(eg >= flexEGs.size()) flexEGs.resize(eg + 1); flexEGs[eg].Parse(key, value); } } }; struct SFZInputFile { FileReader file; std::unique_ptr inputFile; // FileReader has pointers into this so its address must not change std::string remain; SFZInputFile(FileReader f = {}, std::unique_ptr i = {}, std::string r = {}) : file{std::move(f)}, inputFile{std::move(i)}, remain{std::move(r)} {} SFZInputFile(SFZInputFile &&) = default; }; bool CSoundFile::ReadSFZInstrument(INSTRUMENTINDEX nInstr, FileReader &file) { file.Rewind(); enum { kNone, kGlobal, kMaster, kGroup, kRegion, kControl, kCurve, kEffect, kUnknown } section = kNone; bool inMultiLineComment = false; SFZControl control; SFZRegion group, master, globals; std::vector regions; std::map macros; std::vector files; files.emplace_back(file); std::string s; while(!files.empty()) { if(!files.back().file.ReadLine(s, 1024)) { // Finished reading file, so back to remaining characters of the #include line from the previous file s = std::move(files.back().remain); files.pop_back(); } if(inMultiLineComment) { if(auto commentEnd = s.find("*/"); commentEnd != std::string::npos) { s.erase(0, commentEnd + 2); inMultiLineComment = false; } else { continue; } } // First, terminate line at the start of a comment block if(auto commentPos = s.find("//"); commentPos != std::string::npos) { s.resize(commentPos); } // Now, read the tokens. // This format is so funky that no general tokenizer approach seems to work here... // Consider this jolly good example found at https://stackoverflow.com/questions/5923895/tokenizing-a-custom-text-file-format-file-using-c-sharp // sample=piano C3.wav key=48 ampeg_release=0.7 // a comment here // key = 49 sample = piano Db3.wav // // group=1 // key = 48 // sample = piano D3.ogg // The original sfz specification claims that spaces around = are not allowed, but a quick look into the real world tells us otherwise. while(!s.empty()) { s.erase(0, s.find_first_not_of(" \t")); const bool isDefine = SFZStartsWith(s, "#define ") || SFZStartsWith(s, "#define\t"); // Replace macros (unless this is a #define statement, to allow for macro re-definition) if(!isDefine) { for(const auto &[oldStr, newStr] : macros) { std::string::size_type pos = 0; while((pos = s.find(oldStr, pos)) != std::string::npos) { s.replace(pos, oldStr.length(), newStr); pos += newStr.length(); } } } if(s.empty()) break; std::string::size_type charsRead = 0; if(s[0] == '<' && (charsRead = s.find('>')) != std::string::npos) { // Section header const auto sec = std::string_view(s).substr(1, charsRead - 1); section = kUnknown; if(sec == "global") { section = kGlobal; // Reset global parameters globals = SFZRegion(); } else if(sec == "master") { section = kMaster; // Reset master parameters master = globals; } else if(sec == "group") { section = kGroup; // Reset group parameters group = master; } else if(sec == "region") { section = kRegion; regions.push_back(group); } else if(sec == "control") { section = kControl; } else if(sec == "curve") { section = kCurve; } else if(sec == "effect") { section = kEffect; } charsRead++; } else if(isDefine) { // Macro definition charsRead += 8; auto keyStart = s.find_first_not_of(" \t", 8); auto keyEnd = s.find_first_of(" \t", keyStart); auto valueStart = s.find_first_not_of(" \t", keyEnd); if(keyStart != std::string::npos && valueStart != std::string::npos) { charsRead = s.find_first_of(" \t", valueStart); const auto key = s.substr(keyStart, keyEnd - keyStart); if(key.length() > 1 && key[0] == '$') macros[std::move(key)] = s.substr(valueStart, charsRead - valueStart); } else { break; } } else if(SFZStartsWith(s, "#include ") || SFZStartsWith(s, "#include\t")) { // Include other sfz file auto fileStart = s.find("\"", 9); // Yes, there can be arbitrary characters before the opening quote, at least that's how sforzando does it. auto fileEnd = s.find("\"", fileStart + 1); if(fileStart != std::string::npos && fileEnd != std::string::npos) { charsRead = fileEnd + 1; fileStart++; } else { break; } std::string filenameU8 = s.substr(fileStart, fileEnd - fileStart); mpt::PathString filename = mpt::PathString::FromUTF8(filenameU8); if(!filename.empty()) { if(filenameU8.find(':') == std::string::npos) filename = file.GetOptionalFileName().value_or(P_("")).GetPath() + filename; filename = filename.Simplify(); // Avoid recursive #include if(std::find_if(files.begin(), files.end(), [&filename](const SFZInputFile &f) { return f.file.GetOptionalFileName().value_or(P_("")) == filename; }) == files.end()) { auto f = std::make_unique(filename); if(f->IsValid()) { s.erase(0, charsRead); files.emplace_back(GetFileReader(*f), std::move(f), std::move(s)); break; } else { AddToLog(LogWarning, U_("Unable to load include file: ") + filename.ToUnicode()); } } else { AddToLog(LogWarning, U_("Recursive include file ignored: ") + filename.ToUnicode()); } } } else if(SFZStartsWith(s, "/*")) { // Multi-line comment if(auto commentEnd = s.find("*/", charsRead + 2); commentEnd != std::string::npos) { charsRead = commentEnd; } else { inMultiLineComment = true; charsRead = s.length(); } } else if(section == kNone) { // Garbage before any section, probably not an sfz file return false; } else if(s.find('=') != std::string::npos) { // Read key=value pair auto keyEnd = s.find_first_of(" \t="); auto valueStart = s.find_first_not_of(" \t=", keyEnd); if(valueStart == std::string::npos) { break; } const std::string key = mpt::ToLowerCaseAscii(s.substr(0, keyEnd)); // Currently defined *_label opcodes are global_label, group_label, master_label, region_label, sw_label if(key == "sample" || key == "default_path" || SFZStartsWith(key, "label_cc") || SFZStartsWith(key, "label_key") || SFZEndsWith(key, "_label")) { // Sample / CC name may contain spaces... charsRead = s.find_first_of("=\t<", valueStart); if(charsRead != std::string::npos && s[charsRead] == '=') { // Backtrack to end of key while(charsRead > valueStart && s[charsRead] == ' ') charsRead--; // Backtrack to start of key while(charsRead > valueStart && s[charsRead] != ' ') charsRead--; } } else { charsRead = s.find_first_of(" \t<", valueStart); } const std::string value = s.substr(valueStart, charsRead - valueStart); switch(section) { case kGlobal: globals.Parse(key, value, control); [[fallthrough]]; case kMaster: master.Parse(key, value, control); [[fallthrough]]; case kGroup: group.Parse(key, value, control); break; case kRegion: regions.back().Parse(key, value, control); break; case kControl: control.Parse(key, value); break; } } else { // Garbage, probably not an sfz file return false; } // Remove the token(s) we just read s.erase(0, charsRead); } } if(regions.empty()) return false; ModInstrument *pIns = new (std::nothrow) ModInstrument(); if(pIns == nullptr) return false; RecalculateSamplesPerTick(); DestroyInstrument(nInstr, deleteAssociatedSamples); if(nInstr > m_nInstruments) m_nInstruments = nInstr; Instruments[nInstr] = pIns; SAMPLEINDEX prevSmp = 0; for(auto ®ion : regions) { uint8 keyLo = region.keyLo, keyHi = region.keyHi; if(keyLo > keyHi) continue; Clamp(keyLo, 0, NOTE_MAX - NOTE_MIN); Clamp(keyHi, 0, NOTE_MAX - NOTE_MIN); SAMPLEINDEX smp = GetNextFreeSample(nInstr, prevSmp + 1); if(smp == SAMPLEINDEX_INVALID) break; prevSmp = smp; ModSample &sample = Samples[smp]; sample.Initialize(MOD_TYPE_MPT); if(const auto synthSample = std::string_view(region.filename).substr(region.filenameOffset); SFZStartsWith(synthSample, "*")) { sample.nLength = 256; sample.nC5Speed = mpt::saturate_round(sample.nLength * 261.6255653); sample.uFlags.set(CHN_16BIT); std::function generator; if(synthSample == "*sine") generator = [](int32 i) { return mpt::saturate_round(std::sin(i * ((2.0 * mpt::numbers::pi) / 256.0)) * int16_max); }; else if(synthSample == "*square") generator = [](int32 i) { return i < 128 ? int16_max : int16_min; }; else if(synthSample == "*triangle" || synthSample == "*tri") generator = [](int32 i) { return static_cast(i < 128 ? ((63 - i) * 512) : ((i - 192) * 512)); }; else if(synthSample == "*saw") generator = [](int32 i) { return static_cast((i - 128) * 256); }; else if(synthSample == "*silence") generator = [](int32) { return int16(0); }; else if(synthSample == "*noise") { sample.nLength = sample.nC5Speed; generator = [this](int32) { return mpt::random(AccessPRNG()); }; } else { AddToLog(LogWarning, U_("Unknown sample type: ") + mpt::ToUnicode(mpt::Charset::UTF8, std::string(synthSample))); prevSmp--; continue; } if(sample.AllocateSample()) { for(SmpLength i = 0; i < sample.nLength; i++) { sample.sample16()[i] = generator(static_cast(i)); } if(smp > m_nSamples) m_nSamples = smp; region.offset = 0; region.loopMode = SFZRegion::LoopMode::kContinuous; region.loopStart = 0; region.loopEnd = sample.nLength - 1; region.loopCrossfade = 0; region.keyRoot = 60; } } else if(auto filename = mpt::PathString::FromUTF8(region.filename); !filename.empty()) { if(region.filename.find(':') == std::string::npos) { filename = file.GetOptionalFileName().value_or(P_("")).GetPath() + filename; } filename = filename.Simplify(); SetSamplePath(smp, filename); InputFile f(filename, SettingCacheCompleteFileBeforeLoading()); FileReader smpFile = GetFileReader(f); if(!ReadSampleFromFile(smp, smpFile, false)) { AddToLog(LogWarning, U_("Unable to load sample: ") + filename.ToUnicode()); prevSmp--; continue; } if(UseFinetuneAndTranspose()) sample.TransposeToFrequency(); sample.uFlags.set(SMP_KEEPONDISK, sample.HasSampleData()); } if(!region.name.empty()) m_szNames[smp] = mpt::ToCharset(GetCharsetInternal(), mpt::Charset::UTF8, region.name); if(!m_szNames[smp][0]) m_szNames[smp] = mpt::ToCharset(GetCharsetInternal(), mpt::PathString::FromUTF8(region.filename).GetFileName().ToUnicode()); if(region.useSampleKeyRoot) { if(sample.rootNote != NOTE_NONE) region.keyRoot = sample.rootNote - NOTE_MIN; else region.keyRoot = 60; } const auto origSampleRate = sample.GetSampleRate(GetType()); int8 transp = region.transpose + (60 - region.keyRoot); for(uint8 i = keyLo; i <= keyHi; i++) { pIns->Keyboard[i] = smp; if(GetType() != MOD_TYPE_XM) pIns->NoteMap[i] = NOTE_MIN + i + transp; } if(GetType() == MOD_TYPE_XM) sample.Transpose(transp / 12.0); pIns->filterMode = region.filterType; if(region.cutoff != 0) pIns->SetCutoff(FrequencyToCutOff(region.cutoff), true); if(region.resonance != 0) pIns->SetResonance(mpt::saturate_round(region.resonance * 128.0 / 24.0), true); pIns->nCutSwing = mpt::saturate_round(region.filterRandom * (m_SongFlags[SONG_EXFILTERRANGE] ? 20 : 24) / 1200.0); pIns->midiPWD = mpt::saturate_round(region.pitchBend / 100.0); pIns->nNNA = NewNoteAction::NoteOff; if(region.polyphony == 1) { pIns->nDNA = DuplicateNoteAction::NoteCut; pIns->nDCT = DuplicateCheckType::Sample; } region.ampEnv.ConvertToMPT(pIns, *this, ENV_VOLUME); if(region.pitchEnv.depth) region.pitchEnv.ConvertToMPT(pIns, *this, ENV_PITCH); else if(region.filterEnv.depth) region.filterEnv.ConvertToMPT(pIns, *this, ENV_PITCH, true); for(const auto &flexEG : region.flexEGs) { flexEG.ConvertToMPT(pIns, *this); } if(region.ampEnv.release > 0) { const double tickDuration = m_PlayState.m_nSamplesPerTick / static_cast(GetSampleRate()); pIns->nFadeOut = std::min(mpt::saturate_cast(32768.0 * tickDuration / region.ampEnv.release), uint32(32767)); if(GetType() == MOD_TYPE_IT) pIns->nFadeOut = std::min((pIns->nFadeOut + 16u) & ~31u, uint32(8192)); } sample.rootNote = region.keyRoot + NOTE_MIN; sample.nGlobalVol = mpt::saturate_round(64.0 * Clamp(std::pow(10.0, region.volume / 20.0) * region.amplitude / 100.0, 0.0, 1.0)); if(region.panning != -128) { sample.nPan = mpt::saturate_round((region.panning + 100) * 256.0 / 200.0); sample.uFlags.set(CHN_PANNING); } sample.Transpose(region.finetune / 1200.0); if(region.pitchLfoDepth && region.pitchLfoFreq) { sample.nVibSweep = 255; if(region.pitchLfoFade > 0) sample.nVibSweep = mpt::saturate_round(255.0 / region.pitchLfoFade); sample.nVibDepth = mpt::saturate_round(region.pitchLfoDepth * 32.0 / 100.0); sample.nVibRate = mpt::saturate_round(region.pitchLfoFreq * 4.0); } if(region.loopMode != SFZRegion::LoopMode::kUnspecified) { switch(region.loopMode) { case SFZRegion::LoopMode::kContinuous: case SFZRegion::LoopMode::kOneShot: sample.uFlags.set(CHN_LOOP); break; case SFZRegion::LoopMode::kSustain: sample.uFlags.set(CHN_SUSTAINLOOP); break; case SFZRegion::LoopMode::kNoLoop: sample.uFlags.reset(CHN_LOOP | CHN_SUSTAINLOOP); } } if(region.loopEnd > region.loopStart) { // Loop may also be defined in file, in which case loopStart and loopEnd are unset. if(region.loopMode == SFZRegion::LoopMode::kSustain) { sample.nSustainStart = region.loopStart; sample.nSustainEnd = region.loopEnd + 1; } else if(region.loopMode == SFZRegion::LoopMode::kContinuous || region.loopMode == SFZRegion::LoopMode::kOneShot) { sample.nLoopStart = region.loopStart; sample.nLoopEnd = region.loopEnd + 1; } } else if(sample.nLoopEnd <= sample.nLoopStart && region.loopMode != SFZRegion::LoopMode::kUnspecified && region.loopMode != SFZRegion::LoopMode::kNoLoop) { sample.nLoopEnd = sample.nLength; } switch(region.loopType) { case SFZRegion::LoopType::kUnspecified: break; case SFZRegion::LoopType::kForward: sample.uFlags.reset(CHN_PINGPONGLOOP | CHN_PINGPONGSUSTAIN | CHN_REVERSE); break; case SFZRegion::LoopType::kBackward: sample.uFlags.set(CHN_REVERSE); break; case SFZRegion::LoopType::kAlternate: sample.uFlags.set(CHN_PINGPONGLOOP | CHN_PINGPONGSUSTAIN); break; default: break; } if(sample.nSustainEnd <= sample.nSustainStart && sample.nLoopEnd > sample.nLoopStart && region.loopMode == SFZRegion::LoopMode::kSustain) { // Turn normal loop (imported from sample) into sustain loop std::swap(sample.nSustainStart, sample.nLoopStart); std::swap(sample.nSustainEnd, sample.nLoopEnd); sample.uFlags.set(CHN_SUSTAINLOOP); sample.uFlags.set(CHN_PINGPONGSUSTAIN, sample.uFlags[CHN_PINGPONGLOOP]); sample.uFlags.reset(CHN_LOOP | CHN_PINGPONGLOOP); } mpt::PathString filenameModifier; // Loop cross-fade SmpLength fadeSamples = mpt::saturate_round(region.loopCrossfade * origSampleRate); LimitMax(fadeSamples, sample.uFlags[CHN_SUSTAINLOOP] ? sample.nSustainStart : sample.nLoopStart); if(fadeSamples > 0) { ctrlSmp::XFadeSample(sample, fadeSamples, 50000, true, sample.uFlags[CHN_SUSTAINLOOP], *this); sample.uFlags.set(SMP_MODIFIED); filenameModifier += P_(" (cross-fade)"); } // Sample offset if(region.offset && region.offset < sample.nLength) { auto offset = region.offset * sample.GetBytesPerSample(); memmove(sample.sampleb(), sample.sampleb() + offset, sample.nLength * sample.GetBytesPerSample() - offset); if(region.end > region.offset) region.end -= region.offset; sample.nLength -= region.offset; sample.nLoopStart -= region.offset; sample.nLoopEnd -= region.offset; sample.uFlags.set(SMP_MODIFIED); filenameModifier += P_(" (offset)"); } LimitMax(sample.nLength, region.end); if(region.invertPhase) { ctrlSmp::InvertSample(sample, 0, sample.nLength, *this); sample.uFlags.set(SMP_MODIFIED); filenameModifier += P_(" (inverted)"); } if(sample.uFlags.test_all(SMP_KEEPONDISK | SMP_MODIFIED)) { // Avoid ruining the original samples if(auto filename = GetSamplePath(smp); !filename.empty()) { filename = filename.GetPath() + filename.GetFileName() + filenameModifier + filename.GetFileExt(); SetSamplePath(smp, filename); } } sample.PrecomputeLoops(*this, false); sample.Convert(MOD_TYPE_MPT, GetType()); } pIns->Sanitize(MOD_TYPE_MPT); pIns->Convert(MOD_TYPE_MPT, GetType()); return true; } #ifndef MODPLUG_NO_FILESAVE static double SFZLinear2dB(double volume) { return (volume > 0.0 ? 20.0 * std::log10(volume) : -144.0); } static void WriteSFZEnvelope(std::ostream &f, double tickDuration, int index, const InstrumentEnvelope &env, const char *type, double scale, std::function convFunc) { if(!env.dwFlags[ENV_ENABLED] || env.empty()) return; const bool sustainAtEnd = (!env.dwFlags[ENV_SUSTAIN] || env.nSustainStart == (env.size() - 1)) && convFunc(env.back().value) != 0.0; const auto prefix = MPT_AFORMAT("\neg{}_")(mpt::afmt::dec0<2>(index)); f << "\n" << prefix << type << "=" << scale; f << prefix << "points=" << (env.size() + (sustainAtEnd ? 1 : 0)); EnvelopeNode::tick_t lastTick = 0; int nodeIndex = 0; for(const auto &node : env) { const double time = (node.tick - lastTick) * tickDuration; lastTick = node.tick; f << prefix << "time" << nodeIndex << "=" << time; f << prefix << "level" << nodeIndex << "=" << convFunc(node.value); nodeIndex++; } if(sustainAtEnd) { // Prevent envelope from going back to neutral f << prefix << "time" << nodeIndex << "=0"; f << prefix << "level" << nodeIndex << "=" << convFunc(env.back().value); } // We always must write a sustain point, or the envelope will be sustained on the first point of the envelope f << prefix << "sustain=" << (env.dwFlags[ENV_SUSTAIN] ? env.nSustainStart : (env.size() - 1)); if(env.dwFlags[ENV_LOOP]) f << "\n// Loop: " << static_cast(env.nLoopStart) << "-" << static_cast(env.nLoopEnd); if(env.dwFlags[ENV_SUSTAIN] && env.nSustainEnd > env.nSustainStart) f << "\n// Sustain Loop: " << static_cast(env.nSustainStart) << "-" << static_cast(env.nSustainEnd); if(env.nReleaseNode != ENV_RELEASE_NODE_UNSET) f << "\n// Release Node: " << static_cast(env.nReleaseNode); } bool CSoundFile::SaveSFZInstrument(INSTRUMENTINDEX nInstr, std::ostream &f, const mpt::PathString &filename, bool useFLACsamples) const { #ifdef MODPLUG_TRACKER const mpt::FlushMode flushMode = mpt::FlushModeFromBool(TrackerSettings::Instance().MiscFlushFileBuffersOnSave); #else const mpt::FlushMode flushMode = mpt::FlushMode::Full; #endif const ModInstrument *ins = Instruments[nInstr]; if(ins == nullptr) return false; // Creating directory names with trailing spaces or dots is a bad idea, as they are difficult to remove in Windows. const mpt::RawPathString whitespaceDirName = PL_(" \n\r\t."); const mpt::PathString sampleBaseName = mpt::PathString::FromNative(mpt::trim(filename.GetFileName().AsNative(), whitespaceDirName)); const mpt::PathString sampleDirName = (sampleBaseName.empty() ? P_("Samples") : sampleBaseName) + P_("/"); const mpt::PathString sampleBasePath = filename.GetPath() + sampleDirName; if(!sampleBasePath.IsDirectory() && !::CreateDirectory(sampleBasePath.AsNative().c_str(), nullptr)) return false; const double tickDuration = m_PlayState.m_nSamplesPerTick / static_cast(m_MixerSettings.gdwMixingFreq); f << std::setprecision(10); if(!ins->name.empty()) { f << "// Name: " << mpt::ToCharset(mpt::Charset::UTF8, GetCharsetInternal(), ins->name) << "\n"; } f << "// Created with " << mpt::ToCharset(mpt::Charset::UTF8, Version::Current().GetOpenMPTVersionString()) << "\n"; f << "// Envelope tempo base: tempo " << m_PlayState.m_nMusicTempo.ToDouble(); switch(m_nTempoMode) { case TempoMode::Classic: f << " (classic tempo mode)"; break; case TempoMode::Alternative: f << " (alternative tempo mode)"; break; case TempoMode::Modern: f << ", " << m_PlayState.m_nMusicSpeed << " ticks per row, " << m_PlayState.m_nCurrentRowsPerBeat << " rows per beat (modern tempo mode)"; break; default: MPT_ASSERT_NOTREACHED(); break; } f << "\n\n\ndefault_path=" << sampleDirName.ToUTF8() << "\n\n"; f << ""; f << "\nbend_up=" << ins->midiPWD * 100; f << "\nbend_down=" << -ins->midiPWD * 100; const uint32 cutoff = ins->IsCutoffEnabled() ? ins->GetCutoff() : 127; // If filter envelope is active but cutoff is not set, we still need to set the base cutoff frequency to be modulated by the envelope. if(ins->IsCutoffEnabled() || ins->PitchEnv.dwFlags[ENV_FILTER]) f << "\ncutoff=" << CSoundFile::CutOffToFrequency(cutoff) << " // " << cutoff; if(ins->IsResonanceEnabled()) f << "\nresonance=" << Util::muldivr_unsigned(ins->GetResonance(), 24, 128) << " // " << static_cast(ins->GetResonance()); if(ins->IsCutoffEnabled() || ins->IsResonanceEnabled()) f << "\nfil_type=" << (ins->filterMode == FilterMode::HighPass ? "hpf_2p" : "lpf_2p"); if(ins->dwFlags[INS_SETPANNING]) f << "\npan=" << (Util::muldivr_unsigned(ins->nPan, 200, 256) - 100) << " // " << ins->nPan; if(ins->nGlobalVol != 64) f << "\nvolume=" << SFZLinear2dB(ins->nGlobalVol / 64.0) << " // " << ins->nGlobalVol; if(ins->nFadeOut) { f << "\nampeg_release=" << (32768.0 * tickDuration / ins->nFadeOut) << " // " << ins->nFadeOut; f << "\nampeg_release_shape=0"; } if(ins->nDNA == DuplicateNoteAction::NoteCut && ins->nDCT != DuplicateCheckType::None) f << "\npolyphony=1"; WriteSFZEnvelope(f, tickDuration, 1, ins->VolEnv, "amplitude", 100.0, [](int32 val) { return val / static_cast(ENVELOPE_MAX); }); WriteSFZEnvelope(f, tickDuration, 2, ins->PanEnv, "pan", 100.0, [](int32 val) { return 2.0 * (val - ENVELOPE_MID) / (ENVELOPE_MAX - ENVELOPE_MIN); }); if(ins->PitchEnv.dwFlags[ENV_FILTER]) { const auto envScale = 1200.0 * std::log(CutOffToFrequency(127, 256) / static_cast(CutOffToFrequency(0, -256))) / mpt::numbers::ln2; const auto cutoffNormal = CutOffToFrequency(cutoff); WriteSFZEnvelope(f, tickDuration, 3, ins->PitchEnv, "cutoff", envScale, [this, cutoff, cutoffNormal, envScale](int32 val) { // Convert interval between center frequency and envelope into cents const auto freq = CutOffToFrequency(cutoff, (val - ENVELOPE_MID) * 256 / (ENVELOPE_MAX - ENVELOPE_MID)); return 1200.0 * std::log(freq / static_cast(cutoffNormal)) / mpt::numbers::ln2 / envScale; }); } else { WriteSFZEnvelope(f, tickDuration, 3, ins->PitchEnv, "pitch", 1600.0, [](int32 val) { return 2.0 * (val - ENVELOPE_MID) / (ENVELOPE_MAX - ENVELOPE_MIN); }); } size_t numSamples = 0; for(size_t i = 0; i < std::size(ins->Keyboard); i++) { if(ins->Keyboard[i] < 1 || ins->Keyboard[i] > GetNumSamples()) continue; size_t endOfRegion = i + 1; while(endOfRegion < std::size(ins->Keyboard)) { if(ins->Keyboard[endOfRegion] != ins->Keyboard[i] || ins->NoteMap[endOfRegion] != (ins->NoteMap[i] + endOfRegion - i)) break; endOfRegion++; } endOfRegion--; const ModSample &sample = Samples[ins->Keyboard[i]]; const bool isAdlib = sample.uFlags[CHN_ADLIB]; if(!sample.HasSampleData()) { i = endOfRegion; continue; } numSamples++; mpt::PathString sampleName = sampleBasePath + (sampleBaseName.empty() ? P_("Sample") : sampleBaseName) + P_(" ") + mpt::PathString::FromUnicode(mpt::ufmt::val(numSamples)); if(isAdlib) sampleName += P_(".s3i"); else if(useFLACsamples) sampleName += P_(".flac"); else sampleName += P_(".wav"); bool success = false; try { mpt::SafeOutputFile sfSmp(sampleName, std::ios::binary, flushMode); if(sfSmp) { mpt::ofstream &fSmp = sfSmp; fSmp.exceptions(fSmp.exceptions() | std::ios::badbit | std::ios::failbit); if(isAdlib) success = SaveS3ISample(ins->Keyboard[i], fSmp); else if(useFLACsamples) success = SaveFLACSample(ins->Keyboard[i], fSmp); else success = SaveWAVSample(ins->Keyboard[i], fSmp); } } catch(const std::exception &) { success = false; } if(!success) { AddToLog(LogError, MPT_USTRING("Unable to save sample: ") + sampleName.ToUnicode()); } f << "\n\n"; if(!m_szNames[ins->Keyboard[i]].empty()) { f << "\nregion_label=" << mpt::ToCharset(mpt::Charset::UTF8, GetCharsetInternal(), m_szNames[ins->Keyboard[i]]); } f << "\nsample=" << sampleName.GetFullFileName().ToUTF8(); f << "\nlokey=" << i; f << "\nhikey=" << endOfRegion; if(sample.rootNote != NOTE_NONE) f << "\npitch_keycenter=" << sample.rootNote - NOTE_MIN; else f << "\npitch_keycenter=" << NOTE_MIDDLEC + i - ins->NoteMap[i]; if(sample.uFlags[CHN_PANNING]) f << "\npan=" << (Util::muldivr_unsigned(sample.nPan, 200, 256) - 100) << " // " << sample.nPan; if(sample.nGlobalVol != 64) f << "\nvolume=" << SFZLinear2dB((ins->nGlobalVol * sample.nGlobalVol) / 4096.0) << " // " << sample.nGlobalVol; const char *loopMode = "no_loop", *loopType = "forward"; SmpLength loopStart = 0, loopEnd = 0; if(sample.uFlags[CHN_SUSTAINLOOP]) { loopMode = "loop_sustain"; loopStart = sample.nSustainStart; loopEnd = sample.nSustainEnd; if(sample.uFlags[CHN_PINGPONGSUSTAIN]) loopType = "alternate"; } else if(sample.uFlags[CHN_LOOP]) { loopMode = "loop_continuous"; loopStart = sample.nLoopStart; loopEnd = sample.nLoopEnd; if(sample.uFlags[CHN_PINGPONGLOOP]) loopType = "alternate"; else if(sample.uFlags[CHN_REVERSE]) loopType = "backward"; } f << "\nloop_mode=" << loopMode; if(loopStart < loopEnd) { f << "\nloop_start=" << loopStart; f << "\nloop_end=" << (loopEnd - 1); f << "\nloop_type=" << loopType; } if(sample.uFlags.test_all(CHN_SUSTAINLOOP | CHN_LOOP)) { f << "\n// Warning: Only sustain loop was exported!"; } i = endOfRegion; } return true; } #endif // MODPLUG_NO_FILESAVE #else bool CSoundFile::ReadSFZInstrument(INSTRUMENTINDEX, FileReader &) { return false; } #endif // MPT_EXTERNAL_SAMPLES OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/SampleFormatVorbis.cpp0000644000175000017500000002317514072320411022360 00000000000000/* * SampleFormatVorbis.cpp * ---------------------- * Purpose: Vorbis sample import * Notes : * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Sndfile.h" #ifndef MODPLUG_NO_FILESAVE #include "../common/mptFileIO.h" #endif #include "../common/misc_util.h" #include "Tagging.h" #include "Loaders.h" #include "../common/FileReader.h" #include "modsmp_ctrl.h" #include "openmpt/soundbase/Copy.hpp" #include "mpt/audio/span.hpp" #include "../soundlib/ModSampleCopy.h" //#include "mpt/crc/crc.hpp" #include "OggStream.h" #ifdef MPT_WITH_OGG #if MPT_COMPILER_CLANG #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wreserved-id-macro" #endif // MPT_COMPILER_CLANG #include #if MPT_COMPILER_CLANG #pragma clang diagnostic pop #endif // MPT_COMPILER_CLANG #endif // MPT_WITH_OGG #if defined(MPT_WITH_VORBIS) #if MPT_COMPILER_CLANG #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wreserved-id-macro" #endif // MPT_COMPILER_CLANG #include #if MPT_COMPILER_CLANG #pragma clang diagnostic pop #endif // MPT_COMPILER_CLANG #endif // MPT_WITH_VORBIS #if defined(MPT_WITH_VORBISFILE) #if MPT_COMPILER_CLANG #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wreserved-id-macro" #endif // MPT_COMPILER_CLANG #include #if MPT_COMPILER_CLANG #pragma clang diagnostic pop #endif // MPT_COMPILER_CLANG #endif // MPT_WITH_VORBISFILE #ifdef MPT_WITH_STBVORBIS #include #endif // MPT_WITH_STBVORBIS OPENMPT_NAMESPACE_BEGIN //////////////////////////////////////////////////////////////////////////////// // Vorbis #if defined(MPT_WITH_VORBISFILE) static size_t VorbisfileFilereaderRead(void *ptr, size_t size, size_t nmemb, void *datasource) { FileReader &file = *static_cast(datasource); return file.ReadRaw(mpt::span(mpt::void_cast(ptr), size * nmemb)).size() / size; } static int VorbisfileFilereaderSeek(void *datasource, ogg_int64_t offset, int whence) { FileReader &file = *static_cast(datasource); switch(whence) { case SEEK_SET: { if(!mpt::in_range(offset)) { return -1; } return file.Seek(mpt::saturate_cast(offset)) ? 0 : -1; } break; case SEEK_CUR: { if(offset < 0) { if(offset == std::numeric_limits::min()) { return -1; } if(!mpt::in_range(0-offset)) { return -1; } return file.SkipBack(mpt::saturate_cast(0 - offset)) ? 0 : -1; } else { if(!mpt::in_range(offset)) { return -1; } return file.Skip(mpt::saturate_cast(offset)) ? 0 : -1; } } break; case SEEK_END: { if(!mpt::in_range(offset)) { return -1; } if(!mpt::in_range(file.GetLength() + offset)) { return -1; } return file.Seek(mpt::saturate_cast(file.GetLength() + offset)) ? 0 : -1; } break; default: return -1; } } static long VorbisfileFilereaderTell(void *datasource) { FileReader &file = *static_cast(datasource); MPT_MAYBE_CONSTANT_IF(!mpt::in_range(file.GetPosition())) { return -1; } return static_cast(file.GetPosition()); } #if defined(MPT_WITH_VORBIS) static mpt::ustring UStringFromVorbis(const char *str) { return str ? mpt::ToUnicode(mpt::Charset::UTF8, str) : mpt::ustring(); } #endif // MPT_WITH_VORBIS static FileTags GetVorbisFileTags(OggVorbis_File &vf) { FileTags tags; #if defined(MPT_WITH_VORBIS) vorbis_comment *vc = ov_comment(&vf, -1); if(!vc) { return tags; } tags.encoder = UStringFromVorbis(vorbis_comment_query(vc, "ENCODER", 0)); tags.title = UStringFromVorbis(vorbis_comment_query(vc, "TITLE", 0)); tags.comments = UStringFromVorbis(vorbis_comment_query(vc, "DESCRIPTION", 0)); tags.bpm = UStringFromVorbis(vorbis_comment_query(vc, "BPM", 0)); // non-standard tags.artist = UStringFromVorbis(vorbis_comment_query(vc, "ARTIST", 0)); tags.album = UStringFromVorbis(vorbis_comment_query(vc, "ALBUM", 0)); tags.trackno = UStringFromVorbis(vorbis_comment_query(vc, "TRACKNUMBER", 0)); tags.year = UStringFromVorbis(vorbis_comment_query(vc, "DATE", 0)); tags.url = UStringFromVorbis(vorbis_comment_query(vc, "CONTACT", 0)); tags.genre = UStringFromVorbis(vorbis_comment_query(vc, "GENRE", 0)); #else // !MPT_WITH_VORBIS MPT_UNREFERENCED_PARAMETER(vf); #endif // MPT_WITH_VORBIS return tags; } #endif // MPT_WITH_VORBISFILE bool CSoundFile::ReadVorbisSample(SAMPLEINDEX sample, FileReader &file) { #if defined(MPT_WITH_VORBISFILE) || defined(MPT_WITH_STBVORBIS) file.Rewind(); int rate = 0; int channels = 0; std::vector raw_sample_data; std::string sampleName; #endif // VORBIS #if defined(MPT_WITH_VORBISFILE) bool unsupportedSample = false; ov_callbacks callbacks = { &VorbisfileFilereaderRead, &VorbisfileFilereaderSeek, NULL, &VorbisfileFilereaderTell }; OggVorbis_File vf; MemsetZero(vf); if(ov_open_callbacks(&file, &vf, NULL, 0, callbacks) == 0) { if(ov_streams(&vf) == 1) { // we do not support chained vorbis samples vorbis_info *vi = ov_info(&vf, -1); if(vi && vi->rate > 0 && vi->channels > 0) { sampleName = mpt::ToCharset(GetCharsetInternal(), GetSampleNameFromTags(GetVorbisFileTags(vf))); rate = vi->rate; channels = vi->channels; std::size_t offset = 0; int current_section = 0; long decodedSamples = 0; bool eof = false; if(auto length = ov_pcm_total(&vf, 0); length != OV_EINVAL) raw_sample_data.reserve(std::min(MAX_SAMPLE_LENGTH, mpt::saturate_cast(length)) * std::clamp(channels, 1, 2)); while(!eof) { float **output = nullptr; long ret = ov_read_float(&vf, &output, 1024, ¤t_section); if(ret == 0) { eof = true; } else if(ret < 0) { // stream error, just try to continue } else { decodedSamples = ret; if(decodedSamples > 0 && (channels == 1 || channels == 2)) { raw_sample_data.resize(raw_sample_data.size() + (channels * decodedSamples)); CopyAudio(mpt::audio_span_interleaved(raw_sample_data.data() + (offset * channels), channels, decodedSamples), mpt::audio_span_planar(output, channels, decodedSamples)); offset += decodedSamples; if((raw_sample_data.size() / channels) > MAX_SAMPLE_LENGTH) { break; } } } } } else { unsupportedSample = true; } } else { unsupportedSample = true; } ov_clear(&vf); } else { unsupportedSample = true; } if(unsupportedSample) { return false; } #elif defined(MPT_WITH_STBVORBIS) // NOTE/TODO: stb_vorbis does not handle inferred negative PCM sample position // at stream start. (See // ). This // means that, for remuxed and re-aligned/cutted (at stream start) Vorbis // files, stb_vorbis will include superfluous samples at the beginning. FileReader::PinnedView fileView = file.GetPinnedView(); const std::byte* data = fileView.data(); std::size_t dataLeft = fileView.size(); std::size_t offset = 0; int consumed = 0; int error = 0; stb_vorbis *vorb = stb_vorbis_open_pushdata(mpt::byte_cast(data), mpt::saturate_cast(dataLeft), &consumed, &error, nullptr); file.Skip(consumed); data += consumed; dataLeft -= consumed; if(!vorb) { return false; } rate = stb_vorbis_get_info(vorb).sample_rate; channels = stb_vorbis_get_info(vorb).channels; if(rate <= 0 || channels <= 0) { return false; } while((error == VORBIS__no_error || (error == VORBIS_need_more_data && dataLeft > 0))) { int frame_channels = 0; int decodedSamples = 0; float **output = nullptr; consumed = stb_vorbis_decode_frame_pushdata(vorb, mpt::byte_cast(data), mpt::saturate_cast(dataLeft), &frame_channels, &output, &decodedSamples); file.Skip(consumed); data += consumed; dataLeft -= consumed; LimitMax(frame_channels, channels); if(decodedSamples > 0 && (frame_channels == 1 || frame_channels == 2)) { raw_sample_data.resize(raw_sample_data.size() + (channels * decodedSamples)); CopyAudio(mpt::audio_span_interleaved(raw_sample_data.data() + (offset * channels), channels, decodedSamples), mpt::audio_span_planar(output, channels, decodedSamples)); offset += decodedSamples; if((raw_sample_data.size() / channels) > MAX_SAMPLE_LENGTH) { break; } } error = stb_vorbis_get_error(vorb); } stb_vorbis_close(vorb); #endif // VORBIS #if defined(MPT_WITH_VORBISFILE) || defined(MPT_WITH_STBVORBIS) if(rate <= 0 || channels <= 0 || raw_sample_data.empty()) { return false; } DestroySampleThreadsafe(sample); ModSample &mptSample = Samples[sample]; mptSample.Initialize(); mptSample.nC5Speed = rate; mptSample.nLength = std::min(MAX_SAMPLE_LENGTH, mpt::saturate_cast(raw_sample_data.size() / channels)); mptSample.uFlags.set(CHN_16BIT); mptSample.uFlags.set(CHN_STEREO, channels == 2); if(!mptSample.AllocateSample()) { return false; } if(raw_sample_data.size() / channels > MAX_SAMPLE_LENGTH) { AddToLog(LogWarning, U_("Sample has been truncated!")); } std::copy(raw_sample_data.begin(), raw_sample_data.begin() + mptSample.nLength * channels, mptSample.sample16()); mptSample.Convert(MOD_TYPE_IT, GetType()); mptSample.PrecomputeLoops(*this, false); m_szNames[sample] = sampleName; return true; #else // !VORBIS MPT_UNREFERENCED_PARAMETER(sample); MPT_UNREFERENCED_PARAMETER(file); return false; #endif // VORBIS } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/SampleIO.cpp0000644000175000017500000010621214126162046020254 00000000000000/* * SampleIO.cpp * ------------ * Purpose: Central code for reading and writing samples. Create your SampleIO object and have a go at the ReadSample and WriteSample functions! * Notes : Not all combinations of possible sample format combinations are implemented, especially for WriteSample. * Using the existing generic functions, it should be quite easy to extend the code, though. * Authors: Olivier Lapicque * OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Loaders.h" #include "SampleIO.h" #include "openmpt/soundbase/SampleDecode.hpp" #include "SampleCopy.h" #include "SampleNormalize.h" #include "ModSampleCopy.h" #include "ITCompression.h" #ifndef MODPLUG_NO_FILESAVE #include "../common/mptFileIO.h" #include "mpt/io/base.hpp" #include "mpt/io/io.hpp" #include "mpt/io/io_stdstream.hpp" #include "mpt/io_write/buffer.hpp" #endif #include "BitReader.h" OPENMPT_NAMESPACE_BEGIN // Read a sample from memory size_t SampleIO::ReadSample(ModSample &sample, FileReader &file) const { if(!file.IsValid()) { return 0; } LimitMax(sample.nLength, MAX_SAMPLE_LENGTH); FileReader::off_t bytesRead = 0; // Amount of memory that has been read from file FileReader::off_t filePosition = file.GetPosition(); const std::byte * sourceBuf = nullptr; FileReader::PinnedView restrictedSampleDataView; FileReader::off_t fileSize = 0; if(UsesFileReaderForDecoding()) { sourceBuf = nullptr; fileSize = file.BytesLeft(); } else if(!IsVariableLengthEncoded()) { restrictedSampleDataView = file.GetPinnedView(CalculateEncodedSize(sample.nLength)); sourceBuf = restrictedSampleDataView.data(); fileSize = restrictedSampleDataView.size(); if(sourceBuf == nullptr) return 0; } else { MPT_ASSERT_NOTREACHED(); } if(!IsVariableLengthEncoded() && sample.nLength > 0x40000) { // Limit sample length to available bytes in file to avoid excessive memory allocation. // However, for ProTracker MODs we need to support samples exceeding the end of file // (see the comment about MOD.shorttune2 in Load_mod.cpp), so as a semi-arbitrary threshold, // we do not apply this limit to samples shorter than 256K. size_t maxLength = fileSize - std::min(GetEncodedHeaderSize(), fileSize); uint8 bps = GetEncodedBitsPerSample(); if(bps % 8u != 0) { MPT_ASSERT(GetEncoding() == ADPCM && bps == 4); if(Util::MaxValueOfType(maxLength) / 2u >= maxLength) maxLength *= 2; else maxLength = Util::MaxValueOfType(maxLength); } else { size_t encodedBytesPerSample = GetNumChannels() * GetEncodedBitsPerSample() / 8u; // Check if we can round up without overflowing if(Util::MaxValueOfType(maxLength) - maxLength >= (encodedBytesPerSample - 1u)) maxLength += encodedBytesPerSample - 1u; else maxLength = Util::MaxValueOfType(maxLength); maxLength /= encodedBytesPerSample; } LimitMax(sample.nLength, mpt::saturate_cast(maxLength)); } else if(GetEncoding() == IT214 || GetEncoding() == IT215 || GetEncoding() == MDL || GetEncoding() == DMF) { // In the best case, IT compression represents each sample point as a single bit. // In practice, there is of course the two-byte header per compressed block and the initial bit width change. // As a result, if we have a file length of n, we know that the sample can be at most n*8 sample points long. // For DMF, there are at least two bits per sample, and for MDL at least 5 (so both are worse than IT). size_t maxLength = fileSize; uint8 maxSamplesPerByte = 8 / GetNumChannels(); if(Util::MaxValueOfType(maxLength) / maxSamplesPerByte >= maxLength) maxLength *= maxSamplesPerByte; else maxLength = Util::MaxValueOfType(maxLength); LimitMax(sample.nLength, mpt::saturate_cast(maxLength)); } else if(GetEncoding() == AMS) { if(fileSize <= 9) return 0; file.Skip(4); // Target sample size (we already know this) SmpLength maxLength = std::min(file.ReadUint32LE(), mpt::saturate_cast(fileSize)); file.SkipBack(8); // In the best case, every byte triplet can decode to 255 bytes, which is a ratio of exactly 1:85 if(Util::MaxValueOfType(maxLength) / 85 >= maxLength) maxLength *= 85; else maxLength = Util::MaxValueOfType(maxLength); LimitMax(sample.nLength, maxLength / (m_bitdepth / 8u)); } if(sample.nLength < 1) { return 0; } sample.uFlags.set(CHN_16BIT, GetBitDepth() >= 16); sample.uFlags.set(CHN_STEREO, GetChannelFormat() != mono); size_t sampleSize = sample.AllocateSample(); // Target sample size in bytes if(sampleSize == 0) { sample.nLength = 0; return 0; } MPT_ASSERT(sampleSize >= sample.GetSampleSizeInBytes()); ////////////////////////////////////////////////////// // Compressed samples if(*this == SampleIO(_8bit, mono, littleEndian, ADPCM)) { // 4-Bit ADPCM data int8 compressionTable[16]; // ADPCM Compression LUT if(file.ReadArray(compressionTable)) { size_t readLength = (sample.nLength + 1) / 2; LimitMax(readLength, file.BytesLeft()); const uint8 *inBuf = mpt::byte_cast(sourceBuf) + sizeof(compressionTable); int8 *outBuf = sample.sample8(); int8 delta = 0; for(size_t i = readLength; i != 0; i--) { delta += compressionTable[*inBuf & 0x0F]; *(outBuf++) = delta; delta += compressionTable[(*inBuf >> 4) & 0x0F]; *(outBuf++) = delta; inBuf++; } bytesRead = sizeof(compressionTable) + readLength; } } else if(GetEncoding() == IT214 || GetEncoding() == IT215) { // IT 2.14 / 2.15 compressed samples ITDecompression(file, sample, GetEncoding() == IT215); bytesRead = file.GetPosition() - filePosition; } else if(GetEncoding() == AMS && GetChannelFormat() == mono) { // AMS compressed samples file.Skip(4); // Target sample size (we already know this) uint32 sourceSize = file.ReadUint32LE(); int8 packCharacter = file.ReadUint8(); bytesRead += 9; FileReader::PinnedView packedDataView = file.ReadPinnedView(sourceSize); LimitMax(sourceSize, mpt::saturate_cast(packedDataView.size())); bytesRead += sourceSize; AMSUnpack(reinterpret_cast(packedDataView.data()), packedDataView.size(), sample.samplev(), sample.GetSampleSizeInBytes(), packCharacter); if(sample.uFlags[CHN_16BIT] && !mpt::endian_is_little()) { auto p = sample.sample16(); for(SmpLength length = sample.nLength; length != 0; length--, p++) { *p = mpt::bit_cast(*p); } } } else if(GetEncoding() == PTM8Dto16 && GetChannelFormat() == mono && GetBitDepth() == 16) { // PTM 8-Bit delta to 16-Bit sample bytesRead = CopyMonoSample(sample, sourceBuf, fileSize); } else if(GetEncoding() == MDL && GetChannelFormat() == mono && GetBitDepth() <= 16) { // Huffman MDL compressed samples if(file.CanRead(8) && (fileSize = file.ReadUint32LE()) >= 4) { BitReader chunk = file.ReadChunk(fileSize); bytesRead = chunk.GetLength() + 4; uint8 dlt = 0, lowbyte = 0; const bool is16bit = GetBitDepth() == 16; try { for(SmpLength j = 0; j < sample.nLength; j++) { uint8 hibyte; if(is16bit) { lowbyte = static_cast(chunk.ReadBits(8)); } bool sign = chunk.ReadBits(1) != 0; if(chunk.ReadBits(1)) { hibyte = static_cast(chunk.ReadBits(3)); } else { hibyte = 8; while(!chunk.ReadBits(1)) { hibyte += 0x10; } hibyte += static_cast(chunk.ReadBits(4)); } if(sign) { hibyte = ~hibyte; } dlt += hibyte; if(!is16bit) { sample.sample8()[j] = dlt; } else { sample.sample16()[j] = lowbyte | (dlt << 8); } } } catch(const BitReader::eof &) { // Data is not sufficient to decode the whole sample //AddToLog(LogWarning, "Truncated MDL sample block"); } } } else if(GetEncoding() == DMF && GetChannelFormat() == mono && GetBitDepth() <= 16) { // DMF Huffman compression if(fileSize > 4) { bytesRead = DMFUnpack(file, mpt::byte_cast(sample.sampleb()), sample.GetSampleSizeInBytes()); } } else if((GetEncoding() == uLaw || GetEncoding() == aLaw) && GetBitDepth() == 16 && (GetChannelFormat() == mono || GetChannelFormat() == stereoInterleaved)) { SmpLength readLength = sample.nLength * GetNumChannels(); LimitMax(readLength, mpt::saturate_cast(fileSize)); bytesRead = readLength; const std::byte *inBuf = sourceBuf; int16 *outBuf = sample.sample16(); if(GetEncoding() == uLaw) { SC::DecodeInt16uLaw conv; while(readLength--) { *(outBuf++) = conv(inBuf++); } } else { SC::DecodeInt16ALaw conv; while(readLength--) { *(outBuf++) = conv(inBuf++); } } } ///////////////////////// // Uncompressed samples ////////////////////////////////////////////////////// // 8-Bit / Mono / PCM else if(GetBitDepth() == 8 && GetChannelFormat() == mono) { switch(GetEncoding()) { case signedPCM: // 8-Bit / Mono / Signed / PCM bytesRead = CopyMonoSample(sample, sourceBuf, fileSize); break; case unsignedPCM: // 8-Bit / Mono / Unsigned / PCM bytesRead = CopyMonoSample(sample, sourceBuf, fileSize); break; case deltaPCM: // 8-Bit / Mono / Delta / PCM case MT2: bytesRead = CopyMonoSample(sample, sourceBuf, fileSize); break; default: MPT_ASSERT_NOTREACHED(); break; } } ////////////////////////////////////////////////////// // 8-Bit / Stereo Split / PCM else if(GetBitDepth() == 8 && GetChannelFormat() == stereoSplit) { switch(GetEncoding()) { case signedPCM: // 8-Bit / Stereo Split / Signed / PCM bytesRead = CopyStereoSplitSample(sample, sourceBuf, fileSize); break; case unsignedPCM: // 8-Bit / Stereo Split / Unsigned / PCM bytesRead = CopyStereoSplitSample(sample, sourceBuf, fileSize); break; case deltaPCM: // 8-Bit / Stereo Split / Delta / PCM case MT2: // same as deltaPCM, but right channel is stored as a difference from the left channel bytesRead = CopyStereoSplitSample(sample, sourceBuf, fileSize); if(GetEncoding() == MT2) { for(int8 *p = sample.sample8(), *pEnd = p + sample.nLength * 2; p < pEnd; p += 2) { p[1] = static_cast(static_cast(p[0]) + static_cast(p[1])); } } break; default: MPT_ASSERT_NOTREACHED(); break; } } ////////////////////////////////////////////////////// // 8-Bit / Stereo Interleaved / PCM else if(GetBitDepth() == 8 && GetChannelFormat() == stereoInterleaved) { switch(GetEncoding()) { case signedPCM: // 8-Bit / Stereo Interleaved / Signed / PCM bytesRead = CopyStereoInterleavedSample(sample, sourceBuf, fileSize); break; case unsignedPCM: // 8-Bit / Stereo Interleaved / Unsigned / PCM bytesRead = CopyStereoInterleavedSample(sample, sourceBuf, fileSize); break; case deltaPCM: // 8-Bit / Stereo Interleaved / Delta / PCM bytesRead = CopyStereoInterleavedSample(sample, sourceBuf, fileSize); break; default: MPT_ASSERT_NOTREACHED(); break; } } ////////////////////////////////////////////////////// // 16-Bit / Mono / Little Endian / PCM else if(GetBitDepth() == 16 && GetChannelFormat() == mono && GetEndianness() == littleEndian) { switch(GetEncoding()) { case signedPCM: // 16-Bit / Stereo Interleaved / Signed / PCM bytesRead = CopyMonoSample >(sample, sourceBuf, fileSize); break; case unsignedPCM: // 16-Bit / Stereo Interleaved / Unsigned / PCM bytesRead = CopyMonoSample >(sample, sourceBuf, fileSize); break; case deltaPCM: // 16-Bit / Stereo Interleaved / Delta / PCM case MT2: bytesRead = CopyMonoSample >(sample, sourceBuf, fileSize); break; default: MPT_ASSERT_NOTREACHED(); break; } } ////////////////////////////////////////////////////// // 16-Bit / Mono / Big Endian / PCM else if(GetBitDepth() == 16 && GetChannelFormat() == mono && GetEndianness() == bigEndian) { switch(GetEncoding()) { case signedPCM: // 16-Bit / Mono / Signed / PCM bytesRead = CopyMonoSample >(sample, sourceBuf, fileSize); break; case unsignedPCM: // 16-Bit / Mono / Unsigned / PCM bytesRead = CopyMonoSample >(sample, sourceBuf, fileSize); break; case deltaPCM: // 16-Bit / Mono / Delta / PCM bytesRead = CopyMonoSample >(sample, sourceBuf, fileSize); break; default: MPT_ASSERT_NOTREACHED(); break; } } ////////////////////////////////////////////////////// // 16-Bit / Stereo Split / Little Endian / PCM else if(GetBitDepth() == 16 && GetChannelFormat() == stereoSplit && GetEndianness() == littleEndian) { switch(GetEncoding()) { case signedPCM: // 16-Bit / Stereo Split / Signed / PCM bytesRead = CopyStereoSplitSample >(sample, sourceBuf, fileSize); break; case unsignedPCM: // 16-Bit / Stereo Split / Unsigned / PCM bytesRead = CopyStereoSplitSample >(sample, sourceBuf, fileSize); break; case deltaPCM: // 16-Bit / Stereo Split / Delta / PCM case MT2: // same as deltaPCM, but right channel is stored as a difference from the left channel bytesRead = CopyStereoSplitSample >(sample, sourceBuf, fileSize); if(GetEncoding() == MT2) { for(int16 *p = sample.sample16(), *pEnd = p + sample.nLength * 2; p < pEnd; p += 2) { p[1] = static_cast(static_cast(p[0]) + static_cast(p[1])); } } break; default: MPT_ASSERT_NOTREACHED(); break; } } ////////////////////////////////////////////////////// // 16-Bit / Stereo Split / Big Endian / PCM else if(GetBitDepth() == 16 && GetChannelFormat() == stereoSplit && GetEndianness() == bigEndian) { switch(GetEncoding()) { case signedPCM: // 16-Bit / Stereo Split / Signed / PCM bytesRead = CopyStereoSplitSample >(sample, sourceBuf, fileSize); break; case unsignedPCM: // 16-Bit / Stereo Split / Unsigned / PCM bytesRead = CopyStereoSplitSample >(sample, sourceBuf, fileSize); break; case deltaPCM: // 16-Bit / Stereo Split / Delta / PCM bytesRead = CopyStereoSplitSample >(sample, sourceBuf, fileSize); break; default: MPT_ASSERT_NOTREACHED(); break; } } ////////////////////////////////////////////////////// // 16-Bit / Stereo Interleaved / Little Endian / PCM else if(GetBitDepth() == 16 && GetChannelFormat() == stereoInterleaved && GetEndianness() == littleEndian) { switch(GetEncoding()) { case signedPCM: // 16-Bit / Stereo Interleaved / Signed / PCM bytesRead = CopyStereoInterleavedSample >(sample, sourceBuf, fileSize); break; case unsignedPCM: // 16-Bit / Stereo Interleaved / Unsigned / PCM bytesRead = CopyStereoInterleavedSample >(sample, sourceBuf, fileSize); break; case deltaPCM: // 16-Bit / Stereo Interleaved / Delta / PCM bytesRead = CopyStereoInterleavedSample >(sample, sourceBuf, fileSize); break; default: MPT_ASSERT_NOTREACHED(); break; } } ////////////////////////////////////////////////////// // 16-Bit / Stereo Interleaved / Big Endian / PCM else if(GetBitDepth() == 16 && GetChannelFormat() == stereoInterleaved && GetEndianness() == bigEndian) { switch(GetEncoding()) { case signedPCM: // 16-Bit / Stereo Interleaved / Signed / PCM bytesRead = CopyStereoInterleavedSample >(sample, sourceBuf, fileSize); break; case unsignedPCM: // 16-Bit / Stereo Interleaved / Unsigned / PCM bytesRead = CopyStereoInterleavedSample >(sample, sourceBuf, fileSize); break; case deltaPCM: // 16-Bit / Stereo Interleaved / Delta / PCM bytesRead = CopyStereoInterleavedSample >(sample, sourceBuf, fileSize); break; default: MPT_ASSERT_NOTREACHED(); break; } } ////////////////////////////////////////////////////// // 24-Bit / Signed / Mono / PCM else if(GetBitDepth() == 24 && GetChannelFormat() == mono && GetEncoding() == signedPCM) { if(GetEndianness() == littleEndian) { bytesRead = CopyMonoSample, SC::DecodeInt24<0, littleEndian24> > >(sample, sourceBuf, fileSize); } else { bytesRead = CopyMonoSample, SC::DecodeInt24<0, bigEndian24> > >(sample, sourceBuf, fileSize); } } ////////////////////////////////////////////////////// // 24-Bit / Signed / Stereo Interleaved / PCM else if(GetBitDepth() == 24 && GetChannelFormat() == stereoInterleaved && GetEncoding() == signedPCM) { if(GetEndianness() == littleEndian) { bytesRead = CopyStereoInterleavedSample, SC::DecodeInt24<0, littleEndian24> > >(sample, sourceBuf, fileSize); } else { bytesRead = CopyStereoInterleavedSample, SC::DecodeInt24<0, bigEndian24> > >(sample, sourceBuf, fileSize); } } ////////////////////////////////////////////////////// // 32-Bit / Signed / Mono / PCM else if(GetBitDepth() == 32 && GetChannelFormat() == mono && GetEncoding() == signedPCM) { if(GetEndianness() == littleEndian) { bytesRead = CopyMonoSample, SC::DecodeInt32<0, littleEndian32> > >(sample, sourceBuf, fileSize); } else { bytesRead = CopyMonoSample, SC::DecodeInt32<0, bigEndian32> > >(sample, sourceBuf, fileSize); } } ////////////////////////////////////////////////////// // 32-Bit / Signed / Stereo Interleaved / PCM else if(GetBitDepth() == 32 && GetChannelFormat() == stereoInterleaved && GetEncoding() == signedPCM) { if(GetEndianness() == littleEndian) { bytesRead = CopyStereoInterleavedSample, SC::DecodeInt32<0, littleEndian32> > >(sample, sourceBuf, fileSize); } else { bytesRead = CopyStereoInterleavedSample, SC::DecodeInt32<0, bigEndian32> > >(sample, sourceBuf, fileSize); } } ////////////////////////////////////////////////////// // 64-Bit / Signed / Mono / PCM else if(GetBitDepth() == 64 && GetChannelFormat() == mono && GetEncoding() == signedPCM) { if(GetEndianness() == littleEndian) { bytesRead = CopyMonoSample, SC::DecodeInt64<0, littleEndian64> > >(sample, sourceBuf, fileSize); } else { bytesRead = CopyMonoSample, SC::DecodeInt64<0, bigEndian64> > >(sample, sourceBuf, fileSize); } } ////////////////////////////////////////////////////// // 64-Bit / Signed / Stereo Interleaved / PCM else if(GetBitDepth() == 64 && GetChannelFormat() == stereoInterleaved && GetEncoding() == signedPCM) { if(GetEndianness() == littleEndian) { bytesRead = CopyStereoInterleavedSample, SC::DecodeInt64<0, littleEndian64> > >(sample, sourceBuf, fileSize); } else { bytesRead = CopyStereoInterleavedSample, SC::DecodeInt64<0, bigEndian64> > >(sample, sourceBuf, fileSize); } } ////////////////////////////////////////////////////// // 32-Bit / Float / Mono / PCM else if(GetBitDepth() == 32 && GetChannelFormat() == mono && GetEncoding() == floatPCM) { if(GetEndianness() == littleEndian) { bytesRead = CopyMonoSample, SC::DecodeFloat32 > >(sample, sourceBuf, fileSize); } else { bytesRead = CopyMonoSample, SC::DecodeFloat32 > >(sample, sourceBuf, fileSize); } } ////////////////////////////////////////////////////// // 32-Bit / Float / Stereo Interleaved / PCM else if(GetBitDepth() == 32 && GetChannelFormat() == stereoInterleaved && GetEncoding() == floatPCM) { if(GetEndianness() == littleEndian) { bytesRead = CopyStereoInterleavedSample, SC::DecodeFloat32 > >(sample, sourceBuf, fileSize); } else { bytesRead = CopyStereoInterleavedSample, SC::DecodeFloat32 > >(sample, sourceBuf, fileSize); } } ////////////////////////////////////////////////////// // 64-Bit / Float / Mono / PCM else if(GetBitDepth() == 64 && GetChannelFormat() == mono && GetEncoding() == floatPCM) { if(GetEndianness() == littleEndian) { bytesRead = CopyMonoSample, SC::DecodeFloat64 > >(sample, sourceBuf, fileSize); } else { bytesRead = CopyMonoSample, SC::DecodeFloat64 > >(sample, sourceBuf, fileSize); } } ////////////////////////////////////////////////////// // 64-Bit / Float / Stereo Interleaved / PCM else if(GetBitDepth() == 64 && GetChannelFormat() == stereoInterleaved && GetEncoding() == floatPCM) { if(GetEndianness() == littleEndian) { bytesRead = CopyStereoInterleavedSample, SC::DecodeFloat64 > >(sample, sourceBuf, fileSize); } else { bytesRead = CopyStereoInterleavedSample, SC::DecodeFloat64 > >(sample, sourceBuf, fileSize); } } ////////////////////////////////////////////////////// // 24-Bit / Signed / Mono, Stereo Interleaved / PCM else if(GetBitDepth() == 24 && (GetChannelFormat() == mono || GetChannelFormat() == stereoInterleaved) && GetEncoding() == signedPCMnormalize) { // Normalize to 16-Bit uint32 srcPeak = uint32(1)<<31; if(GetEndianness() == littleEndian) { bytesRead = CopyAndNormalizeSample, SC::DecodeInt24<0, littleEndian24> > >(sample, sourceBuf, fileSize, &srcPeak); } else { bytesRead = CopyAndNormalizeSample, SC::DecodeInt24<0, bigEndian24> > >(sample, sourceBuf, fileSize, &srcPeak); } if(bytesRead && srcPeak != uint32(1)<<31) { // Adjust sample volume so we do not affect relative volume of the sample. Normalizing is only done to increase precision. sample.nGlobalVol = static_cast(Clamp(Util::muldivr_unsigned(sample.nGlobalVol, srcPeak, uint32(1)<<31), uint32(1), uint32(64))); sample.uFlags.set(SMP_MODIFIED); } } ////////////////////////////////////////////////////// // 32-Bit / Signed / Mono, Stereo Interleaved / PCM else if(GetBitDepth() == 32 && (GetChannelFormat() == mono || GetChannelFormat() == stereoInterleaved) && GetEncoding() == signedPCMnormalize) { // Normalize to 16-Bit uint32 srcPeak = uint32(1)<<31; if(GetEndianness() == littleEndian) { bytesRead = CopyAndNormalizeSample, SC::DecodeInt32<0, littleEndian32> > >(sample, sourceBuf, fileSize, &srcPeak); } else { bytesRead = CopyAndNormalizeSample, SC::DecodeInt32<0, bigEndian32> > >(sample, sourceBuf, fileSize, &srcPeak); } if(bytesRead && srcPeak != uint32(1)<<31) { // Adjust sample volume so we do not affect relative volume of the sample. Normalizing is only done to increase precision. sample.nGlobalVol = static_cast(Clamp(Util::muldivr_unsigned(sample.nGlobalVol, srcPeak, uint32(1)<<31), uint32(1), uint32(64))); sample.uFlags.set(SMP_MODIFIED); } } ////////////////////////////////////////////////////// // 32-Bit / Float / Mono, Stereo Interleaved / PCM else if(GetBitDepth() == 32 && (GetChannelFormat() == mono || GetChannelFormat() == stereoInterleaved) && GetEncoding() == floatPCMnormalize) { // Normalize to 16-Bit float32 srcPeak = 1.0f; if(GetEndianness() == littleEndian) { bytesRead = CopyAndNormalizeSample, SC::DecodeFloat32 > >(sample, sourceBuf, fileSize, &srcPeak); } else { bytesRead = CopyAndNormalizeSample, SC::DecodeFloat32 > >(sample, sourceBuf, fileSize, &srcPeak); } if(bytesRead && srcPeak != 1.0f) { // Adjust sample volume so we do not affect relative volume of the sample. Normalizing is only done to increase precision. sample.nGlobalVol = mpt::saturate_round(Clamp(sample.nGlobalVol * srcPeak, 1.0f, 64.0f)); sample.uFlags.set(SMP_MODIFIED); } } ////////////////////////////////////////////////////// // 64-Bit / Float / Mono, Stereo Interleaved / PCM else if(GetBitDepth() == 64 && (GetChannelFormat() == mono || GetChannelFormat() == stereoInterleaved) && GetEncoding() == floatPCMnormalize) { // Normalize to 16-Bit float64 srcPeak = 1.0; if(GetEndianness() == littleEndian) { bytesRead = CopyAndNormalizeSample, SC::DecodeFloat64 > >(sample, sourceBuf, fileSize, &srcPeak); } else { bytesRead = CopyAndNormalizeSample, SC::DecodeFloat64 > >(sample, sourceBuf, fileSize, &srcPeak); } if(bytesRead && srcPeak != 1.0) { // Adjust sample volume so we do not affect relative volume of the sample. Normalizing is only done to increase precision. sample.nGlobalVol = mpt::saturate_round(Clamp(sample.nGlobalVol * srcPeak, 1.0, 64.0)); sample.uFlags.set(SMP_MODIFIED); } } ////////////////////////////////////////////////////// // 32-Bit / Float / Mono / PCM / full scale 2^15 else if(GetBitDepth() == 32 && GetChannelFormat() == mono && GetEncoding() == floatPCM15) { if(GetEndianness() == littleEndian) { bytesRead = CopyMonoSample (sample, sourceBuf, fileSize, SC::ConversionChain, SC::DecodeScaledFloat32 > (SC::Convert(), SC::DecodeScaledFloat32(1.0f / static_cast(1<<15))) ); } else { bytesRead = CopyMonoSample (sample, sourceBuf, fileSize, SC::ConversionChain, SC::DecodeScaledFloat32 > (SC::Convert(), SC::DecodeScaledFloat32(1.0f / static_cast(1<<15))) ); } } ////////////////////////////////////////////////////// // 32-Bit / Float / Stereo Interleaved / PCM / full scale 2^15 else if(GetBitDepth() == 32 && GetChannelFormat() == stereoInterleaved && GetEncoding() == floatPCM15) { if(GetEndianness() == littleEndian) { bytesRead = CopyStereoInterleavedSample (sample, sourceBuf, fileSize, SC::ConversionChain, SC::DecodeScaledFloat32 > (SC::Convert(), SC::DecodeScaledFloat32(1.0f / static_cast(1<<15))) ); } else { bytesRead = CopyStereoInterleavedSample (sample, sourceBuf, fileSize, SC::ConversionChain, SC::DecodeScaledFloat32 > (SC::Convert(), SC::DecodeScaledFloat32(1.0f / static_cast(1<<15))) ); } } ////////////////////////////////////////////////////// // 32-Bit / Float / Stereo Interleaved / PCM / full scale 2^23 else if(GetBitDepth() == 32 && GetChannelFormat() == mono && GetEncoding() == floatPCM23) { if(GetEndianness() == littleEndian) { bytesRead = CopyMonoSample (sample, sourceBuf, fileSize, SC::ConversionChain, SC::DecodeScaledFloat32 > (SC::Convert(), SC::DecodeScaledFloat32(1.0f / static_cast(1<<23))) ); } else { bytesRead = CopyMonoSample (sample, sourceBuf, fileSize, SC::ConversionChain, SC::DecodeScaledFloat32 > (SC::Convert(), SC::DecodeScaledFloat32(1.0f / static_cast(1<<23))) ); } } ////////////////////////////////////////////////////// // 32-Bit / Float / Stereo Interleaved / PCM / full scale 2^23 else if(GetBitDepth() == 32 && GetChannelFormat() == stereoInterleaved && GetEncoding() == floatPCM23) { if(GetEndianness() == littleEndian) { bytesRead = CopyStereoInterleavedSample (sample, sourceBuf, fileSize, SC::ConversionChain, SC::DecodeScaledFloat32 > (SC::Convert(), SC::DecodeScaledFloat32(1.0f / static_cast(1<<23))) ); } else { bytesRead = CopyStereoInterleavedSample (sample, sourceBuf, fileSize, SC::ConversionChain, SC::DecodeScaledFloat32 > (SC::Convert(), SC::DecodeScaledFloat32(1.0f / static_cast(1<<23))) ); } } //////////////// // Unsupported else { MPT_ASSERT_NOTREACHED(); } MPT_ASSERT(filePosition + bytesRead <= file.GetLength()); file.Seek(filePosition + bytesRead); return bytesRead; } #ifndef MODPLUG_NO_FILESAVE // Write a sample to file size_t SampleIO::WriteSample(std::ostream &f, const ModSample &sample, SmpLength maxSamples) const { if(sample.uFlags[CHN_ADLIB]) { mpt::IO::Write(f, sample.adlib); return sizeof(sample.adlib); } if(!sample.HasSampleData()) { return 0; } std::array writeBuffer; mpt::IO::WriteBuffer fb{f, mpt::as_span(writeBuffer)}; SmpLength numSamples = sample.nLength; if(maxSamples && numSamples > maxSamples) { numSamples = maxSamples; } std::size_t len = CalculateEncodedSize(numSamples); if(GetBitDepth() == 16 && GetChannelFormat() == mono && GetEndianness() == littleEndian && (GetEncoding() == signedPCM || GetEncoding() == unsignedPCM || GetEncoding() == deltaPCM)) { // 16-bit little-endian mono samples MPT_ASSERT(len == numSamples * 2); const int16 *const pSample16 = sample.sample16(); const int16 *p = pSample16; int s_old = 0; const int s_ofs = (GetEncoding() == unsignedPCM) ? 0x8000 : 0; for(SmpLength j = 0; j < numSamples; j++) { int s_new = *p; p++; if(sample.uFlags[CHN_STEREO]) { // Downmix stereo s_new = (s_new + (*p) + 1) / 2; p++; } if(GetEncoding() == deltaPCM) { mpt::IO::Write(fb, mpt::as_le(static_cast(s_new - s_old))); s_old = s_new; } else { mpt::IO::Write(fb, mpt::as_le(static_cast(s_new + s_ofs))); } } } else if(GetBitDepth() == 8 && GetChannelFormat() == stereoSplit && (GetEncoding() == signedPCM || GetEncoding() == unsignedPCM || GetEncoding() == deltaPCM)) { // 8-bit Stereo samples (not interleaved) MPT_ASSERT(len == numSamples * 2); const int8 *const pSample8 = sample.sample8(); const int s_ofs = (GetEncoding() == unsignedPCM) ? 0x80 : 0; for (uint32 iCh=0; iCh<2; iCh++) { const int8 *p = pSample8 + iCh; int s_old = 0; for (SmpLength j = 0; j < numSamples; j++) { int s_new = *p; p += 2; if (GetEncoding() == deltaPCM) { mpt::IO::Write(fb, static_cast(s_new - s_old)); s_old = s_new; } else { mpt::IO::Write(fb, static_cast(s_new + s_ofs)); } } } } else if(GetBitDepth() == 16 && GetChannelFormat() == stereoSplit && GetEndianness() == littleEndian && (GetEncoding() == signedPCM || GetEncoding() == unsignedPCM || GetEncoding() == deltaPCM)) { // 16-bit little-endian Stereo samples (not interleaved) MPT_ASSERT(len == numSamples * 4); const int16 *const pSample16 = sample.sample16(); const int s_ofs = (GetEncoding() == unsignedPCM) ? 0x8000 : 0; for (uint32 iCh=0; iCh<2; iCh++) { const int16 *p = pSample16 + iCh; int s_old = 0; for (SmpLength j = 0; j < numSamples; j++) { int s_new = *p; p += 2; if (GetEncoding() == deltaPCM) { mpt::IO::Write(fb, mpt::as_le(static_cast(s_new - s_old))); s_old = s_new; } else { mpt::IO::Write(fb, mpt::as_le(static_cast(s_new + s_ofs))); } } } } else if(GetBitDepth() == 8 && GetChannelFormat() == stereoInterleaved && GetEncoding() == signedPCM) { // Stereo signed interleaved MPT_ASSERT(len == numSamples * 2); const int8 *const pSample8 = sample.sample8(); mpt::IO::WriteRaw(f, reinterpret_cast(pSample8), len); } else if(GetBitDepth() == 16 && GetChannelFormat() == stereoInterleaved && GetEncoding() == signedPCM && GetEndianness() == littleEndian) { // Stereo signed interleaved MPT_ASSERT(len == numSamples * 4); const int16 *const pSample16 = sample.sample16(); const int16 *p = pSample16; for(SmpLength j = 0; j < numSamples; j++) { mpt::IO::Write(fb, mpt::as_le(p[0])); mpt::IO::Write(fb, mpt::as_le(p[1])); p += 2; } } else if(GetBitDepth() == 16 && GetChannelFormat() == stereoInterleaved && GetEncoding() == signedPCM && GetEndianness() == bigEndian) { // Stereo signed interleaved MPT_ASSERT(len == numSamples * 4); const int16 *const pSample16 = sample.sample16(); const int16 *p = pSample16; for(SmpLength j = 0; j < numSamples; j++) { mpt::IO::Write(fb, mpt::as_be(p[0])); mpt::IO::Write(fb, mpt::as_be(p[1])); p += 2; } } else if(GetBitDepth() == 8 && GetChannelFormat() == stereoInterleaved && GetEncoding() == unsignedPCM) { // Stereo unsigned interleaved MPT_ASSERT(len == numSamples * 2); const int8 *const pSample8 = sample.sample8(); for(SmpLength j = 0; j < numSamples * 2; j++) { mpt::IO::Write(fb, static_cast(static_cast(pSample8[j]) + 0x80)); } } else if(GetEncoding() == IT214 || GetEncoding() == IT215) { // IT2.14-encoded samples ITCompression its(sample, GetEncoding() == IT215, &f, numSamples); len = its.GetCompressedSize(); } // Default: assume 8-bit PCM data else { MPT_ASSERT(GetBitDepth() == 8); MPT_ASSERT(len == numSamples); if(sample.uFlags[CHN_16BIT]) { const int16 *p = sample.sample16(); int s_old = 0; const int s_ofs = (GetEncoding() == unsignedPCM) ? 0x80 : 0; for(SmpLength j = 0; j < numSamples; j++) { int s_new = mpt::rshift_signed(*p, 8); p++; if(sample.uFlags[CHN_STEREO]) { s_new = (s_new + mpt::rshift_signed(*p, 8) + 1) / 2; p++; } if(GetEncoding() == deltaPCM) { mpt::IO::Write(fb, static_cast(s_new - s_old)); s_old = s_new; } else { mpt::IO::Write(fb, static_cast(s_new + s_ofs)); } } } else { const int8 *const pSample8 = sample.sample8(); const int8 *p = pSample8; int s_old = 0; const int s_ofs = (GetEncoding() == unsignedPCM) ? 0x80 : 0; for(SmpLength j = 0; j < numSamples; j++) { int s_new = *p; p++; if(sample.uFlags[CHN_STEREO]) { s_new = (s_new + (static_cast(*p)) + 1) / 2; p++; } if(GetEncoding() == deltaPCM) { mpt::IO::Write(fb, static_cast(s_new - s_old)); s_old = s_new; } else { mpt::IO::Write(fb, static_cast(s_new + s_ofs)); } } } } return len; } #endif // MODPLUG_NO_FILESAVE OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/SampleIO.h0000644000175000017500000001514214126162046017722 00000000000000/* * SampleIO.h * ---------- * Purpose: Central code for reading and writing samples. Create your SampleIO object and have a go at the ReadSample and WriteSample functions! * Notes : Not all combinations of possible sample format combinations are implemented, especially for WriteSample. * Using the existing generic sample conversion functors in SampleFormatConverters.h, it should be quite easy to extend the code, though. * Authors: Olivier Lapicque * OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "../common/FileReaderFwd.h" OPENMPT_NAMESPACE_BEGIN struct ModSample; // Sample import / export formats class SampleIO { public: // Bits per sample enum Bitdepth : uint8 { _8bit = 8, _16bit = 16, _24bit = 24, _32bit = 32, _64bit = 64, }; // Number of channels + channel format enum Channels : uint8 { mono = 1, stereoInterleaved, // LRLRLR... stereoSplit, // LLL...RRR... }; // Sample byte order enum Endianness : uint8 { littleEndian = 0, bigEndian = 1, }; // Sample encoding enum Encoding : uint8 { signedPCM = 0, // Integer PCM, signed unsignedPCM, // Integer PCM, unsigned deltaPCM, // Integer PCM, delta-encoded floatPCM, // Floating point PCM IT214, // Impulse Tracker 2.14 compressed IT215, // Impulse Tracker 2.15 compressed AMS, // AMS / Velvet Studio packed DMF, // DMF Huffman compression MDL, // MDL Huffman compression PTM8Dto16, // PTM 8-Bit delta value -> 16-Bit sample ADPCM, // 4-Bit ADPCM-packed MT2, // MadTracker 2 stereo delta encoding floatPCM15, // Floating point PCM with 2^15 full scale floatPCM23, // Floating point PCM with 2^23 full scale floatPCMnormalize, // Floating point PCM and data will be normalized while reading signedPCMnormalize, // Integer PCM and data will be normalized while reading uLaw, // 8-to-16 bit G.711 u-law compression aLaw, // 8-to-16 bit G.711 a-law compression }; protected: Bitdepth m_bitdepth; Channels m_channels; Endianness m_endianness; Encoding m_encoding; public: constexpr SampleIO(Bitdepth bits = _8bit, Channels channels = mono, Endianness endianness = littleEndian, Encoding encoding = signedPCM) : m_bitdepth(bits), m_channels(channels), m_endianness(endianness), m_encoding(encoding) { } bool operator== (const SampleIO &other) const { return memcmp(this, &other, sizeof(*this)) == 0; } bool operator!= (const SampleIO &other) const { return memcmp(this, &other, sizeof(*this)) != 0; } void operator|= (Bitdepth bits) { m_bitdepth = bits; } void operator|= (Channels channels) { m_channels = channels; } void operator|= (Endianness endianness) { m_endianness = endianness; } void operator|= (Encoding encoding) { m_encoding = encoding; } void MayNormalize() { if(GetBitDepth() >= 24) { if(GetEncoding() == SampleIO::signedPCM) { m_encoding = SampleIO::signedPCMnormalize; } else if(GetEncoding() == SampleIO::floatPCM) { m_encoding = SampleIO::floatPCMnormalize; } } } // Return 0 in case of variable-length encoded samples. MPT_CONSTEXPRINLINE uint8 GetEncodedBitsPerSample() const { switch(GetEncoding()) { case signedPCM: // Integer PCM, signed case unsignedPCM: //Integer PCM, unsigned case deltaPCM: // Integer PCM, delta-encoded case floatPCM: // Floating point PCM case MT2: // MadTracker 2 stereo delta encoding case floatPCM15: // Floating point PCM with 2^15 full scale case floatPCM23: // Floating point PCM with 2^23 full scale case floatPCMnormalize: // Floating point PCM and data will be normalized while reading case signedPCMnormalize: // Integer PCM and data will be normalized while reading return GetBitDepth(); case IT214: // Impulse Tracker 2.14 compressed case IT215: // Impulse Tracker 2.15 compressed case AMS: // AMS / Velvet Studio packed case DMF: // DMF Huffman compression case MDL: // MDL Huffman compression return 0; // variable-length compressed case PTM8Dto16: // PTM 8-Bit delta value -> 16-Bit sample return 16; case ADPCM: // 4-Bit ADPCM-packed return 4; case uLaw: // G.711 u-law return 8; case aLaw: // G.711 a-law return 8; default: return 0; } } // Return the static header size additional to the raw encoded sample data. MPT_CONSTEXPRINLINE std::size_t GetEncodedHeaderSize() const { switch(GetEncoding()) { case ADPCM: return 16; default: return 0; } } // Returns true if the encoded size cannot be calculated apriori from the encoding format and the sample length. MPT_CONSTEXPRINLINE bool IsVariableLengthEncoded() const { return GetEncodedBitsPerSample() == 0; } // Returns true if the decoder for a given format uses FileReader interface and thus do not need to call GetPinnedView() MPT_CONSTEXPRINLINE bool UsesFileReaderForDecoding() const { switch(GetEncoding()) { case IT214: case IT215: case AMS: case DMF: case MDL: return true; default: return false; } } // Get bits per sample constexpr uint8 GetBitDepth() const { return static_cast(m_bitdepth); } // Get channel layout constexpr Channels GetChannelFormat() const { return m_channels; } // Get number of channels constexpr uint8 GetNumChannels() const { return GetChannelFormat() == mono ? 1u : 2u; } // Get sample byte order constexpr Endianness GetEndianness() const { return m_endianness; } // Get sample format / encoding constexpr Encoding GetEncoding() const { return m_encoding; } // Returns the encoded size of the sample. In case of variable-length encoding returns 0. std::size_t CalculateEncodedSize(SmpLength length) const { if(IsVariableLengthEncoded()) { return 0; } uint8 bps = GetEncodedBitsPerSample(); if(bps % 8u != 0) { MPT_ASSERT(GetEncoding() == ADPCM && bps == 4); return GetEncodedHeaderSize() + (((length + 1) / 2) * GetNumChannels()); // round up } return GetEncodedHeaderSize() + (length * (bps / 8) * GetNumChannels()); } // Read a sample from memory size_t ReadSample(ModSample &sample, FileReader &file) const; #ifndef MODPLUG_NO_FILESAVE // Write a sample to file size_t WriteSample(std::ostream &f, const ModSample &sample, SmpLength maxSamples = 0) const; #endif // MODPLUG_NO_FILESAVE }; OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/SampleNormalize.h0000644000175000017500000000717314053010430021344 00000000000000/* * SampleNormalize.h * ----------------- * Purpose: Functions for normalizing samples. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" OPENMPT_NAMESPACE_BEGIN namespace SC { // SC = _S_ample_C_onversion template struct Normalize; template <> struct Normalize { typedef int32 input_t; typedef int32 output_t; typedef uint32 peak_t; uint32 maxVal; MPT_FORCEINLINE Normalize() : maxVal(0) {} MPT_FORCEINLINE void FindMax(input_t val) { if(val < 0) { if(val == std::numeric_limits::min()) { maxVal = static_cast(-static_cast(std::numeric_limits::min())); return; } val = -val; } if(static_cast(val) > maxVal) { maxVal = static_cast(val); } } MPT_FORCEINLINE bool IsSilent() const { return maxVal == 0; } MPT_FORCEINLINE output_t operator()(input_t val) { return Util::muldivrfloor(val, static_cast(1) << 31, maxVal); } MPT_FORCEINLINE peak_t GetSrcPeak() const { return maxVal; } }; template <> struct Normalize { typedef float32 input_t; typedef float32 output_t; typedef float32 peak_t; float maxVal; float maxValInv; MPT_FORCEINLINE Normalize() : maxVal(0.0f), maxValInv(1.0f) {} MPT_FORCEINLINE void FindMax(input_t val) { float absval = std::fabs(val); if(absval > maxVal) { maxVal = absval; } } MPT_FORCEINLINE bool IsSilent() { if(maxVal == 0.0f) { maxValInv = 1.0f; return true; } else { maxValInv = 1.0f / maxVal; return false; } } MPT_FORCEINLINE output_t operator()(input_t val) { return val * maxValInv; } MPT_FORCEINLINE peak_t GetSrcPeak() const { return maxVal; } }; template <> struct Normalize { typedef float64 input_t; typedef float64 output_t; typedef float64 peak_t; double maxVal; double maxValInv; MPT_FORCEINLINE Normalize() : maxVal(0.0), maxValInv(1.0) {} MPT_FORCEINLINE void FindMax(input_t val) { double absval = std::fabs(val); if(absval > maxVal) { maxVal = absval; } } MPT_FORCEINLINE bool IsSilent() { if(maxVal == 0.0) { maxValInv = 1.0; return true; } else { maxValInv = 1.0 / maxVal; return false; } } MPT_FORCEINLINE output_t operator()(input_t val) { return val * maxValInv; } MPT_FORCEINLINE peak_t GetSrcPeak() const { return maxVal; } }; // Reads sample data with Func1, then normalizes the sample data, and then converts it with Func2. // Func1::output_t and Func2::input_t must be identical. // Func1 can also be the identity decode (DecodeIdentity). // Func2 can also be the identity conversion (Convert). template struct NormalizationChain { typedef typename Func1::input_t input_t; typedef typename Func1::output_t normalize_t; typedef typename Normalize::peak_t peak_t; typedef typename Func2::output_t output_t; static constexpr std::size_t input_inc = Func1::input_inc; Func1 func1; Normalize normalize; Func2 func2; MPT_FORCEINLINE void FindMax(const input_t *inBuf) { normalize.FindMax(func1(inBuf)); } MPT_FORCEINLINE bool IsSilent() { return normalize.IsSilent(); } MPT_FORCEINLINE output_t operator()(const input_t *inBuf) { return func2(normalize(func1(inBuf))); } MPT_FORCEINLINE peak_t GetSrcPeak() const { return normalize.GetSrcPeak(); } MPT_FORCEINLINE NormalizationChain(Func2 f2 = Func2(), Func1 f1 = Func1()) : func1(f1) , func2(f2) { return; } }; } // namespace SC OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Snd_defs.h0000644000175000017500000010076714155106444020010 00000000000000/* * Snd_Defs.h * ---------- * Purpose: Basic definitions of data types, enums, etc. for the playback engine core. * Notes : (currently none) * Authors: Olivier Lapicque * OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "openmpt/base/FlagSet.hpp" OPENMPT_NAMESPACE_BEGIN using ROWINDEX = uint32; inline constexpr ROWINDEX ROWINDEX_INVALID = uint32_max; using CHANNELINDEX = uint16; inline constexpr CHANNELINDEX CHANNELINDEX_INVALID = uint16_max; using ORDERINDEX = uint16; inline constexpr ORDERINDEX ORDERINDEX_INVALID = uint16_max; inline constexpr ORDERINDEX ORDERINDEX_MAX = uint16_max - 1; using PATTERNINDEX = uint16; inline constexpr PATTERNINDEX PATTERNINDEX_INVALID = uint16_max; using PLUGINDEX = uint8; inline constexpr PLUGINDEX PLUGINDEX_INVALID = uint8_max; using SAMPLEINDEX = uint16; inline constexpr SAMPLEINDEX SAMPLEINDEX_INVALID = uint16_max; using INSTRUMENTINDEX = uint16; inline constexpr INSTRUMENTINDEX INSTRUMENTINDEX_INVALID = uint16_max; using SEQUENCEINDEX = uint8; inline constexpr SEQUENCEINDEX SEQUENCEINDEX_INVALID = uint8_max; using SmpLength = uint32; inline constexpr SmpLength MAX_SAMPLE_LENGTH = 0x10000000; // Sample length in frames. Sample size in bytes can be more than this (= 256 MB). inline constexpr ROWINDEX MAX_PATTERN_ROWS = 1024; inline constexpr ROWINDEX MAX_ROWS_PER_BEAT = 65536; inline constexpr ORDERINDEX MAX_ORDERS = ORDERINDEX_MAX + 1; inline constexpr PATTERNINDEX MAX_PATTERNS = 4000; inline constexpr SAMPLEINDEX MAX_SAMPLES = 4000; inline constexpr INSTRUMENTINDEX MAX_INSTRUMENTS = 256; inline constexpr PLUGINDEX MAX_MIXPLUGINS = 250; inline constexpr SEQUENCEINDEX MAX_SEQUENCES = 50; inline constexpr CHANNELINDEX MAX_BASECHANNELS = 127; // Maximum pattern channels. inline constexpr CHANNELINDEX MAX_CHANNELS = 256; // Maximum number of mixing channels. enum { FREQ_FRACBITS = 4 }; // Number of fractional bits in return value of CSoundFile::GetFreqFromPeriod() // String lengths (including trailing null char) enum { MAX_SAMPLENAME = 32, MAX_SAMPLEFILENAME = 22, MAX_INSTRUMENTNAME = 32, MAX_INSTRUMENTFILENAME = 32, MAX_PATTERNNAME = 32, MAX_CHANNELNAME = 20, }; enum MODTYPE { MOD_TYPE_NONE = 0x00, MOD_TYPE_MOD = 0x01, MOD_TYPE_S3M = 0x02, MOD_TYPE_XM = 0x04, MOD_TYPE_MED = 0x08, MOD_TYPE_MTM = 0x10, MOD_TYPE_IT = 0x20, MOD_TYPE_669 = 0x40, MOD_TYPE_ULT = 0x80, MOD_TYPE_STM = 0x100, MOD_TYPE_FAR = 0x200, MOD_TYPE_DTM = 0x400, MOD_TYPE_AMF = 0x800, MOD_TYPE_AMS = 0x1000, MOD_TYPE_DSM = 0x2000, MOD_TYPE_MDL = 0x4000, MOD_TYPE_OKT = 0x8000, MOD_TYPE_MID = 0x10000, MOD_TYPE_DMF = 0x20000, MOD_TYPE_PTM = 0x40000, MOD_TYPE_DBM = 0x80000, MOD_TYPE_MT2 = 0x100000, MOD_TYPE_AMF0 = 0x200000, MOD_TYPE_PSM = 0x400000, MOD_TYPE_J2B = 0x800000, MOD_TYPE_MPT = 0x1000000, MOD_TYPE_IMF = 0x2000000, MOD_TYPE_DIGI = 0x4000000, MOD_TYPE_STP = 0x8000000, MOD_TYPE_PLM = 0x10000000, MOD_TYPE_SFX = 0x20000000, }; DECLARE_FLAGSET(MODTYPE) enum MODCONTAINERTYPE { MOD_CONTAINERTYPE_NONE = 0x0, MOD_CONTAINERTYPE_UMX = 0x3, MOD_CONTAINERTYPE_XPK = 0x4, MOD_CONTAINERTYPE_PP20 = 0x5, MOD_CONTAINERTYPE_MMCMP= 0x6, MOD_CONTAINERTYPE_WAV = 0x7, // WAV as module MOD_CONTAINERTYPE_UAX = 0x8, // Unreal sample set as module }; // Module channel / sample flags enum ChannelFlags { // Sample Flags CHN_16BIT = 0x01, // 16-bit sample CHN_LOOP = 0x02, // Looped sample CHN_PINGPONGLOOP = 0x04, // Bidi-looped sample CHN_SUSTAINLOOP = 0x08, // Sample with sustain loop CHN_PINGPONGSUSTAIN = 0x10, // Sample with bidi sustain loop CHN_PANNING = 0x20, // Sample with forced panning CHN_STEREO = 0x40, // Stereo sample CHN_REVERSE = 0x80, // Start sample playback from sample / loop end (Velvet Studio feature) CHN_SURROUND = 0x100, // Use surround channel CHN_ADLIB = 0x200, // Adlib / OPL instrument is active on this channel // Channel Flags CHN_PINGPONGFLAG = 0x80, // When flag is on, sample is processed backwards - this is intentionally the same flag as CHN_REVERSE. CHN_MUTE = 0x400, // Muted channel CHN_KEYOFF = 0x800, // Exit sustain CHN_NOTEFADE = 0x1000, // Fade note (instrument mode) CHN_WRAPPED_LOOP = 0x2000, // Loop just wrapped around to loop start (required for correct interpolation around loop points) CHN_AMIGAFILTER = 0x4000, // Apply Amiga low-pass filter CHN_FILTER = 0x8000, // Apply resonant filter on sample CHN_VOLUMERAMP = 0x10000, // Apply volume ramping CHN_VIBRATO = 0x20000, // Apply vibrato CHN_TREMOLO = 0x40000, // Apply tremolo CHN_PORTAMENTO = 0x80000, // Apply portamento CHN_GLISSANDO = 0x100000, // Glissando (force portamento to semitones) mode CHN_FASTVOLRAMP = 0x200000, // Force usage of global ramping settings instead of ramping over the complete render buffer length CHN_EXTRALOUD = 0x400000, // Force sample to play at 0dB CHN_REVERB = 0x800000, // Apply reverb on this channel CHN_NOREVERB = 0x1000000, // Disable reverb on this channel CHN_SOLO = 0x2000000, // Solo channel CHN_NOFX = 0x4000000, // Dry channel (no plugins) CHN_SYNCMUTE = 0x8000000, // Keep sample sync on mute // Sample flags (only present in ModSample::uFlags, may overlap with CHN_CHANNELFLAGS) SMP_MODIFIED = 0x2000, // Sample data has been edited in the tracker SMP_KEEPONDISK = 0x4000, // Sample is not saved to file, data is restored from original sample file SMP_NODEFAULTVOLUME = 0x8000, // Ignore default volume setting }; DECLARE_FLAGSET(ChannelFlags) #define CHN_SAMPLEFLAGS (CHN_16BIT | CHN_LOOP | CHN_PINGPONGLOOP | CHN_SUSTAINLOOP | CHN_PINGPONGSUSTAIN | CHN_PANNING | CHN_STEREO | CHN_PINGPONGFLAG | CHN_REVERSE | CHN_SURROUND | CHN_ADLIB) #define CHN_CHANNELFLAGS (~CHN_SAMPLEFLAGS | CHN_SURROUND) // Sample flags fit into the first 16 bits, and with the current memory layout, storing them as a 16-bit integer packs struct ModSample nicely. using SampleFlags = FlagSet; // Instrument envelope-specific flags enum EnvelopeFlags : uint8 { ENV_ENABLED = 0x01, // env is enabled ENV_LOOP = 0x02, // env loop ENV_SUSTAIN = 0x04, // env sustain ENV_CARRY = 0x08, // env carry ENV_FILTER = 0x10, // filter env enabled (this has to be combined with ENV_ENABLED in the pitch envelope's flags) }; DECLARE_FLAGSET(EnvelopeFlags) // Envelope value boundaries #define ENVELOPE_MIN 0 // Vertical min value of a point #define ENVELOPE_MID 32 // Vertical middle line #define ENVELOPE_MAX 64 // Vertical max value of a point #define MAX_ENVPOINTS 240 // Maximum length of each instrument envelope // Instrument-specific flags enum InstrumentFlags : uint8 { INS_SETPANNING = 0x01, // Panning enabled INS_MUTE = 0x02, // Instrument is muted }; DECLARE_FLAGSET(InstrumentFlags) // envelope types in instrument editor enum EnvelopeType : uint8 { ENV_VOLUME = 0, ENV_PANNING, ENV_PITCH, ENV_MAXTYPES }; // Filter Modes enum class FilterMode : uint8 { Unchanged = 0xFF, LowPass = 0, HighPass = 1, }; // NNA types (New Note Action) enum class NewNoteAction : uint8 { NoteCut = 0, Continue = 1, NoteOff = 2, NoteFade = 3, }; // DCT types (Duplicate Check Types) enum class DuplicateCheckType : uint8 { None = 0, Note = 1, Sample = 2, Instrument = 3, Plugin = 4, }; // DNA types (Duplicate Note Action) enum class DuplicateNoteAction : uint8 { NoteCut = 0, NoteOff = 1, NoteFade = 2, }; // Module flags - contains both song configuration and playback state... Use SONG_FILE_FLAGS and SONG_PLAY_FLAGS distinguish between the two. enum SongFlags { SONG_FASTVOLSLIDES = 0x02, // Old Scream Tracker 3.0 volume slides SONG_ITOLDEFFECTS = 0x04, // Old Impulse Tracker effect implementations SONG_ITCOMPATGXX = 0x08, // IT "Compatible Gxx" (IT's flag to behave more like other trackers w/r/t portamento effects) SONG_LINEARSLIDES = 0x10, // Linear slides vs. Amiga slides SONG_PATTERNLOOP = 0x20, // Loop current pattern (pattern editor) SONG_STEP = 0x40, // Song is in "step" mode (pattern editor) SONG_PAUSED = 0x80, // Song is paused (no tick processing, just rendering audio) SONG_FADINGSONG = 0x0100, // Song is fading out SONG_ENDREACHED = 0x0200, // Song is finished SONG_FIRSTTICK = 0x1000, // Is set when the current tick is the first tick of the row SONG_MPTFILTERMODE = 0x2000, // Local filter mode (reset filter on each note) SONG_SURROUNDPAN = 0x4000, // Pan in the rear channels SONG_EXFILTERRANGE = 0x8000, // Cutoff Filter has double frequency range (up to ~10Khz) SONG_AMIGALIMITS = 0x1'0000, // Enforce amiga frequency limits SONG_S3MOLDVIBRATO = 0x2'0000, // ScreamTracker 2 vibrato in S3M files SONG_BREAKTOROW = 0x8'0000, // Break to row command encountered (internal flag, do not touch) SONG_POSJUMP = 0x10'0000, // Position jump encountered (internal flag, do not touch) SONG_PT_MODE = 0x20'0000, // ProTracker 1/2 playback mode SONG_PLAYALLSONGS = 0x40'0000, // Play all subsongs consecutively (libopenmpt) SONG_ISAMIGA = 0x80'0000, // Is an Amiga module and thus qualifies to be played using the Paula BLEP resampler SONG_IMPORTED = 0x100'0000, // Song type does not represent actual module format / was imported from a different format (OpenMPT) }; DECLARE_FLAGSET(SongFlags) #define SONG_FILE_FLAGS (SONG_FASTVOLSLIDES|SONG_ITOLDEFFECTS|SONG_ITCOMPATGXX|SONG_LINEARSLIDES|SONG_EXFILTERRANGE|SONG_AMIGALIMITS|SONG_S3MOLDVIBRATO|SONG_PT_MODE|SONG_ISAMIGA|SONG_IMPORTED) #define SONG_PLAY_FLAGS (~SONG_FILE_FLAGS) // Global Options (Renderer) #ifndef NO_AGC #define SNDDSP_AGC 0x40 // Automatic gain control #endif // ~NO_AGC #ifndef NO_DSP #define SNDDSP_MEGABASS 0x02 // Bass expansion #define SNDDSP_SURROUND 0x08 // Surround mix #define SNDDSP_BITCRUSH 0x01 #endif // NO_DSP #ifndef NO_REVERB #define SNDDSP_REVERB 0x20 // Apply reverb #endif // NO_REVERB #ifndef NO_EQ #define SNDDSP_EQ 0x80 // Apply EQ #endif // NO_EQ #define SNDMIX_SOFTPANNING 0x10 // Soft panning mode (this is forced with mixmode RC3 and later) // Misc Flags (can safely be turned on or off) #define SNDMIX_MAXDEFAULTPAN 0x80000 // Currently unused (should be used by Amiga MOD loaders) #define SNDMIX_MUTECHNMODE 0x100000 // Notes are not played on muted channels #define MAX_GLOBAL_VOLUME 256u // Resampling modes enum ResamplingMode : uint8 { // ATTENTION: Do not change ANY of these values, as they get written out to files in per instrument interpolation settings // and old files have these exact values in them which should not change meaning. SRCMODE_NEAREST = 0, // 1 tap, no AA SRCMODE_LINEAR = 1, // 2 tap, no AA SRCMODE_CUBIC = 2, // 4 tap, no AA SRCMODE_SINC8 = 4, // 8 tap, no AA (yes, index 4) (XMMS-ModPlug) SRCMODE_SINC8LP = 3, // 8 tap, with AA (yes, index 3) (Polyphase) SRCMODE_DEFAULT = 5, // Only used for instrument settings, not used inside the mixer SRCMODE_AMIGA = 0xFF, // Not explicitely user-selectable }; namespace Resampling { enum class AmigaFilter { Off = 0, A500 = 1, A1200 = 2, Unfiltered = 3, }; inline std::array AllModes() noexcept { return { { SRCMODE_NEAREST, SRCMODE_LINEAR, SRCMODE_CUBIC, SRCMODE_SINC8, SRCMODE_SINC8LP } }; } inline std::array AllModesWithDefault() noexcept { return { { SRCMODE_NEAREST, SRCMODE_LINEAR, SRCMODE_CUBIC, SRCMODE_SINC8, SRCMODE_SINC8LP, SRCMODE_DEFAULT } }; } constexpr ResamplingMode Default() noexcept { return SRCMODE_SINC8LP; } constexpr bool IsKnownMode(int mode) noexcept { return (mode >= 0) && (mode < SRCMODE_DEFAULT); } constexpr ResamplingMode ToKnownMode(int mode) noexcept { return Resampling::IsKnownMode(mode) ? static_cast(mode) : (mode == SRCMODE_AMIGA) ? SRCMODE_LINEAR : Resampling::Default(); } constexpr int Length(ResamplingMode mode) noexcept { return mode == SRCMODE_NEAREST ? 1 : mode == SRCMODE_LINEAR ? 2 : mode == SRCMODE_CUBIC ? 4 : mode == SRCMODE_SINC8 ? 8 : mode == SRCMODE_SINC8LP ? 8 : 0; } constexpr bool HasAA(ResamplingMode mode) noexcept { return (mode == SRCMODE_SINC8LP); } constexpr ResamplingMode AddAA(ResamplingMode mode) noexcept { return (mode == SRCMODE_SINC8) ? SRCMODE_SINC8LP : mode; } constexpr ResamplingMode RemoveAA(ResamplingMode mode) noexcept { return (mode == SRCMODE_SINC8LP) ? SRCMODE_SINC8 : mode; } } // Release node defines #define ENV_RELEASE_NODE_UNSET 0xFF #define NOT_YET_RELEASED (-1) static_assert(ENV_RELEASE_NODE_UNSET > MAX_ENVPOINTS); enum PluginPriority { ChannelOnly, InstrumentOnly, PrioritiseInstrument, PrioritiseChannel, }; enum PluginMutePriority { EvenIfMuted, RespectMutes, }; // Plugin velocity handling options enum PlugVelocityHandling : uint8 { PLUGIN_VELOCITYHANDLING_CHANNEL = 0, PLUGIN_VELOCITYHANDLING_VOLUME }; // Plugin volumecommand handling options enum PlugVolumeHandling : uint8 { PLUGIN_VOLUMEHANDLING_MIDI = 0, PLUGIN_VOLUMEHANDLING_DRYWET, PLUGIN_VOLUMEHANDLING_IGNORE, PLUGIN_VOLUMEHANDLING_CUSTOM, PLUGIN_VOLUMEHANDLING_MAX, }; enum MidiChannel : uint8 { MidiNoChannel = 0, MidiFirstChannel = 1, MidiLastChannel = 16, MidiMappedChannel = 17, }; // Vibrato Types enum VibratoType : uint8 { VIB_SINE = 0, VIB_SQUARE, VIB_RAMP_UP, VIB_RAMP_DOWN, VIB_RANDOM }; // Tracker-specific playback behaviour // Note: The index of every flag has to be fixed, so do not remove flags. Always add new flags at the end! enum PlayBehaviour { MSF_COMPATIBLE_PLAY, // No-op - only used during loading (Old general compatibility flag for IT/MPT/XM) kMPTOldSwingBehaviour, // MPT 1.16 swing behaviour (IT/MPT, deprecated) kMIDICCBugEmulation, // Emulate broken volume MIDI CC behaviour (IT/MPT/XM, deprecated) kOldMIDIPitchBends, // Old VST MIDI pitch bend behaviour (IT/MPT/XM, deprecated) kFT2VolumeRamping, // Smooth volume ramping like in FT2 (XM) kMODVBlankTiming, // F21 and above set speed instead of tempo kSlidesAtSpeed1, // Execute normal slides at speed 1 as if they were fine slides kPeriodsAreHertz, // Compute note frequency in Hertz rather than periods kTempoClamp, // Clamp tempo to 32-255 range. kPerChannelGlobalVolSlide, // Global volume slide memory is per-channel kPanOverride, // Panning commands override surround and random pan variation kITInstrWithoutNote, // Avoid instrument handling if there is no note kITVolColFinePortamento, // Volume column portamento never does fine portamento kITArpeggio, // IT arpeggio algorithm kITOutOfRangeDelay, // Out-of-range delay command behaviour in IT kITPortaMemoryShare, // Gxx shares memory with Exx and Fxx kITPatternLoopTargetReset, // After finishing a pattern loop, set the pattern loop target to the next row kITFT2PatternLoop, // Nested pattern loop behaviour kITPingPongNoReset, // Don't reset ping pong direction with instrument numbers kITEnvelopeReset, // IT envelope reset behaviour kITClearOldNoteAfterCut, // Forget the previous note after cutting it kITVibratoTremoloPanbrello, // More IT-like Hxx / hx, Rxx, Yxx and autovibrato handling, including more precise LUTs kITTremor, // Ixx behaves like in IT kITRetrigger, // Qxx behaves like in IT kITMultiSampleBehaviour, // Properly update C-5 frequency when changing in multisampled instrument kITPortaTargetReached, // Clear portamento target after it has been reached kITPatternLoopBreak, // Don't reset loop count on pattern break. kITOffset, // IT-style Oxx edge case handling kITSwingBehaviour, // IT's swing behaviour kITNNAReset, // NNA is reset on every note change, not every instrument change kITSCxStopsSample, // SCx really stops the sample and does not just mute it kITEnvelopePositionHandling, // IT-style envelope position advance + enable/disable behaviour kITPortamentoInstrument, // No sample changes during portamento with Compatible Gxx enabled, instrument envelope reset with portamento kITPingPongMode, // Don't repeat last sample point in ping pong loop, like IT's software mixer kITRealNoteMapping, // Use triggered note rather than translated note for PPS and other effects kITHighOffsetNoRetrig, // SAx should not apply an offset effect to a note next to it kITFilterBehaviour, // User IT's filter coefficients (unless extended filter range is used) kITNoSurroundPan, // Panning and surround are mutually exclusive kITShortSampleRetrig, // Don't retrigger already stopped channels kITPortaNoNote, // Don't apply any portamento if no previous note is playing kITFT2DontResetNoteOffOnPorta, // Only reset note-off status on portamento in IT Compatible Gxx mode kITVolColMemory, // IT volume column effects share their memory with the effect column kITPortamentoSwapResetsPos, // Portamento with sample swap plays the new sample from the beginning kITEmptyNoteMapSlot, // IT ignores instrument note map entries with no note completely kITFirstTickHandling, // IT-style first tick handling kITSampleAndHoldPanbrello, // IT-style sample&hold panbrello waveform kITClearPortaTarget, // New notes reset portamento target in IT kITPanbrelloHold, // Don't reset panbrello effect until next note or panning effect kITPanningReset, // Sample and instrument panning is only applied on note change, not instrument change kITPatternLoopWithJumpsOld, // Bxx on the same row as SBx terminates the loop in IT (old implementation of kITPatternLoopWithJumps) kITInstrWithNoteOff, // Instrument number with note-off recalls default volume kFT2Arpeggio, // FT2 arpeggio algorithm kFT2Retrigger, // Rxx behaves like in FT2 kFT2VolColVibrato, // Vibrato depth in volume column does not actually execute the vibrato effect kFT2PortaNoNote, // Don't play portamento-ed note if no previous note is playing kFT2KeyOff, // FT2-style Kxx handling kFT2PanSlide, // Volume-column pan slides should be handled like fine slides kFT2ST3OffsetOutOfRange, // Offset past sample end stops the note kFT2RestrictXCommand, // Don't allow MPT extensions to Xxx command in XM kFT2RetrigWithNoteDelay, // Retrigger envelopes if there is a note delay with no note kFT2SetPanEnvPos, // Lxx only sets the pan env position if the volume envelope's sustain flag is set kFT2PortaIgnoreInstr, // Portamento plus instrument number applies the volume settings of the new sample, but not the new sample itself. kFT2VolColMemory, // No volume column memory in FT2 kFT2LoopE60Restart, // Next pattern starts on the same row as the last E60 command kFT2ProcessSilentChannels, // Keep processing silent channels for later 3xx pickup kFT2ReloadSampleSettings, // Reload sample settings even if a note-off is placed next to an instrument number kFT2PortaDelay, // Portamento with note delay next to it is ignored in FT2 kFT2Transpose, // Out-of-range transposed notes in FT2 kFT2PatternLoopWithJumps, // Bxx or Dxx on the same row as E6x terminates the loop in FT2 kFT2PortaTargetNoReset, // Portamento target is not reset with new notes in FT2 kFT2EnvelopeEscape, // FT2 sustain point at end of envelope kFT2Tremor, // Txx behaves like in FT2 kFT2OutOfRangeDelay, // Out-of-range delay command behaviour in FT2 kFT2Periods, // Use FT2's broken period handling kFT2PanWithDelayedNoteOff, // Pan command with delayed note-off kFT2VolColDelay, // FT2-style volume column handling if there is a note delay kFT2FinetunePrecision, // Only take the upper 4 bits of sample finetune. kST3NoMutedChannels, // Don't process any effects on muted S3M channels kST3EffectMemory, // Most effects share the same memory in ST3 kST3PortaSampleChange, // Portamento plus instrument number applies the volume settings of the new sample, but not the new sample itself (GUS behaviour). kST3VibratoMemory, // Do not remember vibrato type in effect memory kST3LimitPeriod, // Cut note instead of limiting final period (ModPlug Tracker style) KST3PortaAfterArpeggio, // Portamento after arpeggio continues at the note where the arpeggio left off kMODOneShotLoops, // Allow ProTracker-like oneshot loops kMODIgnorePanning, // Do not process any panning commands kMODSampleSwap, // On-the-fly sample swapping kFT2NoteOffFlags, // Set and reset the correct fade/key-off flags with note-off and instrument number after note-off kITMultiSampleInstrumentNumber, // After portamento to different sample within multi-sampled instrument, lone instrument numbers in patterns always recall the new sample's default settings kRowDelayWithNoteDelay, // Retrigger note delays on every reptition of a row kFT2MODTremoloRampWaveform, // FT2-/ProTracker-compatible tremolo ramp down / triangle waveform kFT2PortaUpDownMemory, // Portamento up and down have separate memory kMODOutOfRangeNoteDelay, // ProTracker behaviour for out-of-range note delays kMODTempoOnSecondTick, // ProTracker sets tempo after the first tick kFT2PanSustainRelease, // If the sustain point of a panning envelope is reached before key-off, FT2 does not escape it anymore kLegacyReleaseNode, // Legacy release node volume processing kOPLBeatingOscillators, // Emulate beating FM oscillators from CDFM / Composer 670 kST3OffsetWithoutInstrument, // Note without instrument uses same offset as previous note kReleaseNodePastSustainBug, // OpenMPT 1.23.01.02 / r4009 broke release nodes past the sustain point, fixed in OpenMPT 1.28 kFT2NoteDelayWithoutInstr, // Sometime between OpenMPT 1.18.03.00 and 1.19.01.00, delayed instrument-less notes in XM started recalling the default sample volume and panning kOPLFlexibleNoteOff, // Full control after note-off over OPL voices, ^^^ sends note cut instead of just note-off kITInstrWithNoteOffOldEffects, // Instrument number with note-off recalls default volume - special cases with Old Effects enabled kMIDIVolumeOnNoteOffBug, // Update MIDI channel volume on note-off (legacy bug emulation) kITDoNotOverrideChannelPan, // Sample / instrument pan does not override channel pan for following samples / instruments that are not panned kITPatternLoopWithJumps, // Bxx right of SBx terminates the loop in IT kITDCTBehaviour, // DCT="Sample" requires sample instrument, DCT="Note" checks old pattern note against new pattern note (previously was checking old pattern note against new translated note) kOPLwithNNA, // NNA note-off / fade are applied to OPL channels kST3RetrigAfterNoteCut, // Qxy does not retrigger note after it has been cut with ^^^ or SCx kST3SampleSwap, // On-the-fly sample swapping (SoundBlaster behaviour) kOPLRealRetrig, // Retrigger effect (Qxy) restarts OPL notes kOPLNoResetAtEnvelopeEnd, // Do not reset OPL channel status at end of envelope (OpenMPT 1.28 inconsistency with samples) kOPLNoteStopWith0Hz, // Set note frequency to 0 Hz to "stop" OPL notes kOPLNoteOffOnNoteChange, // Send note-off events for old note on every note change kFT2PortaResetDirection, // Reset portamento direction when reaching portamento target from below kApplyUpperPeriodLimit, // Enforce m_nMaxPeriod kApplyOffsetWithoutNote, // Offset commands even work when there's no note next to them (e.g. DMF, MDL, PLM formats) kITPitchPanSeparation, // Pitch/Pan Separation can be overridden by panning commands (this also fixes a bug where any "special" notes affect PPS) kImprecisePingPongLoops, // Use old (less precise) ping-pong overshoot calculation // Add new play behaviours here. kMaxPlayBehaviours, }; // Tempo swing determines how much every row in modern tempo mode contributes to a beat. class TempoSwing : public std::vector { public: static constexpr uint32 Unity = 1u << 24; // Normalize the tempo swing coefficients so that they add up to exactly the specified tempo again void Normalize(); void resize(size_type newSize, value_type val = Unity) { std::vector::resize(newSize, val); Normalize(); } static void Serialize(std::ostream &oStrm, const TempoSwing &swing); static void Deserialize(std::istream &iStrm, TempoSwing &swing, const size_t); }; // Sample position and sample position increment value struct SamplePosition { using value_t = int64; using unsigned_value_t = uint64; protected: value_t v = 0; public: static constexpr uint32 fractMax = 0xFFFFFFFFu; MPT_CONSTEXPRINLINE SamplePosition() { } MPT_CONSTEXPRINLINE explicit SamplePosition(value_t pos) : v(pos) { } MPT_CONSTEXPRINLINE SamplePosition(int32 intPart, uint32 fractPart) : v((static_cast(intPart) * (1ll << 32)) | fractPart) { } static SamplePosition Ratio(uint32 dividend, uint32 divisor) { return SamplePosition((static_cast(dividend) << 32) / divisor); } static SamplePosition FromDouble(double pos) { return SamplePosition(static_cast(pos * 4294967296.0)); } double ToDouble() const { return v / 4294967296.0; } // Set integer and fractional part MPT_CONSTEXPRINLINE SamplePosition &Set(int32 intPart, uint32 fractPart = 0) { v = (static_cast(intPart) << 32) | fractPart; return *this; } // Set integer part, keep fractional part MPT_CONSTEXPRINLINE SamplePosition &SetInt(int32 intPart) { v = (static_cast(intPart) << 32) | GetFract(); return *this; } // Get integer part (as sample length / position) MPT_CONSTEXPRINLINE SmpLength GetUInt() const { return static_cast(static_cast(v) >> 32); } // Get integer part MPT_CONSTEXPRINLINE int32 GetInt() const { return static_cast(static_cast(v) >> 32); } // Get fractional part MPT_CONSTEXPRINLINE uint32 GetFract() const { return static_cast(v); } // Get the inverted fractional part MPT_CONSTEXPRINLINE SamplePosition GetInvertedFract() const { return SamplePosition(0x100000000ll - GetFract()); } // Get the raw fixed-point value MPT_CONSTEXPRINLINE int64 GetRaw() const { return v; } // Negate the current value MPT_CONSTEXPRINLINE SamplePosition &Negate() { v = -v; return *this; } // Multiply and divide by given integer scalars MPT_CONSTEXPRINLINE SamplePosition &MulDiv(uint32 mul, uint32 div) { v = (v * mul) / div; return *this; } // Removes the integer part, only keeping fractions MPT_CONSTEXPRINLINE SamplePosition &RemoveInt() { v &= fractMax; return *this; } // Check if value is 1.0 MPT_CONSTEXPRINLINE bool IsUnity() const { return v == 0x100000000ll; } // Check if value is 0 MPT_CONSTEXPRINLINE bool IsZero() const { return v == 0; } // Check if value is > 0 MPT_CONSTEXPRINLINE bool IsPositive() const { return v > 0; } // Check if value is < 0 MPT_CONSTEXPRINLINE bool IsNegative() const { return v < 0; } // Addition / subtraction of another fixed-point number SamplePosition operator+ (const SamplePosition &other) const { return SamplePosition(v + other.v); } SamplePosition operator- (const SamplePosition &other) const { return SamplePosition(v - other.v); } void operator+= (const SamplePosition &other) { v += other.v; } void operator-= (const SamplePosition &other) { v -= other.v; } // Multiplication with integer scalar template SamplePosition operator* (T other) const { return SamplePosition(static_cast(v * other)); } template void operator*= (T other) { v = static_cast(v *other); } // Division by other fractional point number; returns scalar value_t operator/ (SamplePosition other) const { return v / other.v; } // Division by scalar; returns fractional point number SamplePosition operator/ (int div) const { return SamplePosition(v / div); } MPT_CONSTEXPRINLINE bool operator==(const SamplePosition &other) const { return v == other.v; } MPT_CONSTEXPRINLINE bool operator!=(const SamplePosition &other) const { return v != other.v; } MPT_CONSTEXPRINLINE bool operator<=(const SamplePosition &other) const { return v <= other.v; } MPT_CONSTEXPRINLINE bool operator>=(const SamplePosition &other) const { return v >= other.v; } MPT_CONSTEXPRINLINE bool operator<(const SamplePosition &other) const { return v < other.v; } MPT_CONSTEXPRINLINE bool operator>(const SamplePosition &other) const { return v > other.v; } }; // Aaaand another fixed-point type, e.g. used for fractional tempos // Note that this doesn't use classical bit shifting for the fixed point part. // This is mostly for the clarity of stored values and to be able to represent any value .0000 to .9999 properly. // For easier debugging, use the Debugger Visualizers available in build/vs/debug/ // to easily display the stored values. template struct FPInt { protected: T v; MPT_CONSTEXPRINLINE FPInt(T rawValue) : v(rawValue) { } public: enum : size_t { fractFact = FFact }; using store_t = T; MPT_CONSTEXPRINLINE FPInt() : v(0) { } MPT_CONSTEXPRINLINE FPInt(T intPart, T fractPart) : v((intPart * fractFact) + (fractPart % fractFact)) { } explicit MPT_CONSTEXPRINLINE FPInt(float f) : v(mpt::saturate_round(f * float(fractFact))) { } explicit MPT_CONSTEXPRINLINE FPInt(double f) : v(mpt::saturate_round(f * double(fractFact))) { } // Set integer and fractional part MPT_CONSTEXPRINLINE FPInt &Set(T intPart, T fractPart = 0) { v = (intPart * fractFact) + (fractPart % fractFact); return *this; } // Set raw internal representation directly MPT_CONSTEXPRINLINE FPInt &SetRaw(T value) { v = value; return *this; } // Retrieve the integer part of the stored value MPT_CONSTEXPRINLINE T GetInt() const { return v / fractFact; } // Retrieve the fractional part of the stored value MPT_CONSTEXPRINLINE T GetFract() const { return v % fractFact; } // Retrieve the raw internal representation of the stored value MPT_CONSTEXPRINLINE T GetRaw() const { return v; } // Formats the stored value as a floating-point value MPT_CONSTEXPRINLINE double ToDouble() const { return v / double(fractFact); } MPT_CONSTEXPRINLINE friend FPInt operator+ (const FPInt &a, const FPInt &b) noexcept { return FPInt(a.v + b.v); } MPT_CONSTEXPRINLINE friend FPInt operator- (const FPInt &a, const FPInt &b) noexcept { return FPInt(a.v - b.v); } MPT_CONSTEXPRINLINE FPInt operator+= (const FPInt &other) noexcept { v += other.v; return *this; } MPT_CONSTEXPRINLINE FPInt operator-= (const FPInt &other) noexcept { v -= other.v; return *this; } MPT_CONSTEXPRINLINE friend bool operator== (const FPInt &a, const FPInt &b) noexcept { return a.v == b.v; } MPT_CONSTEXPRINLINE friend bool operator!= (const FPInt &a, const FPInt &b) noexcept { return a.v != b.v; } MPT_CONSTEXPRINLINE friend bool operator<= (const FPInt &a, const FPInt &b) noexcept { return a.v <= b.v; } MPT_CONSTEXPRINLINE friend bool operator>= (const FPInt &a, const FPInt &b) noexcept { return a.v >= b.v; } MPT_CONSTEXPRINLINE friend bool operator< (const FPInt &a, const FPInt &b) noexcept { return a.v < b.v; } MPT_CONSTEXPRINLINE friend bool operator> (const FPInt &a, const FPInt &b) noexcept { return a.v > b.v; } }; using TEMPO = FPInt<10000, uint32>; using OPLPatch = std::array; OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Sndfile.cpp0000644000175000017500000016146514175042045020202 00000000000000/* * Sndfile.cpp * ----------- * Purpose: Core class of the playback engine. Every song is represented by a CSoundFile object. * Notes : (currently none) * Authors: Olivier Lapicque * OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #ifdef MODPLUG_TRACKER #include "../mptrack/Mptrack.h" // For CTrackApp::OpenURL #include "../mptrack/TrackerSettings.h" #include "../mptrack/Moddoc.h" #include "../mptrack/Reporting.h" #include "../mptrack/Mainfrm.h" #endif // MODPLUG_TRACKER #ifdef MPT_EXTERNAL_SAMPLES #include "../common/mptFileIO.h" #endif // MPT_EXTERNAL_SAMPLES #include "../common/version.h" #include "../soundlib/AudioCriticalSection.h" #include "../common/serialization_utils.h" #include "Sndfile.h" #include "Tables.h" #include "mod_specifications.h" #include "tuningcollection.h" #include "plugins/PluginManager.h" #include "plugins/PlugInterface.h" #include "../common/mptStringBuffer.h" #include "../common/FileReader.h" #include "Container.h" #include "OPL.h" #include "mpt/io/io.hpp" #include "mpt/io/io_stdstream.hpp" #ifndef NO_ARCHIVE_SUPPORT #include "../unarchiver/unarchiver.h" #endif // NO_ARCHIVE_SUPPORT OPENMPT_NAMESPACE_BEGIN bool SettingCacheCompleteFileBeforeLoading() { #ifdef MODPLUG_TRACKER return TrackerSettings::Instance().MiscCacheCompleteFileBeforeLoading; #else return false; #endif } mpt::ustring FileHistory::AsISO8601() const { tm date = loadDate; if(openTime > 0) { // Calculate the date when editing finished. double openSeconds = static_cast(openTime) / HISTORY_TIMER_PRECISION; tm tmpLoadDate = loadDate; int64 loadDateSinceEpoch = mpt::Date::Unix::FromUTC(tmpLoadDate); int64 saveDateSinceEpoch = loadDateSinceEpoch + mpt::saturate_round(openSeconds); date = mpt::Date::Unix(saveDateSinceEpoch).AsUTC(); } return mpt::Date::ToShortenedISO8601(date); } CSoundFile::PlayState::PlayState() { std::fill(std::begin(Chn), std::end(Chn), ModChannel{}); m_midiMacroScratchSpace.reserve(kMacroLength); // Note: If macros ever become variable-length, the scratch space needs to be at least one byte longer than the longest macro in the file for end-of-SysEx insertion to stay allocation-free in the mixer! } ////////////////////////////////////////////////////////// // CSoundFile #ifdef MODPLUG_TRACKER const NoteName *CSoundFile::m_NoteNames = NoteNamesFlat; #endif CSoundFile::CSoundFile() : #ifndef MODPLUG_TRACKER m_NoteNames(NoteNamesSharp), #endif m_pModSpecs(&ModSpecs::itEx), m_nType(MOD_TYPE_NONE), Patterns(*this), #ifdef MODPLUG_TRACKER m_MIDIMapper(*this), #endif Order(*this), m_PRNG(mpt::make_prng(mpt::global_prng())), m_visitedRows(*this) { MemsetZero(MixSoundBuffer); MemsetZero(MixRearBuffer); MemsetZero(MixFloatBuffer); #ifdef MODPLUG_TRACKER m_bChannelMuteTogglePending.reset(); m_nDefaultRowsPerBeat = m_PlayState.m_nCurrentRowsPerBeat = (TrackerSettings::Instance().m_nRowHighlightBeats) ? TrackerSettings::Instance().m_nRowHighlightBeats : 4; m_nDefaultRowsPerMeasure = m_PlayState.m_nCurrentRowsPerMeasure = (TrackerSettings::Instance().m_nRowHighlightMeasures >= m_nDefaultRowsPerBeat) ? TrackerSettings::Instance().m_nRowHighlightMeasures : m_nDefaultRowsPerBeat * 4; #else m_nDefaultRowsPerBeat = m_PlayState.m_nCurrentRowsPerBeat = 4; m_nDefaultRowsPerMeasure = m_PlayState.m_nCurrentRowsPerMeasure = 16; #endif // MODPLUG_TRACKER MemsetZero(Instruments); Clear(m_szNames); m_pTuningsTuneSpecific = new CTuningCollection(); } CSoundFile::~CSoundFile() { Destroy(); delete m_pTuningsTuneSpecific; m_pTuningsTuneSpecific = nullptr; } void CSoundFile::AddToLog(LogLevel level, const mpt::ustring &text) const { if(m_pCustomLog) { m_pCustomLog->AddToLog(level, text); } else { #ifdef MODPLUG_TRACKER if(GetpModDoc()) GetpModDoc()->AddToLog(level, text); #else MPT_LOG_GLOBAL(level, "soundlib", text); #endif } } // Global variable initializer for loader functions void CSoundFile::InitializeGlobals(MODTYPE type) { // Do not add or change any of these values! And if you do, review each and every loader to check if they require these defaults! m_nType = type; MODTYPE bestType = GetBestSaveFormat(); m_playBehaviour = GetDefaultPlaybackBehaviour(bestType); SetModSpecsPointer(m_pModSpecs, bestType); // Delete instruments in case some previously called loader already created them. for(INSTRUMENTINDEX i = 1; i <= m_nInstruments; i++) { delete Instruments[i]; Instruments[i] = nullptr; } m_ContainerType = MOD_CONTAINERTYPE_NONE; m_nChannels = 0; m_nInstruments = 0; m_nSamples = 0; m_nSamplePreAmp = 48; m_nVSTiVolume = 48; m_OPLVolumeFactor = m_OPLVolumeFactorScale; m_nDefaultSpeed = 6; m_nDefaultTempo.Set(125); m_nDefaultGlobalVolume = MAX_GLOBAL_VOLUME; m_SongFlags.reset(); m_nMinPeriod = 16; m_nMaxPeriod = 32767; m_nResampling = SRCMODE_DEFAULT; m_dwLastSavedWithVersion = Version(0); m_dwCreatedWithVersion = Version(0); SetMixLevels(MixLevels::Compatible); Patterns.ClearPatterns(); Order.Initialize(); m_songName.clear(); m_songArtist.clear(); m_songMessage.clear(); m_modFormat = ModFormatDetails(); m_FileHistory.clear(); m_tempoSwing.clear(); #ifdef MPT_EXTERNAL_SAMPLES m_samplePaths.clear(); #endif // MPT_EXTERNAL_SAMPLES // Note: we do not use the Amiga resampler for DBM as it's a multichannel format and can make use of higher-quality Amiga soundcards instead of Paula. if(GetType() & (/*MOD_TYPE_DBM | */MOD_TYPE_DIGI | MOD_TYPE_MED | MOD_TYPE_MOD | MOD_TYPE_OKT | MOD_TYPE_SFX | MOD_TYPE_STP)) { m_SongFlags.set(SONG_ISAMIGA); } } void CSoundFile::InitializeChannels() { for(CHANNELINDEX nChn = 0; nChn < MAX_BASECHANNELS; nChn++) { InitChannel(nChn); } } struct FileFormatLoader { decltype(CSoundFile::ProbeFileHeaderXM) *prober; decltype(&CSoundFile::ReadXM) loader; }; #ifdef MODPLUG_TRACKER #define MPT_DECLARE_FORMAT(format) { nullptr, &CSoundFile::Read ## format } #else #define MPT_DECLARE_FORMAT(format) { CSoundFile::ProbeFileHeader ## format, &CSoundFile::Read ## format } #endif // All module format loaders, in the order they should be executed. // This order matters, depending on the format, due to some unfortunate // clashes or lack of magic bytes that can lead to mis-detection of some formats. // Apart from that, more common formats with sane magic bytes are also found // at the top of the list to match the most common cases more quickly. static constexpr FileFormatLoader ModuleFormatLoaders[] = { MPT_DECLARE_FORMAT(XM), MPT_DECLARE_FORMAT(IT), MPT_DECLARE_FORMAT(S3M), MPT_DECLARE_FORMAT(STM), MPT_DECLARE_FORMAT(MED), MPT_DECLARE_FORMAT(MTM), MPT_DECLARE_FORMAT(MDL), MPT_DECLARE_FORMAT(DBM), MPT_DECLARE_FORMAT(FAR), MPT_DECLARE_FORMAT(AMS), MPT_DECLARE_FORMAT(AMS2), MPT_DECLARE_FORMAT(OKT), MPT_DECLARE_FORMAT(PTM), MPT_DECLARE_FORMAT(ULT), MPT_DECLARE_FORMAT(DMF), MPT_DECLARE_FORMAT(DSM), MPT_DECLARE_FORMAT(AMF_Asylum), MPT_DECLARE_FORMAT(AMF_DSMI), MPT_DECLARE_FORMAT(PSM), MPT_DECLARE_FORMAT(PSM16), MPT_DECLARE_FORMAT(MT2), MPT_DECLARE_FORMAT(ITP), #if defined(MODPLUG_TRACKER) || defined(MPT_FUZZ_TRACKER) // These make little sense for a module player library MPT_DECLARE_FORMAT(UAX), MPT_DECLARE_FORMAT(WAV), MPT_DECLARE_FORMAT(MID), #endif // MODPLUG_TRACKER || MPT_FUZZ_TRACKER MPT_DECLARE_FORMAT(GDM), MPT_DECLARE_FORMAT(IMF), MPT_DECLARE_FORMAT(DIGI), MPT_DECLARE_FORMAT(DTM), MPT_DECLARE_FORMAT(PLM), MPT_DECLARE_FORMAT(AM), MPT_DECLARE_FORMAT(J2B), MPT_DECLARE_FORMAT(PT36), MPT_DECLARE_FORMAT(SymMOD), MPT_DECLARE_FORMAT(MUS_KM), MPT_DECLARE_FORMAT(FMT), MPT_DECLARE_FORMAT(SFX), MPT_DECLARE_FORMAT(STP), MPT_DECLARE_FORMAT(DSym), MPT_DECLARE_FORMAT(STX), MPT_DECLARE_FORMAT(MOD), MPT_DECLARE_FORMAT(ICE), MPT_DECLARE_FORMAT(669), MPT_DECLARE_FORMAT(C67), MPT_DECLARE_FORMAT(MO3), MPT_DECLARE_FORMAT(M15), }; #undef MPT_DECLARE_FORMAT CSoundFile::ProbeResult CSoundFile::ProbeAdditionalSize(MemoryFileReader &file, const uint64 *pfilesize, uint64 minimumAdditionalSize) { const uint64 availableFileSize = file.GetLength(); const uint64 fileSize = (pfilesize ? *pfilesize : file.GetLength()); //const uint64 validFileSize = std::min(fileSize, static_cast(ProbeRecommendedSize)); const uint64 goalSize = file.GetPosition() + minimumAdditionalSize; //const uint64 goalMinimumSize = std::min(goalSize, static_cast(ProbeRecommendedSize)); if(pfilesize) { if(availableFileSize < std::min(fileSize, static_cast(ProbeRecommendedSize))) { if(availableFileSize < goalSize) { return ProbeWantMoreData; } } else { if(fileSize < goalSize) { return ProbeFailure; } } return ProbeSuccess; } return ProbeSuccess; } #define MPT_DO_PROBE( storedResult , call ) \ do { \ ProbeResult lastResult = call ; \ if(lastResult == ProbeSuccess) { \ return ProbeSuccess; \ } else if(lastResult == ProbeWantMoreData) { \ storedResult = ProbeWantMoreData; \ } \ } while(0) \ /**/ CSoundFile::ProbeResult CSoundFile::Probe(ProbeFlags flags, mpt::span data, const uint64 *pfilesize) { ProbeResult result = ProbeFailure; if(pfilesize && (*pfilesize < data.size())) { throw std::out_of_range(""); } if(!data.data()) { throw std::invalid_argument(""); } MemoryFileReader file(data); if(flags & ProbeContainers) { #if !defined(MPT_WITH_ANCIENT) MPT_DO_PROBE(result, ProbeFileHeaderMMCMP(file, pfilesize)); MPT_DO_PROBE(result, ProbeFileHeaderPP20(file, pfilesize)); MPT_DO_PROBE(result, ProbeFileHeaderXPK(file, pfilesize)); #endif // !MPT_WITH_ANCIENT MPT_DO_PROBE(result, ProbeFileHeaderUMX(file, pfilesize)); } if(flags & ProbeModules) { for(const auto &format : ModuleFormatLoaders) { if(format.prober != nullptr) { MPT_DO_PROBE(result, format.prober(file, pfilesize)); } } } if(pfilesize) { if((result == ProbeWantMoreData) && (mpt::saturate_cast(*pfilesize) <= data.size())) { // If the prober wants more data but we already reached EOF, // probing must fail. result = ProbeFailure; } } else { if((result == ProbeWantMoreData) && (data.size() >= ProbeRecommendedSize)) { // If the prober wants more daat but we already provided the recommended required maximum, // just return success as this is the best we can do for the suggestesd probing size. result = ProbeSuccess; } } return result; } bool CSoundFile::Create(FileReader file, ModLoadingFlags loadFlags, CModDoc *pModDoc) { m_nMixChannels = 0; #ifdef MODPLUG_TRACKER m_pModDoc = pModDoc; #else MPT_UNUSED(pModDoc); m_nFreqFactor = m_nTempoFactor = 65536; #endif // MODPLUG_TRACKER Clear(m_szNames); #ifndef NO_PLUGINS std::fill(std::begin(m_MixPlugins), std::end(m_MixPlugins), SNDMIXPLUGIN()); #endif // NO_PLUGINS if(CreateInternal(file, loadFlags)) return true; #ifndef NO_ARCHIVE_SUPPORT if(!(loadFlags & skipContainer) && file.IsValid()) { CUnarchiver unarchiver(file); if(unarchiver.ExtractBestFile(GetSupportedExtensions(true))) { if(CreateInternal(unarchiver.GetOutputFile(), loadFlags)) { // Read archive comment if there is no song comment if(m_songMessage.empty()) { m_songMessage.assign(mpt::ToCharset(mpt::Charset::Locale, unarchiver.GetComment())); } return true; } } } #endif return false; } bool CSoundFile::CreateInternal(FileReader file, ModLoadingFlags loadFlags) { if(file.IsValid()) { std::vector containerItems; MODCONTAINERTYPE packedContainerType = MOD_CONTAINERTYPE_NONE; if(!(loadFlags & skipContainer)) { ContainerLoadingFlags containerLoadFlags = (loadFlags == onlyVerifyHeader) ? ContainerOnlyVerifyHeader : ContainerUnwrapData; #if !defined(MPT_WITH_ANCIENT) if(packedContainerType == MOD_CONTAINERTYPE_NONE && UnpackXPK(containerItems, file, containerLoadFlags)) packedContainerType = MOD_CONTAINERTYPE_XPK; if(packedContainerType == MOD_CONTAINERTYPE_NONE && UnpackPP20(containerItems, file, containerLoadFlags)) packedContainerType = MOD_CONTAINERTYPE_PP20; if(packedContainerType == MOD_CONTAINERTYPE_NONE && UnpackMMCMP(containerItems, file, containerLoadFlags)) packedContainerType = MOD_CONTAINERTYPE_MMCMP; #endif // !MPT_WITH_ANCIENT if(packedContainerType == MOD_CONTAINERTYPE_NONE && UnpackUMX(containerItems, file, containerLoadFlags)) packedContainerType = MOD_CONTAINERTYPE_UMX; if(packedContainerType != MOD_CONTAINERTYPE_NONE) { if(loadFlags == onlyVerifyHeader) { return true; } if(!containerItems.empty()) { // cppcheck false-positive // cppcheck-suppress containerOutOfBounds file = containerItems[0].file; } } } if(loadFlags & skipModules) { return false; } // Try all module format loaders bool loaderSuccess = false; for(const auto &format : ModuleFormatLoaders) { loaderSuccess = (this->*(format.loader))(file, loadFlags); if(loaderSuccess) break; } if(!loaderSuccess) { m_nType = MOD_TYPE_NONE; m_ContainerType = MOD_CONTAINERTYPE_NONE; } if(loadFlags == onlyVerifyHeader) { return loaderSuccess; } if(packedContainerType != MOD_CONTAINERTYPE_NONE && m_ContainerType == MOD_CONTAINERTYPE_NONE) { m_ContainerType = packedContainerType; } m_visitedRows.Initialize(true); } else { // New song InitializeGlobals(); m_visitedRows.Initialize(true); m_dwCreatedWithVersion = Version::Current(); } // Adjust channels const auto muteFlag = GetChannelMuteFlag(); for(CHANNELINDEX chn = 0; chn < MAX_BASECHANNELS; chn++) { LimitMax(ChnSettings[chn].nVolume, uint16(64)); if(ChnSettings[chn].nPan > 256) ChnSettings[chn].nPan = 128; if(ChnSettings[chn].nMixPlugin > MAX_MIXPLUGINS) ChnSettings[chn].nMixPlugin = 0; m_PlayState.Chn[chn].Reset(ModChannel::resetTotal, *this, chn, muteFlag); } // Checking samples, load external samples for(SAMPLEINDEX nSmp = 1; nSmp <= m_nSamples; nSmp++) { ModSample &sample = Samples[nSmp]; #ifdef MPT_EXTERNAL_SAMPLES if(SampleHasPath(nSmp)) { mpt::PathString filename = GetSamplePath(nSmp); if(file.GetOptionalFileName()) { filename = filename.RelativePathToAbsolute(file.GetOptionalFileName()->GetPath()); } else if(GetpModDoc() != nullptr) { filename = filename.RelativePathToAbsolute(GetpModDoc()->GetPathNameMpt().GetPath()); } filename = filename.Simplify(); if(!LoadExternalSample(nSmp, filename)) { #ifndef MODPLUG_TRACKER // OpenMPT has its own way of reporting this error in CModDoc. AddToLog(LogError, MPT_UFORMAT("Unable to load sample {}: {}")(i, filename.ToUnicode())); #endif // MODPLUG_TRACKER } } else { sample.uFlags.reset(SMP_KEEPONDISK); } #endif // MPT_EXTERNAL_SAMPLES if(sample.HasSampleData()) { sample.PrecomputeLoops(*this, false); } else if(!sample.uFlags[SMP_KEEPONDISK]) { sample.nLength = 0; sample.nLoopStart = 0; sample.nLoopEnd = 0; sample.nSustainStart = 0; sample.nSustainEnd = 0; sample.uFlags.reset(CHN_LOOP | CHN_PINGPONGLOOP | CHN_SUSTAINLOOP | CHN_PINGPONGSUSTAIN); } if(sample.nGlobalVol > 64) sample.nGlobalVol = 64; if(sample.uFlags[CHN_ADLIB] && m_opl == nullptr) InitOPL(); } // Check invalid instruments INSTRUMENTINDEX maxInstr = 0; for(INSTRUMENTINDEX i = 0; i <= m_nInstruments; i++) { if(Instruments[i] != nullptr) { maxInstr = i; Instruments[i]->Sanitize(GetType()); } } m_nInstruments = maxInstr; // Set default play state values if(!m_nDefaultTempo.GetInt()) m_nDefaultTempo.Set(125); else LimitMax(m_nDefaultTempo, TEMPO(uint16_max, 0)); if(!m_nDefaultSpeed) m_nDefaultSpeed = 6; if(m_nDefaultRowsPerMeasure < m_nDefaultRowsPerBeat) m_nDefaultRowsPerMeasure = m_nDefaultRowsPerBeat; LimitMax(m_nDefaultRowsPerBeat, MAX_ROWS_PER_BEAT); LimitMax(m_nDefaultRowsPerMeasure, MAX_ROWS_PER_BEAT); LimitMax(m_nDefaultGlobalVolume, MAX_GLOBAL_VOLUME); if(!m_tempoSwing.empty()) m_tempoSwing.resize(m_nDefaultRowsPerBeat); m_PlayState.m_nMusicSpeed = m_nDefaultSpeed; m_PlayState.m_nMusicTempo = m_nDefaultTempo; m_PlayState.m_nCurrentRowsPerBeat = m_nDefaultRowsPerBeat; m_PlayState.m_nCurrentRowsPerMeasure = m_nDefaultRowsPerMeasure; m_PlayState.m_nGlobalVolume = static_cast(m_nDefaultGlobalVolume); m_PlayState.ResetGlobalVolumeRamping(); m_PlayState.m_nNextOrder = 0; m_PlayState.m_nCurrentOrder = 0; m_PlayState.m_nPattern = 0; m_PlayState.m_nBufferCount = 0; m_PlayState.m_dBufferDiff = 0; m_PlayState.m_nTickCount = TICKS_ROW_FINISHED; m_PlayState.m_nNextRow = 0; m_PlayState.m_nRow = 0; m_PlayState.m_nPatternDelay = 0; m_PlayState.m_nFrameDelay = 0; m_PlayState.m_nextPatStartRow = 0; m_PlayState.m_nSeqOverride = ORDERINDEX_INVALID; if(UseFinetuneAndTranspose()) m_playBehaviour.reset(kPeriodsAreHertz); m_nMaxOrderPosition = 0; RecalculateSamplesPerTick(); for(auto &order : Order) { order.Shrink(); if(order.GetRestartPos() >= order.size()) { order.SetRestartPos(0); } } if(GetType() == MOD_TYPE_NONE) { return false; } SetModSpecsPointer(m_pModSpecs, GetBestSaveFormat()); // When reading a file made with an older version of MPT, it might be necessary to upgrade some settings automatically. if(m_dwLastSavedWithVersion) { UpgradeModule(); } #ifndef NO_PLUGINS // Load plugins #ifdef MODPLUG_TRACKER mpt::ustring notFoundText; #endif // MODPLUG_TRACKER std::vector notFoundIDs; if((loadFlags & (loadPluginData | loadPluginInstance)) == (loadPluginData | loadPluginInstance)) { for(PLUGINDEX plug = 0; plug < MAX_MIXPLUGINS; plug++) { auto &plugin = m_MixPlugins[plug]; if(plugin.IsValidPlugin()) { #ifdef MODPLUG_TRACKER // Provide some visual feedback { mpt::ustring s = MPT_UFORMAT("Loading Plugin FX{}: {} ({})")( mpt::ufmt::dec0<2>(plug + 1), mpt::ToUnicode(mpt::Charset::UTF8, plugin.Info.szLibraryName), mpt::ToUnicode(mpt::Charset::Locale, plugin.Info.szName)); CMainFrame::GetMainFrame()->SetHelpText(mpt::ToCString(s)); } #endif // MODPLUG_TRACKER CreateMixPluginProc(plugin, *this); if(plugin.pMixPlugin) { // Plugin was found plugin.pMixPlugin->RestoreAllParameters(plugin.defaultProgram); } else { // Plugin not found - add to list bool found = std::find_if(notFoundIDs.cbegin(), notFoundIDs.cend(), [&plugin](const SNDMIXPLUGININFO *info) { return info->dwPluginId2 == plugin.Info.dwPluginId2 && info->dwPluginId1 == plugin.Info.dwPluginId1; }) != notFoundIDs.cend(); if(!found) { notFoundIDs.push_back(&plugin.Info); #ifdef MODPLUG_TRACKER notFoundText.append(plugin.GetLibraryName()); notFoundText.append(UL_("\n")); #else AddToLog(LogWarning, U_("Plugin not found: ") + plugin.GetLibraryName()); #endif // MODPLUG_TRACKER } } } } } // Set up mix levels (also recalculates plugin mix levels - must be done after plugins were loaded) SetMixLevels(m_nMixLevels); #ifdef MODPLUG_TRACKER // Display a nice message so the user sees which plugins are missing // TODO: Use IDD_MODLOADING_WARNINGS dialog (NON-MODAL!) to display all warnings that are encountered when loading a module. if(!notFoundIDs.empty()) { if(notFoundIDs.size() == 1) { notFoundText = UL_("The following plugin has not been found:\n\n") + notFoundText + UL_("\nDo you want to search for it online?"); } else { notFoundText = UL_("The following plugins have not been found:\n\n") + notFoundText + UL_("\nDo you want to search for them online?"); } if(Reporting::Confirm(notFoundText, U_("OpenMPT - Plugins missing"), false, true) == cnfYes) { mpt::ustring url = U_("https://resources.openmpt.org/plugins/search.php?p="); for(const auto &id : notFoundIDs) { url += mpt::ufmt::HEX0<8>(id->dwPluginId2.get()); url += mpt::ToUnicode(mpt::Charset::UTF8, id->szLibraryName); url += UL_("%0a"); } CTrackApp::OpenURL(mpt::PathString::FromUnicode(url)); } } #endif // MODPLUG_TRACKER #endif // NO_PLUGINS return true; } bool CSoundFile::Destroy() { for(auto &chn : m_PlayState.Chn) { chn.pModInstrument = nullptr; chn.pModSample = nullptr; chn.pCurrentSample = nullptr; chn.nLength = 0; } Patterns.DestroyPatterns(); m_songName.clear(); m_songArtist.clear(); m_songMessage.clear(); m_FileHistory.clear(); #ifdef MPT_EXTERNAL_SAMPLES m_samplePaths.clear(); #endif // MPT_EXTERNAL_SAMPLES for(auto &smp : Samples) { smp.FreeSample(); } for(auto &ins : Instruments) { delete ins; ins = nullptr; } #ifndef NO_PLUGINS for(auto &plug : m_MixPlugins) { plug.Destroy(); } #endif // NO_PLUGINS m_nType = MOD_TYPE_NONE; m_ContainerType = MOD_CONTAINERTYPE_NONE; m_nChannels = m_nSamples = m_nInstruments = 0; return true; } ////////////////////////////////////////////////////////////////////////// // Misc functions void CSoundFile::SetDspEffects(uint32 DSPMask) { m_MixerSettings.DSPMask = DSPMask; InitPlayer(false); } void CSoundFile::SetPreAmp(uint32 nVol) { if (nVol < 1) nVol = 1; if (nVol > 0x200) nVol = 0x200; // x4 maximum #ifndef NO_AGC if ((nVol < m_MixerSettings.m_nPreAmp) && (nVol) && (m_MixerSettings.DSPMask & SNDDSP_AGC)) { m_AGC.Adjust(m_MixerSettings.m_nPreAmp, nVol); } #endif m_MixerSettings.m_nPreAmp = nVol; } double CSoundFile::GetCurrentBPM() const { double bpm; if (m_nTempoMode == TempoMode::Modern) { // With modern mode, we trust that true bpm is close enough to what user chose. // This avoids oscillation due to tick-to-tick corrections. bpm = m_PlayState.m_nMusicTempo.ToDouble(); } else { //with other modes, we calculate it: double ticksPerBeat = m_PlayState.m_nMusicSpeed * m_PlayState.m_nCurrentRowsPerBeat; //ticks/beat = ticks/row * rows/beat double samplesPerBeat = m_PlayState.m_nSamplesPerTick * ticksPerBeat; //samps/beat = samps/tick * ticks/beat bpm = m_MixerSettings.gdwMixingFreq / samplesPerBeat * 60; //beats/sec = samps/sec / samps/beat } //beats/min = beats/sec * 60 return bpm; } void CSoundFile::ResetPlayPos() { const auto muteFlag = GetChannelMuteFlag(); for(CHANNELINDEX i = 0; i < MAX_CHANNELS; i++) m_PlayState.Chn[i].Reset(ModChannel::resetSetPosFull, *this, i, muteFlag); m_visitedRows.Initialize(true); m_SongFlags.reset(SONG_FADINGSONG | SONG_ENDREACHED); m_PlayState.m_nGlobalVolume = m_nDefaultGlobalVolume; m_PlayState.m_nMusicSpeed = m_nDefaultSpeed; m_PlayState.m_nMusicTempo = m_nDefaultTempo; // Do not ramp global volume when starting playback m_PlayState.ResetGlobalVolumeRamping(); m_PlayState.m_nNextOrder = 0; m_PlayState.m_nNextRow = 0; m_PlayState.m_nTickCount = TICKS_ROW_FINISHED; m_PlayState.m_nBufferCount = 0; m_PlayState.m_nPatternDelay = 0; m_PlayState.m_nFrameDelay = 0; m_PlayState.m_nextPatStartRow = 0; m_PlayState.m_lTotalSampleCount = 0; } void CSoundFile::SetCurrentOrder(ORDERINDEX nOrder) { while(nOrder < Order().size() && !Order().IsValidPat(nOrder)) nOrder++; if(nOrder >= Order().size()) return; for(auto &chn : m_PlayState.Chn) { chn.nPeriod = 0; chn.nNote = NOTE_NONE; chn.nPortamentoDest = 0; chn.nCommand = 0; chn.nPatternLoopCount = 0; chn.nPatternLoop = 0; chn.nVibratoPos = chn.nTremoloPos = chn.nPanbrelloPos = 0; //IT compatibility 15. Retrigger if(m_playBehaviour[kITRetrigger]) { chn.nRetrigCount = 0; chn.nRetrigParam = 1; } chn.nTremorCount = 0; } #ifndef NO_PLUGINS // Stop hanging notes from VST instruments as well StopAllVsti(); #endif // NO_PLUGINS if (!nOrder) { ResetPlayPos(); } else { m_PlayState.m_nNextOrder = nOrder; m_PlayState.m_nRow = m_PlayState.m_nNextRow = 0; m_PlayState.m_nPattern = 0; m_PlayState.m_nTickCount = TICKS_ROW_FINISHED; m_PlayState.m_nBufferCount = 0; m_PlayState.m_nPatternDelay = 0; m_PlayState.m_nFrameDelay = 0; m_PlayState.m_nextPatStartRow = 0; } m_SongFlags.reset(SONG_FADINGSONG | SONG_ENDREACHED); } void CSoundFile::SuspendPlugins() { #ifndef NO_PLUGINS for(auto &plug : m_MixPlugins) { IMixPlugin *pPlugin = plug.pMixPlugin; if(pPlugin != nullptr && pPlugin->IsResumed()) { pPlugin->NotifySongPlaying(false); pPlugin->HardAllNotesOff(); pPlugin->Suspend(); } } #endif // NO_PLUGINS } void CSoundFile::ResumePlugins() { #ifndef NO_PLUGINS for(auto &plugin : m_MixPlugins) { IMixPlugin *pPlugin = plugin.pMixPlugin; if(pPlugin != nullptr && !pPlugin->IsResumed()) { pPlugin->NotifySongPlaying(true); pPlugin->Resume(); } } #endif // NO_PLUGINS } void CSoundFile::StopAllVsti() { #ifndef NO_PLUGINS for(auto &plugin : m_MixPlugins) { IMixPlugin *pPlugin = plugin.pMixPlugin; if(pPlugin != nullptr && pPlugin->IsResumed()) { pPlugin->HardAllNotesOff(); } } #endif // NO_PLUGINS } void CSoundFile::SetMixLevels(MixLevels levels) { m_nMixLevels = levels; m_PlayConfig.SetMixLevels(m_nMixLevels); RecalculateGainForAllPlugs(); } void CSoundFile::RecalculateGainForAllPlugs() { #ifndef NO_PLUGINS for(auto &plugin : m_MixPlugins) { if(plugin.pMixPlugin != nullptr) plugin.pMixPlugin->RecalculateGain(); } #endif // NO_PLUGINS } void CSoundFile::ResetChannels() { m_SongFlags.reset(SONG_FADINGSONG | SONG_ENDREACHED); m_PlayState.m_nBufferCount = 0; for(auto &chn : m_PlayState.Chn) { chn.nROfs = chn.nLOfs = 0; chn.nLength = 0; if(chn.dwFlags[CHN_ADLIB] && m_opl) { CHANNELINDEX c = static_cast(std::distance(std::begin(m_PlayState.Chn), &chn)); m_opl->NoteCut(c); } } } #ifdef MODPLUG_TRACKER void CSoundFile::PatternTranstionChnSolo(const CHANNELINDEX chnIndex) { if(chnIndex >= m_nChannels) return; for(CHANNELINDEX i = 0; i < m_nChannels; i++) { m_bChannelMuteTogglePending[i] = !ChnSettings[i].dwFlags[CHN_MUTE]; } m_bChannelMuteTogglePending[chnIndex] = ChnSettings[chnIndex].dwFlags[CHN_MUTE]; } void CSoundFile::PatternTransitionChnUnmuteAll() { for(CHANNELINDEX i = 0; i < m_nChannels; i++) { m_bChannelMuteTogglePending[i] = ChnSettings[i].dwFlags[CHN_MUTE]; } } #endif // MODPLUG_TRACKER void CSoundFile::LoopPattern(PATTERNINDEX nPat, ROWINDEX nRow) { if(!Patterns.IsValidPat(nPat)) { m_SongFlags.reset(SONG_PATTERNLOOP); } else { if(nRow >= Patterns[nPat].GetNumRows()) nRow = 0; m_PlayState.m_nPattern = nPat; m_PlayState.m_nRow = m_PlayState.m_nNextRow = nRow; m_PlayState.m_nTickCount = TICKS_ROW_FINISHED; m_PlayState.m_nPatternDelay = 0; m_PlayState.m_nFrameDelay = 0; m_PlayState.m_nextPatStartRow = 0; m_SongFlags.set(SONG_PATTERNLOOP); } m_PlayState.m_nBufferCount = 0; } void CSoundFile::DontLoopPattern(PATTERNINDEX nPat, ROWINDEX nRow) { if(!Patterns.IsValidPat(nPat)) nPat = 0; if(nRow >= Patterns[nPat].GetNumRows()) nRow = 0; m_PlayState.m_nPattern = nPat; m_PlayState.m_nRow = m_PlayState.m_nNextRow = nRow; m_PlayState.m_nTickCount = TICKS_ROW_FINISHED; m_PlayState.m_nPatternDelay = 0; m_PlayState.m_nFrameDelay = 0; m_PlayState.m_nBufferCount = 0; m_PlayState.m_nextPatStartRow = 0; m_SongFlags.reset(SONG_PATTERNLOOP); } void CSoundFile::SetDefaultPlaybackBehaviour(MODTYPE type) { m_playBehaviour = GetDefaultPlaybackBehaviour(type); } PlayBehaviourSet CSoundFile::GetSupportedPlaybackBehaviour(MODTYPE type) { PlayBehaviourSet playBehaviour; switch(type) { case MOD_TYPE_MPT: case MOD_TYPE_IT: playBehaviour.set(MSF_COMPATIBLE_PLAY); playBehaviour.set(kPeriodsAreHertz); playBehaviour.set(kTempoClamp); playBehaviour.set(kPerChannelGlobalVolSlide); playBehaviour.set(kPanOverride); playBehaviour.set(kITInstrWithoutNote); playBehaviour.set(kITVolColFinePortamento); playBehaviour.set(kITArpeggio); playBehaviour.set(kITOutOfRangeDelay); playBehaviour.set(kITPortaMemoryShare); playBehaviour.set(kITPatternLoopTargetReset); playBehaviour.set(kITFT2PatternLoop); playBehaviour.set(kITPingPongNoReset); playBehaviour.set(kITEnvelopeReset); playBehaviour.set(kITClearOldNoteAfterCut); playBehaviour.set(kITVibratoTremoloPanbrello); playBehaviour.set(kITTremor); playBehaviour.set(kITRetrigger); playBehaviour.set(kITMultiSampleBehaviour); playBehaviour.set(kITPortaTargetReached); playBehaviour.set(kITPatternLoopBreak); playBehaviour.set(kITOffset); playBehaviour.set(kITSwingBehaviour); playBehaviour.set(kITNNAReset); playBehaviour.set(kITSCxStopsSample); playBehaviour.set(kITEnvelopePositionHandling); playBehaviour.set(kITPortamentoInstrument); playBehaviour.set(kITPingPongMode); playBehaviour.set(kITRealNoteMapping); playBehaviour.set(kITHighOffsetNoRetrig); playBehaviour.set(kITFilterBehaviour); playBehaviour.set(kITNoSurroundPan); playBehaviour.set(kITShortSampleRetrig); playBehaviour.set(kITPortaNoNote); playBehaviour.set(kITFT2DontResetNoteOffOnPorta); playBehaviour.set(kITVolColMemory); playBehaviour.set(kITPortamentoSwapResetsPos); playBehaviour.set(kITEmptyNoteMapSlot); playBehaviour.set(kITFirstTickHandling); playBehaviour.set(kITSampleAndHoldPanbrello); playBehaviour.set(kITClearPortaTarget); playBehaviour.set(kITPanbrelloHold); playBehaviour.set(kITPanningReset); playBehaviour.set(kITPatternLoopWithJumps); playBehaviour.set(kITInstrWithNoteOff); playBehaviour.set(kITMultiSampleInstrumentNumber); playBehaviour.set(kRowDelayWithNoteDelay); playBehaviour.set(kITInstrWithNoteOffOldEffects); playBehaviour.set(kITDoNotOverrideChannelPan); playBehaviour.set(kITDCTBehaviour); playBehaviour.set(kITPitchPanSeparation); if(type == MOD_TYPE_MPT) { playBehaviour.set(kOPLFlexibleNoteOff); playBehaviour.set(kOPLwithNNA); playBehaviour.set(kOPLNoteOffOnNoteChange); } break; case MOD_TYPE_XM: playBehaviour.set(MSF_COMPATIBLE_PLAY); playBehaviour.set(kFT2VolumeRamping); playBehaviour.set(kTempoClamp); playBehaviour.set(kPerChannelGlobalVolSlide); playBehaviour.set(kPanOverride); playBehaviour.set(kITFT2PatternLoop); playBehaviour.set(kITFT2DontResetNoteOffOnPorta); playBehaviour.set(kFT2Arpeggio); playBehaviour.set(kFT2Retrigger); playBehaviour.set(kFT2VolColVibrato); playBehaviour.set(kFT2PortaNoNote); playBehaviour.set(kFT2KeyOff); playBehaviour.set(kFT2PanSlide); playBehaviour.set(kFT2ST3OffsetOutOfRange); playBehaviour.set(kFT2RestrictXCommand); playBehaviour.set(kFT2RetrigWithNoteDelay); playBehaviour.set(kFT2SetPanEnvPos); playBehaviour.set(kFT2PortaIgnoreInstr); playBehaviour.set(kFT2VolColMemory); playBehaviour.set(kFT2LoopE60Restart); playBehaviour.set(kFT2ProcessSilentChannels); playBehaviour.set(kFT2ReloadSampleSettings); playBehaviour.set(kFT2PortaDelay); playBehaviour.set(kFT2Transpose); playBehaviour.set(kFT2PatternLoopWithJumps); playBehaviour.set(kFT2PortaTargetNoReset); playBehaviour.set(kFT2EnvelopeEscape); playBehaviour.set(kFT2Tremor); playBehaviour.set(kFT2OutOfRangeDelay); playBehaviour.set(kFT2Periods); playBehaviour.set(kFT2PanWithDelayedNoteOff); playBehaviour.set(kFT2VolColDelay); playBehaviour.set(kFT2FinetunePrecision); playBehaviour.set(kFT2NoteOffFlags); playBehaviour.set(kRowDelayWithNoteDelay); playBehaviour.set(kFT2MODTremoloRampWaveform); playBehaviour.set(kFT2PortaUpDownMemory); playBehaviour.set(kFT2PanSustainRelease); playBehaviour.set(kFT2NoteDelayWithoutInstr); playBehaviour.set(kFT2PortaResetDirection); break; case MOD_TYPE_S3M: playBehaviour.set(MSF_COMPATIBLE_PLAY); playBehaviour.set(kTempoClamp); playBehaviour.set(kPanOverride); playBehaviour.set(kITPanbrelloHold); playBehaviour.set(kFT2ST3OffsetOutOfRange); playBehaviour.set(kST3NoMutedChannels); playBehaviour.set(kST3PortaSampleChange); playBehaviour.set(kST3EffectMemory); playBehaviour.set(kST3VibratoMemory); playBehaviour.set(KST3PortaAfterArpeggio); playBehaviour.set(kRowDelayWithNoteDelay); playBehaviour.set(kST3OffsetWithoutInstrument); playBehaviour.set(kST3RetrigAfterNoteCut); playBehaviour.set(kST3SampleSwap); playBehaviour.set(kOPLNoteOffOnNoteChange); playBehaviour.set(kApplyUpperPeriodLimit); break; case MOD_TYPE_MOD: playBehaviour.set(kMODVBlankTiming); playBehaviour.set(kMODOneShotLoops); playBehaviour.set(kMODIgnorePanning); playBehaviour.set(kMODSampleSwap); playBehaviour.set(kMODOutOfRangeNoteDelay); playBehaviour.set(kMODTempoOnSecondTick); playBehaviour.set(kRowDelayWithNoteDelay); playBehaviour.set(kFT2MODTremoloRampWaveform); break; default: playBehaviour.set(MSF_COMPATIBLE_PLAY); playBehaviour.set(kPeriodsAreHertz); playBehaviour.set(kTempoClamp); playBehaviour.set(kPanOverride); break; } return playBehaviour; } PlayBehaviourSet CSoundFile::GetDefaultPlaybackBehaviour(MODTYPE type) { PlayBehaviourSet playBehaviour; switch(type) { case MOD_TYPE_MPT: playBehaviour.set(kPeriodsAreHertz); playBehaviour.set(kPerChannelGlobalVolSlide); playBehaviour.set(kPanOverride); playBehaviour.set(kITArpeggio); playBehaviour.set(kITPortaMemoryShare); playBehaviour.set(kITPatternLoopTargetReset); playBehaviour.set(kITFT2PatternLoop); playBehaviour.set(kITPingPongNoReset); playBehaviour.set(kITClearOldNoteAfterCut); playBehaviour.set(kITVibratoTremoloPanbrello); playBehaviour.set(kITMultiSampleBehaviour); playBehaviour.set(kITPortaTargetReached); playBehaviour.set(kITPatternLoopBreak); playBehaviour.set(kITSwingBehaviour); playBehaviour.set(kITSCxStopsSample); playBehaviour.set(kITEnvelopePositionHandling); playBehaviour.set(kITPingPongMode); playBehaviour.set(kITRealNoteMapping); playBehaviour.set(kITPortaNoNote); playBehaviour.set(kITVolColMemory); playBehaviour.set(kITFirstTickHandling); playBehaviour.set(kITClearPortaTarget); playBehaviour.set(kITSampleAndHoldPanbrello); playBehaviour.set(kITPanbrelloHold); playBehaviour.set(kITPanningReset); playBehaviour.set(kITInstrWithNoteOff); playBehaviour.set(kOPLFlexibleNoteOff); playBehaviour.set(kITDoNotOverrideChannelPan); playBehaviour.set(kITDCTBehaviour); playBehaviour.set(kOPLwithNNA); playBehaviour.set(kITPitchPanSeparation); break; case MOD_TYPE_S3M: playBehaviour = GetSupportedPlaybackBehaviour(type); // Default behaviour was chosen to follow GUS, so kST3PortaSampleChange is enabled and kST3SampleSwap is disabled. // For SoundBlaster behaviour, those two flags would need to be swapped. playBehaviour.reset(kST3SampleSwap); break; case MOD_TYPE_XM: playBehaviour = GetSupportedPlaybackBehaviour(type); // Only set this explicitely for FT2-made XMs. playBehaviour.reset(kFT2VolumeRamping); break; case MOD_TYPE_MOD: playBehaviour.set(kRowDelayWithNoteDelay); break; default: playBehaviour = GetSupportedPlaybackBehaviour(type); break; } return playBehaviour; } MODTYPE CSoundFile::GetBestSaveFormat() const { switch(GetType()) { case MOD_TYPE_MOD: case MOD_TYPE_S3M: case MOD_TYPE_XM: case MOD_TYPE_IT: case MOD_TYPE_MPT: return GetType(); case MOD_TYPE_AMF0: case MOD_TYPE_DIGI: case MOD_TYPE_SFX: case MOD_TYPE_STP: return MOD_TYPE_MOD; case MOD_TYPE_MED: if(!m_nInstruments) { for(const auto &pat : Patterns) { if(pat.IsValid() && pat.GetNumRows() != 64) return MOD_TYPE_XM; } return MOD_TYPE_MOD; } return MOD_TYPE_XM; case MOD_TYPE_PSM: if(GetNumChannels() > 16) return MOD_TYPE_IT; for(CHANNELINDEX i = 0; i < GetNumChannels(); i++) { if(ChnSettings[i].dwFlags[CHN_SURROUND] || ChnSettings[i].nVolume != 64) { return MOD_TYPE_IT; break; } } return MOD_TYPE_S3M; case MOD_TYPE_669: case MOD_TYPE_FAR: case MOD_TYPE_STM: case MOD_TYPE_DSM: case MOD_TYPE_AMF: case MOD_TYPE_MTM: return MOD_TYPE_S3M; case MOD_TYPE_AMS: case MOD_TYPE_DMF: case MOD_TYPE_DBM: case MOD_TYPE_IMF: case MOD_TYPE_J2B: case MOD_TYPE_ULT: case MOD_TYPE_OKT: case MOD_TYPE_MT2: case MOD_TYPE_MDL: case MOD_TYPE_PTM: case MOD_TYPE_DTM: default: return MOD_TYPE_IT; case MOD_TYPE_MID: return MOD_TYPE_MPT; } } const char *CSoundFile::GetSampleName(SAMPLEINDEX nSample) const { MPT_ASSERT(nSample <= GetNumSamples()); if (nSample < MAX_SAMPLES) { return m_szNames[nSample].buf; } else { return ""; } } const char *CSoundFile::GetInstrumentName(INSTRUMENTINDEX nInstr) const { if((nInstr >= MAX_INSTRUMENTS) || (!Instruments[nInstr])) return ""; MPT_ASSERT(nInstr <= GetNumInstruments()); return Instruments[nInstr]->name.buf; } bool CSoundFile::InitChannel(CHANNELINDEX nChn) { if(nChn >= MAX_BASECHANNELS) return true; ChnSettings[nChn].Reset(); m_PlayState.Chn[nChn].Reset(ModChannel::resetTotal, *this, nChn, GetChannelMuteFlag()); #ifdef MODPLUG_TRACKER if(GetpModDoc() != nullptr) { GetpModDoc()->SetChannelRecordGroup(nChn, RecordGroup::NoGroup); } #endif // MODPLUG_TRACKER #ifdef MODPLUG_TRACKER m_bChannelMuteTogglePending[nChn] = false; #endif // MODPLUG_TRACKER return false; } void CSoundFile::InitAmigaResampler() { if(m_SongFlags[SONG_ISAMIGA] && m_Resampler.m_Settings.emulateAmiga != Resampling::AmigaFilter::Off) { const Paula::State defaultState(GetSampleRate()); for(auto &chn : m_PlayState.Chn) { chn.paulaState = defaultState; } } } void CSoundFile::InitOPL() { if(!m_opl) m_opl = std::make_unique(m_MixerSettings.gdwMixingFreq); } // Detect samples that are referenced by an instrument, but actually not used in a song. // Only works in instrument mode. Unused samples are marked as false in the vector. SAMPLEINDEX CSoundFile::DetectUnusedSamples(std::vector &sampleUsed) const { sampleUsed.assign(GetNumSamples() + 1, false); if(GetNumInstruments() == 0) { return 0; } SAMPLEINDEX unused = 0; std::vector lastIns; for(const auto &pat : Patterns) if(pat.IsValid()) { lastIns.assign(GetNumChannels(), 0); auto p = pat.cbegin(); for(ROWINDEX row = 0; row < pat.GetNumRows(); row++) { for(CHANNELINDEX c = 0; c < GetNumChannels(); c++, p++) { if(p->IsNote()) { ModCommand::INSTR instr = p->instr; if(!p->instr) instr = lastIns[c]; INSTRUMENTINDEX minInstr = 1, maxInstr = GetNumInstruments(); if(instr > 0) { if(instr <= GetNumInstruments()) { minInstr = maxInstr = instr; } lastIns[c] = instr; } else { // No idea which instrument this note belongs to, so mark it used in any instruments. } for(INSTRUMENTINDEX i = minInstr; i <= maxInstr; i++) { if(const auto *pIns = Instruments[i]; pIns != nullptr) { SAMPLEINDEX n = pIns->Keyboard[p->note - NOTE_MIN]; if(n <= GetNumSamples()) sampleUsed[n] = true; } } } } } } for (SAMPLEINDEX ichk = GetNumSamples(); ichk >= 1; ichk--) { if ((!sampleUsed[ichk]) && (Samples[ichk].HasSampleData())) unused++; } return unused; } // Destroy samples where keepSamples index is false. First sample is keepSamples[1]! SAMPLEINDEX CSoundFile::RemoveSelectedSamples(const std::vector &keepSamples) { if(keepSamples.empty()) { return 0; } SAMPLEINDEX nRemoved = 0; for(SAMPLEINDEX nSmp = std::min(GetNumSamples(), static_cast(keepSamples.size() - 1)); nSmp >= 1; nSmp--) { if(!keepSamples[nSmp]) { CriticalSection cs; #ifdef MODPLUG_TRACKER if(GetpModDoc()) { GetpModDoc()->GetSampleUndo().PrepareUndo(nSmp, sundo_replace, "Remove Sample"); } #endif // MODPLUG_TRACKER if(DestroySample(nSmp)) { m_szNames[nSmp] = ""; nRemoved++; } if((nSmp == GetNumSamples()) && (nSmp > 1)) m_nSamples--; } } return nRemoved; } bool CSoundFile::DestroySample(SAMPLEINDEX nSample) { if(!nSample || nSample >= MAX_SAMPLES) { return false; } if(!Samples[nSample].HasSampleData()) { return true; } ModSample &sample = Samples[nSample]; for(auto &chn : m_PlayState.Chn) { if(chn.pModSample == &sample) { chn.position.Set(0); chn.nLength = 0; chn.pCurrentSample = nullptr; } } sample.FreeSample(); sample.nLength = 0; sample.uFlags.reset(CHN_16BIT | CHN_STEREO); sample.SetAdlib(false); #ifdef MODPLUG_TRACKER ResetSamplePath(nSample); #endif return true; } bool CSoundFile::DestroySampleThreadsafe(SAMPLEINDEX nSample) { CriticalSection cs; return DestroySample(nSample); } std::unique_ptr CSoundFile::CreateTuning12TET(const mpt::ustring &name) { std::unique_ptr pT = CTuning::CreateGeometric(name, 12, 2, 15); for(ModCommand::NOTE note = 0; note < 12; ++note) { pT->SetNoteName(note, mpt::ustring(NoteNamesSharp[note])); } return pT; } mpt::ustring CSoundFile::GetNoteName(const ModCommand::NOTE note, const INSTRUMENTINDEX inst) const { // For MPTM instruments with custom tuning, find the appropriate note name. Else, use default note names. if(ModCommand::IsNote(note) && GetType() == MOD_TYPE_MPT && inst >= 1 && inst <= GetNumInstruments() && Instruments[inst] && Instruments[inst]->pTuning) { return Instruments[inst]->pTuning->GetNoteName(note - NOTE_MIDDLEC); } else { return GetNoteName(note); } } mpt::ustring CSoundFile::GetNoteName(const ModCommand::NOTE note) const { return GetNoteName(note, m_NoteNames); } mpt::ustring CSoundFile::GetNoteName(const ModCommand::NOTE note, const NoteName *noteNames) { if(ModCommand::IsSpecialNote(note)) { // cppcheck false-positive // cppcheck-suppress constStatement const mpt::uchar specialNoteNames[][4] = { UL_("PCs"), UL_("PC "), UL_("~~~"), UL_("^^^"), UL_("===") }; static_assert(mpt::array_size::size == NOTE_MAX_SPECIAL - NOTE_MIN_SPECIAL + 1); return specialNoteNames[note - NOTE_MIN_SPECIAL]; } else if(ModCommand::IsNote(note)) { return mpt::ustring() .append(noteNames[(note - NOTE_MIN) % 12]) .append(1, UC_('0') + (note - NOTE_MIN) / 12) ; // e.g. "C#" + "5" } else if(note == NOTE_NONE) { return UL_("..."); } return UL_("???"); } #ifdef MODPLUG_TRACKER void CSoundFile::SetDefaultNoteNames() { m_NoteNames = TrackerSettings::Instance().accidentalFlats ? NoteNamesFlat : NoteNamesSharp; } const NoteName *CSoundFile::GetDefaultNoteNames() { return m_NoteNames; } #endif // MODPLUG_TRACKER void CSoundFile::SetModSpecsPointer(const CModSpecifications*& pModSpecs, const MODTYPE type) { switch(type) { case MOD_TYPE_MPT: pModSpecs = &ModSpecs::mptm; break; case MOD_TYPE_IT: pModSpecs = &ModSpecs::itEx; break; case MOD_TYPE_XM: pModSpecs = &ModSpecs::xmEx; break; case MOD_TYPE_S3M: pModSpecs = &ModSpecs::s3mEx; break; case MOD_TYPE_MOD: default: pModSpecs = &ModSpecs::mod; break; } } void CSoundFile::SetType(MODTYPE type) { m_nType = type; m_playBehaviour = GetDefaultPlaybackBehaviour(GetBestSaveFormat()); SetModSpecsPointer(m_pModSpecs, GetBestSaveFormat()); } #ifdef MODPLUG_TRACKER void CSoundFile::ChangeModTypeTo(const MODTYPE newType, bool adjust) { const MODTYPE oldType = GetType(); m_nType = newType; SetModSpecsPointer(m_pModSpecs, m_nType); if(oldType == newType || !adjust) return; SetupMODPanning(); // Setup LRRL panning scheme if needed // Only keep supported play behaviour flags PlayBehaviourSet oldAllowedFlags = GetSupportedPlaybackBehaviour(oldType); PlayBehaviourSet newAllowedFlags = GetSupportedPlaybackBehaviour(newType); PlayBehaviourSet newDefaultFlags = GetDefaultPlaybackBehaviour(newType); for(size_t i = 0; i < m_playBehaviour.size(); i++) { // If a flag is supported in both formats, keep its status if(m_playBehaviour[i]) m_playBehaviour.set(i, newAllowedFlags[i]); // Set allowed flags to their defaults if they were not supported in the old format if(!oldAllowedFlags[i]) m_playBehaviour.set(i, newDefaultFlags[i]); } // Special case for OPL behaviour when converting from S3M to MPTM to retain S3M-like note-off behaviour if(oldType == MOD_TYPE_S3M && newType == MOD_TYPE_MPT && m_opl) m_playBehaviour.reset(kOPLFlexibleNoteOff); Order.OnModTypeChanged(oldType); Patterns.OnModTypeChanged(oldType); m_modFormat.type = mpt::ToUnicode(mpt::Charset::UTF8, GetModSpecifications().fileExtension); } #endif // MODPLUG_TRACKER ModMessageHeuristicOrder CSoundFile::GetMessageHeuristic() const { ModMessageHeuristicOrder result = ModMessageHeuristicOrder::Default; switch(GetType()) { case MOD_TYPE_MPT: result = ModMessageHeuristicOrder::Samples; break; case MOD_TYPE_IT: result = ModMessageHeuristicOrder::Samples; break; case MOD_TYPE_XM: result = ModMessageHeuristicOrder::InstrumentsSamples; break; case MOD_TYPE_MDL: result = ModMessageHeuristicOrder::InstrumentsSamples; break; case MOD_TYPE_IMF: result = ModMessageHeuristicOrder::InstrumentsSamples; break; default: result = ModMessageHeuristicOrder::Default; break; } return result; } bool CSoundFile::SetTitle(const std::string &newTitle) { if(m_songName != newTitle) { m_songName = newTitle; return true; } return false; } double CSoundFile::GetPlaybackTimeAt(ORDERINDEX ord, ROWINDEX row, bool updateVars, bool updateSamplePos) { const GetLengthType t = GetLength(updateVars ? (updateSamplePos ? eAdjustSamplePositions : eAdjust) : eNoAdjust, GetLengthTarget(ord, row)).back(); if(t.targetReached) return t.duration; else return -1; //Given position not found from play sequence. } std::vector CSoundFile::GetAllSubSongs() { std::vector subSongs; for(SEQUENCEINDEX seq = 0; seq < Order.GetNumSequences(); seq++) { const auto subSongsSeq = GetLength(eNoAdjust, GetLengthTarget(true).StartPos(seq, 0, 0)); subSongs.reserve(subSongs.size() + subSongsSeq.size()); for(const auto &song : subSongsSeq) { subSongs.push_back({song.duration, song.startRow, song.endRow, song.lastRow, song.startOrder, song.endOrder, song.lastOrder, seq}); } } return subSongs; } // Calculate the length of a tick, depending on the tempo mode. // This differs from GetTickDuration() by not accumulating errors // because this is not called once per tick but in unrelated // circumstances. So this should not update error accumulation. void CSoundFile::RecalculateSamplesPerTick() { switch(m_nTempoMode) { case TempoMode::Classic: default: m_PlayState.m_nSamplesPerTick = Util::muldiv(m_MixerSettings.gdwMixingFreq, 5 * TEMPO::fractFact, std::max(TEMPO::store_t(1), m_PlayState.m_nMusicTempo.GetRaw() << 1)); break; case TempoMode::Modern: m_PlayState.m_nSamplesPerTick = static_cast((Util::mul32to64_unsigned(m_MixerSettings.gdwMixingFreq, 60 * TEMPO::fractFact) / std::max(uint64(1), Util::mul32to64_unsigned(m_PlayState.m_nMusicSpeed, m_PlayState.m_nCurrentRowsPerBeat) * m_PlayState.m_nMusicTempo.GetRaw()))); break; case TempoMode::Alternative: m_PlayState.m_nSamplesPerTick = Util::muldiv(m_MixerSettings.gdwMixingFreq, TEMPO::fractFact, std::max(TEMPO::store_t(1), m_PlayState.m_nMusicTempo.GetRaw())); break; } #ifndef MODPLUG_TRACKER m_PlayState.m_nSamplesPerTick = Util::muldivr(m_PlayState.m_nSamplesPerTick, m_nTempoFactor, 65536); #endif // !MODPLUG_TRACKER if(!m_PlayState.m_nSamplesPerTick) m_PlayState.m_nSamplesPerTick = 1; } // Get length of a tick in sample, with tick-to-tick tempo correction in modern tempo mode. // This has to be called exactly once per tick because otherwise the error accumulation // goes wrong. uint32 CSoundFile::GetTickDuration(PlayState &playState) const { uint32 retval = 0; switch(m_nTempoMode) { case TempoMode::Classic: default: retval = Util::muldiv(m_MixerSettings.gdwMixingFreq, 5 * TEMPO::fractFact, std::max(TEMPO::store_t(1), playState.m_nMusicTempo.GetRaw() << 1)); break; case TempoMode::Alternative: retval = Util::muldiv(m_MixerSettings.gdwMixingFreq, TEMPO::fractFact, std::max(TEMPO::store_t(1), playState.m_nMusicTempo.GetRaw())); break; case TempoMode::Modern: { double accurateBufferCount = static_cast(m_MixerSettings.gdwMixingFreq) * (60.0 / (playState.m_nMusicTempo.ToDouble() * Util::mul32to64_unsigned(playState.m_nMusicSpeed, playState.m_nCurrentRowsPerBeat))); const TempoSwing &swing = (Patterns.IsValidPat(playState.m_nPattern) && Patterns[playState.m_nPattern].HasTempoSwing()) ? Patterns[playState.m_nPattern].GetTempoSwing() : m_tempoSwing; if(!swing.empty()) { // Apply current row's tempo swing factor TempoSwing::value_type swingFactor = swing[playState.m_nRow % swing.size()]; accurateBufferCount = accurateBufferCount * swingFactor / double(TempoSwing::Unity); } uint32 bufferCount = static_cast(accurateBufferCount); playState.m_dBufferDiff += accurateBufferCount - bufferCount; //tick-to-tick tempo correction: if(playState.m_dBufferDiff >= 1) { bufferCount++; playState.m_dBufferDiff--; } else if(m_PlayState.m_dBufferDiff <= -1) { bufferCount--; playState.m_dBufferDiff++; } MPT_ASSERT(std::abs(playState.m_dBufferDiff) < 1.0); retval = bufferCount; } break; } #ifndef MODPLUG_TRACKER // when the user modifies the tempo, we do not really care about accurate tempo error accumulation retval = Util::muldivr_unsigned(retval, m_nTempoFactor, 65536); #endif // !MODPLUG_TRACKER if(!retval) retval = 1; return retval; } // Get the duration of a row in milliseconds, based on the current rows per beat and given speed and tempo settings. double CSoundFile::GetRowDuration(TEMPO tempo, uint32 speed) const { switch(m_nTempoMode) { case TempoMode::Classic: default: return static_cast(2500 * speed) / tempo.ToDouble(); case TempoMode::Modern: { // If there are any row delay effects, the row length factor compensates for those. return 60000.0 / tempo.ToDouble() / static_cast(m_PlayState.m_nCurrentRowsPerBeat); } case TempoMode::Alternative: return static_cast(1000 * speed) / tempo.ToDouble(); } } const CModSpecifications& CSoundFile::GetModSpecifications(const MODTYPE type) { const CModSpecifications* p = nullptr; SetModSpecsPointer(p, type); return *p; } ChannelFlags CSoundFile::GetChannelMuteFlag() { #ifdef MODPLUG_TRACKER return (TrackerSettings::Instance().m_dwPatternSetup & PATTERN_SYNCMUTE) ? CHN_SYNCMUTE : CHN_MUTE; #else return CHN_SYNCMUTE; #endif } // Resolve note/instrument combination to real sample index. Return value is guaranteed to be in [0, GetNumSamples()]. SAMPLEINDEX CSoundFile::GetSampleIndex(ModCommand::NOTE note, uint32 instr) const noexcept { SAMPLEINDEX smp = 0; if(GetNumInstruments()) { if(ModCommand::IsNote(note) && instr <= GetNumInstruments() && Instruments[instr] != nullptr) smp = Instruments[instr]->Keyboard[note - NOTE_MIN]; } else { smp = static_cast(instr); } if(smp <= GetNumSamples()) return smp; else return 0; } // Find an unused sample slot. If it is going to be assigned to an instrument, targetInstrument should be specified. // SAMPLEINDEX_INVLAID is returned if no free sample slot could be found. SAMPLEINDEX CSoundFile::GetNextFreeSample(INSTRUMENTINDEX targetInstrument, SAMPLEINDEX start) const { // Find empty slot in two passes - in the first pass, we only search for samples with empty sample names, // in the second pass we check all samples with non-empty sample names. for(int passes = 0; passes < 2; passes++) { for(SAMPLEINDEX i = start; i <= GetModSpecifications().samplesMax; i++) { // Early exit for FM instruments if(Samples[i].uFlags[CHN_ADLIB] && (targetInstrument == INSTRUMENTINDEX_INVALID || !IsSampleReferencedByInstrument(i, targetInstrument))) continue; // When loading into an instrument, ignore non-empty sample names. Else, only use this slot if the sample name is empty or we're in second pass. if((i > GetNumSamples() && passes == 1) || (!Samples[i].HasSampleData() && (!m_szNames[i][0] || passes == 1 || targetInstrument != INSTRUMENTINDEX_INVALID)) || (targetInstrument != INSTRUMENTINDEX_INVALID && IsSampleReferencedByInstrument(i, targetInstrument))) // Not empty, but already used by this instrument. XXX this should only be done when replacing an instrument with a single sample! Otherwise it will use an inconsistent sample map! { // Empty slot, so it's a good candidate already. // In instrument mode, check whether any instrument references this sample slot. If that is the case, we won't use it as it could lead to unwanted conflicts. // If we are loading the sample *into* an instrument, we should also not consider that instrument's sample map, since it might be inconsistent at this time. bool isReferenced = false; for(INSTRUMENTINDEX ins = 1; ins <= GetNumInstruments(); ins++) { if(ins == targetInstrument) { continue; } if(IsSampleReferencedByInstrument(i, ins)) { isReferenced = true; break; } } if(!isReferenced) { return i; } } } } return SAMPLEINDEX_INVALID; } // Find an unused instrument slot. // INSTRUMENTINDEX_INVALID is returned if no free instrument slot could be found. INSTRUMENTINDEX CSoundFile::GetNextFreeInstrument(INSTRUMENTINDEX start) const { for(INSTRUMENTINDEX i = start; i <= GetModSpecifications().instrumentsMax; i++) { if(Instruments[i] == nullptr) { return i; } } return INSTRUMENTINDEX_INVALID; } // Check whether a given sample is used by a given instrument. bool CSoundFile::IsSampleReferencedByInstrument(SAMPLEINDEX sample, INSTRUMENTINDEX instr) const { if(instr < 1 || instr > GetNumInstruments()) return false; const ModInstrument *targetIns = Instruments[instr]; if(targetIns == nullptr) return false; return mpt::contains(mpt::as_span(targetIns->Keyboard).first(NOTE_MAX), sample); } ModInstrument *CSoundFile::AllocateInstrument(INSTRUMENTINDEX instr, SAMPLEINDEX assignedSample) { if(instr == 0 || instr >= MAX_INSTRUMENTS) { return nullptr; } ModInstrument *ins = Instruments[instr]; if(ins != nullptr) { // Re-initialize instrument *ins = ModInstrument(assignedSample); } else { // Create new instrument Instruments[instr] = ins = new (std::nothrow) ModInstrument(assignedSample); } if(ins != nullptr) { m_nInstruments = std::max(m_nInstruments, instr); } return ins; } void CSoundFile::PrecomputeSampleLoops(bool updateChannels) { for(SAMPLEINDEX i = 1; i <= GetNumSamples(); i++) { Samples[i].PrecomputeLoops(*this, updateChannels); } } #ifdef MPT_EXTERNAL_SAMPLES // Load external waveform, but keep sample properties like frequency, panning, etc... // Returns true if the file could be loaded. bool CSoundFile::LoadExternalSample(SAMPLEINDEX smp, const mpt::PathString &filename) { bool ok = false; InputFile f(filename, SettingCacheCompleteFileBeforeLoading()); if(f.IsValid()) { const ModSample origSample = Samples[smp]; mpt::charbuf origName; origName = m_szNames[smp]; FileReader file = GetFileReader(f); ok = ReadSampleFromFile(smp, file, false); if(ok) { // Copy over old attributes, but keep new sample data ModSample &sample = GetSample(smp); SmpLength newLength = sample.nLength; void *newData = sample.samplev(); SampleFlags newFlags = sample.uFlags; sample = origSample; sample.nLength = newLength; sample.pData.pSample = newData; sample.uFlags.set(CHN_16BIT, newFlags[CHN_16BIT]); sample.uFlags.set(CHN_STEREO, newFlags[CHN_STEREO]); sample.uFlags.reset(SMP_MODIFIED); sample.SanitizeLoops(); } m_szNames[smp] = origName; } SetSamplePath(smp, filename); return ok; } #endif // MPT_EXTERNAL_SAMPLES // Set up channel panning and volume suitable for MOD + similar files. If the current mod type is not MOD, bForceSetup has to be set to true. void CSoundFile::SetupMODPanning(bool bForceSetup) { // Setup LRRL panning, max channel volume if(!(GetType() & MOD_TYPE_MOD) && bForceSetup == false) return; for(CHANNELINDEX nChn = 0; nChn < MAX_BASECHANNELS; nChn++) { ChnSettings[nChn].nVolume = 64; ChnSettings[nChn].dwFlags.reset(CHN_SURROUND); if(m_MixerSettings.MixerFlags & SNDMIX_MAXDEFAULTPAN) ChnSettings[nChn].nPan = (((nChn & 3) == 1) || ((nChn & 3) == 2)) ? 256 : 0; else ChnSettings[nChn].nPan = (((nChn & 3) == 1) || ((nChn & 3) == 2)) ? 0xC0 : 0x40; } } void CSoundFile::PropagateXMAutoVibrato(INSTRUMENTINDEX ins, VibratoType type, uint8 sweep, uint8 depth, uint8 rate) { if(ins > m_nInstruments || Instruments[ins] == nullptr) return; const std::set referencedSamples = Instruments[ins]->GetSamples(); // Propagate changes to all samples that belong to this instrument. for(auto sample : referencedSamples) { if(sample <= m_nSamples) { Samples[sample].nVibDepth = depth; Samples[sample].nVibType = type; Samples[sample].nVibRate = rate; Samples[sample].nVibSweep = sweep; } } } // Normalize the tempo swing coefficients so that they add up to exactly the specified tempo again void TempoSwing::Normalize() { if(empty()) return; uint64 sum = 0; for(auto &i : *this) { Limit(i, Unity / 4u, Unity * 4u); sum += i; } sum /= size(); int64 remain = Unity * size(); for(auto &i : *this) { i = Util::muldivr_unsigned(i, Unity, static_cast(sum)); remain -= i; } //MPT_ASSERT(static_cast(std::abs(static_cast(remain))) <= size()); at(0) += static_cast(remain); } void TempoSwing::Serialize(std::ostream &oStrm, const TempoSwing &swing) { mpt::IO::WriteIntLE(oStrm, static_cast(swing.size())); for(std::size_t i = 0; i < swing.size(); i++) { mpt::IO::WriteIntLE(oStrm, swing[i]); } } void TempoSwing::Deserialize(std::istream &iStrm, TempoSwing &swing, const size_t) { uint16 numEntries; mpt::IO::ReadIntLE(iStrm, numEntries); swing.resize(numEntries); for(uint16 i = 0; i < numEntries; i++) { mpt::IO::ReadIntLE(iStrm, swing[i]); } swing.Normalize(); } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Sndfile.h0000644000175000017500000015261314175042045017642 00000000000000/* * Sndfile.h * --------- * Purpose: Core class of the playback engine. Every song is represented by a CSoundFile object. * Notes : (currently none) * Authors: Olivier Lapicque * OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "SoundFilePlayConfig.h" #include "MixerSettings.h" #include "../common/misc_util.h" #include "../common/mptRandom.h" #include "../common/version.h" #include #include #include #include "Snd_defs.h" #include "tuningbase.h" #include "MIDIMacros.h" #ifdef MODPLUG_TRACKER #include "../mptrack/MIDIMapping.h" #endif // MODPLUG_TRACKER #include "Mixer.h" #include "Resampler.h" #ifndef NO_REVERB #include "../sounddsp/Reverb.h" #endif #ifndef NO_AGC #include "../sounddsp/AGC.h" #endif #ifndef NO_DSP #include "../sounddsp/DSP.h" #endif #ifndef NO_EQ #include "../sounddsp/EQ.h" #endif #include "modcommand.h" #include "ModSample.h" #include "ModInstrument.h" #include "ModChannel.h" #include "plugins/PluginStructs.h" #include "RowVisitor.h" #include "Message.h" #include "pattern.h" #include "patternContainer.h" #include "ModSequence.h" #include "mpt/audio/span.hpp" #include "../common/FileReaderFwd.h" OPENMPT_NAMESPACE_BEGIN bool SettingCacheCompleteFileBeforeLoading(); // ----------------------------------------------------------------------------- // MODULAR ModInstrument FIELD ACCESS : body content in InstrumentExtensions.cpp // ----------------------------------------------------------------------------- #ifndef MODPLUG_NO_FILESAVE void WriteInstrumentHeaderStructOrField(ModInstrument * input, std::ostream &file, uint32 only_this_code = -1 /* -1 for all */, uint16 fixedsize = 0); #endif // !MODPLUG_NO_FILESAVE bool ReadInstrumentHeaderField(ModInstrument * input, uint32 fcode, uint16 fsize, FileReader &file); // -------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------- // Sample decompression routines in format-specific source files void AMSUnpack(const int8 * const source, size_t sourceSize, void * const dest, const size_t destSize, char packCharacter); uintptr_t DMFUnpack(FileReader &file, uint8 *psample, uint32 maxlen); #ifdef LIBOPENMPT_BUILD #ifndef NO_PLUGINS class CVstPluginManager; #endif #endif using PlayBehaviourSet = std::bitset; #ifdef MODPLUG_TRACKER // For WAV export (writing pattern positions to file) struct PatternCuePoint { uint64 offset; // offset in the file (in samples) ORDERINDEX order; // which order is this? bool processed; // has this point been processed by the main WAV render function yet? }; #endif // MODPLUG_TRACKER // Return values for GetLength() struct GetLengthType { double duration = 0.0; // Total time in seconds ROWINDEX lastRow = ROWINDEX_INVALID; // Last parsed row (if no target is specified, this is the first row that is parsed twice, i.e. not the *last* played order) ROWINDEX endRow = ROWINDEX_INVALID; // Last row before module loops (UNDEFINED if a target is specified) ROWINDEX startRow = 0; // First row of parsed subsong ORDERINDEX lastOrder = ORDERINDEX_INVALID; // Last parsed order (see lastRow remark) ORDERINDEX endOrder = ORDERINDEX_INVALID; // Last order before module loops (UNDEFINED if a target is specified) ORDERINDEX startOrder = 0; // First order of parsed subsong bool targetReached = false; // True if the specified order/row combination or duration has been reached while going through the module }; struct SubSong { double duration; ROWINDEX startRow, endRow, loopStartRow; ORDERINDEX startOrder, endOrder, loopStartOrder; SEQUENCEINDEX sequence; }; // Target seek mode for GetLength() struct GetLengthTarget { ROWINDEX startRow; ORDERINDEX startOrder; SEQUENCEINDEX sequence; struct pos_type { ROWINDEX row; ORDERINDEX order; }; union { double time; pos_type pos; }; enum Mode { NoTarget, // Don't seek, i.e. return complete length of the first subsong. GetAllSubsongs, // Same as NoTarget (i.e. get complete length), but returns the length of all sub songs SeekPosition, // Seek to given pattern position. SeekSeconds, // Seek to given time. } mode; // Don't seek, i.e. return complete module length. GetLengthTarget(bool allSongs = false) { mode = allSongs ? GetAllSubsongs : NoTarget; sequence = SEQUENCEINDEX_INVALID; startOrder = 0; startRow = 0; } // Seek to given pattern position if position is valid. GetLengthTarget(ORDERINDEX order, ROWINDEX row) { mode = NoTarget; sequence = SEQUENCEINDEX_INVALID; startOrder = 0; startRow = 0; if(order != ORDERINDEX_INVALID && row != ROWINDEX_INVALID) { mode = SeekPosition; pos.row = row; pos.order = order; } } // Seek to given time if t is valid (i.e. not negative). GetLengthTarget(double t) { mode = NoTarget; sequence = SEQUENCEINDEX_INVALID; startOrder = 0; startRow = 0; if(t >= 0.0) { mode = SeekSeconds; time = t; } } // Set start position from which seeking should begin. GetLengthTarget &StartPos(SEQUENCEINDEX seq, ORDERINDEX order, ROWINDEX row) { sequence = seq; startOrder = order; startRow = row; return *this; } }; // Reset mode for GetLength() enum enmGetLengthResetMode { // Never adjust global variables / mod parameters eNoAdjust = 0x00, // Mod parameters (such as global volume, speed, tempo, etc...) will always be memorized if the target was reached (i.e. they won't be reset to the previous values). If target couldn't be reached, they are reset to their default values. eAdjust = 0x01, // Same as above, but global variables will only be memorized if the target could be reached. This does *NOT* influence the visited rows vector - it will *ALWAYS* be adjusted in this mode. eAdjustOnSuccess = 0x02 | eAdjust, // Same as previous option, but will also try to emulate sample playback so that voices from previous patterns will sound when continuing playback at the target position. eAdjustSamplePositions = 0x04 | eAdjustOnSuccess, // Only adjust the visited rows state eAdjustOnlyVisitedRows = 0x08, }; // Delete samples assigned to instrument enum deleteInstrumentSamples { deleteAssociatedSamples, doNoDeleteAssociatedSamples, }; namespace Tuning { class CTuningCollection; } // namespace Tuning using CTuningCollection = Tuning::CTuningCollection; struct CModSpecifications; class OPL; class CModDoc; ///////////////////////////////////////////////////////////////////////// // File edit history #define HISTORY_TIMER_PRECISION 18.2 struct FileHistory { // Date when the file was loaded in the the tracker or created. tm loadDate = {}; // Time the file was open in the editor, in 1/18.2th seconds (frequency of a standard DOS timer, to keep compatibility with Impulse Tracker easy). uint32 openTime = 0; // Return the date as a (possibly truncated if not enough precision is available) ISO 8601 formatted date. mpt::ustring AsISO8601() const; // Returns true if the date component is valid. Some formats only store edit time, not edit date. bool HasValidDate() const { return loadDate.tm_mday != 0; } }; struct TimingInfo { double InputLatency = 0.0; // seconds double OutputLatency = 0.0; // seconds int64 StreamFrames = 0; uint64 SystemTimestamp = 0; // nanoseconds double Speed = 1.0; }; enum class ModMessageHeuristicOrder { Instruments, Samples, InstrumentsSamples, SamplesInstruments, BothInstrumentsSamples, BothSamplesInstruments, Default = InstrumentsSamples, }; struct ModFormatDetails { mpt::ustring formatName; // "FastTracker 2" mpt::ustring type; // "xm" mpt::ustring madeWithTracker; // "OpenMPT 1.28.01.00" mpt::ustring originalFormatName; // "FastTracker 2" in the case of converted formats like MO3 or GDM mpt::ustring originalType; // "xm" in the case of converted formats like MO3 or GDM mpt::Charset charset = mpt::Charset::UTF8; }; class IAudioTarget { protected: virtual ~IAudioTarget() = default; public: virtual void Process(mpt::audio_span_interleaved buffer) = 0; virtual void Process(mpt::audio_span_interleaved buffer) = 0; }; class IAudioSource { public: virtual ~IAudioSource() = default; public: virtual void Process(mpt::audio_span_planar buffer) = 0; virtual void Process(mpt::audio_span_planar buffer) = 0; }; class IMonitorInput { public: virtual ~IMonitorInput() = default; public: virtual void Process(mpt::audio_span_planar buffer) = 0; virtual void Process(mpt::audio_span_planar buffer) = 0; }; class IMonitorOutput { public: virtual ~IMonitorOutput() = default; public: virtual void Process(mpt::audio_span_interleaved buffer) = 0; virtual void Process(mpt::audio_span_interleaved buffer) = 0; }; class AudioSourceNone : public IAudioSource { public: void Process(mpt::audio_span_planar buffer) override { for(std::size_t channel = 0; channel < buffer.size_channels(); ++channel) { for(std::size_t frame = 0; frame < buffer.size_frames(); ++frame) { buffer(channel, frame) = 0; } } } void Process(mpt::audio_span_planar buffer) override { for(std::size_t channel = 0; channel < buffer.size_channels(); ++channel) { for(std::size_t frame = 0; frame < buffer.size_frames(); ++frame) { buffer(channel, frame) = MixSampleFloat(0.0); } } } }; using NoteName = mpt::uchar[4]; class CSoundFile { friend class GetLengthMemory; public: #ifdef MODPLUG_TRACKER void ChangeModTypeTo(const MODTYPE newType, bool adjust = true); #endif // MODPLUG_TRACKER // Returns value in seconds. If given position won't be played at all, returns -1. // If updateVars is true, the state of various playback variables will be updated according to the playback position. // If updateSamplePos is also true, the sample positions of samples still playing from previous patterns will be kept in sync. double GetPlaybackTimeAt(ORDERINDEX ord, ROWINDEX row, bool updateVars, bool updateSamplePos); std::vector GetAllSubSongs(); //Tuning--> public: static std::unique_ptr CreateTuning12TET(const mpt::ustring &name); static CTuning *GetDefaultTuning() {return nullptr;} CTuningCollection& GetTuneSpecificTunings() {return *m_pTuningsTuneSpecific;} mpt::ustring GetNoteName(const ModCommand::NOTE note, const INSTRUMENTINDEX inst) const; mpt::ustring GetNoteName(const ModCommand::NOTE note) const; static mpt::ustring GetNoteName(const ModCommand::NOTE note, const NoteName *noteNames); #ifdef MODPLUG_TRACKER public: static void SetDefaultNoteNames(); static const NoteName *GetDefaultNoteNames(); static mpt::ustring GetDefaultNoteName(int note) // note = [0..11] { return m_NoteNames[note]; } private: static const NoteName *m_NoteNames; #else private: const NoteName *m_NoteNames; #endif private: CTuningCollection* m_pTuningsTuneSpecific = nullptr; #ifdef MODPLUG_TRACKER public: CMIDIMapper& GetMIDIMapper() {return m_MIDIMapper;} const CMIDIMapper& GetMIDIMapper() const {return m_MIDIMapper;} private: CMIDIMapper m_MIDIMapper; #endif // MODPLUG_TRACKER private: //Misc private methods. static void SetModSpecsPointer(const CModSpecifications* &pModSpecs, const MODTYPE type); private: //Misc data const CModSpecifications *m_pModSpecs; private: // Interleaved Front Mix Buffer (Also room for interleaved rear mix) mixsample_t MixSoundBuffer[MIXBUFFERSIZE * 4]; mixsample_t MixRearBuffer[MIXBUFFERSIZE * 2]; // Non-interleaved plugin processing buffer float MixFloatBuffer[2][MIXBUFFERSIZE]; mixsample_t MixInputBuffer[NUMMIXINPUTBUFFERS][MIXBUFFERSIZE]; // End-of-sample pop reduction tail level mixsample_t m_dryLOfsVol = 0, m_dryROfsVol = 0; mixsample_t m_surroundLOfsVol = 0, m_surroundROfsVol = 0; public: MixerSettings m_MixerSettings; CResampler m_Resampler; #ifndef NO_REVERB mixsample_t ReverbSendBuffer[MIXBUFFERSIZE * 2]; mixsample_t m_RvbROfsVol = 0, m_RvbLOfsVol = 0; CReverb m_Reverb; #endif #ifndef NO_DSP CSurround m_Surround; CMegaBass m_MegaBass; #endif #ifndef NO_EQ CEQ m_EQ; #endif #ifndef NO_AGC CAGC m_AGC; #endif #ifndef NO_DSP BitCrush m_BitCrush; #endif using samplecount_t = uint32; // Number of rendered samples static constexpr uint32 TICKS_ROW_FINISHED = uint32_max - 1u; public: // for Editing #ifdef MODPLUG_TRACKER CModDoc *m_pModDoc = nullptr; // Can be a null pointer for example when previewing samples from the treeview. #endif // MODPLUG_TRACKER Enum m_nType; private: MODCONTAINERTYPE m_ContainerType = MOD_CONTAINERTYPE_NONE; public: CHANNELINDEX m_nChannels = 0; SAMPLEINDEX m_nSamples = 0; INSTRUMENTINDEX m_nInstruments = 0; uint32 m_nDefaultSpeed, m_nDefaultGlobalVolume; TEMPO m_nDefaultTempo; FlagSet m_SongFlags; CHANNELINDEX m_nMixChannels = 0; private: CHANNELINDEX m_nMixStat; public: ROWINDEX m_nDefaultRowsPerBeat, m_nDefaultRowsPerMeasure; // default rows per beat and measure for this module TempoMode m_nTempoMode = TempoMode::Classic; #ifdef MODPLUG_TRACKER // Lock playback between two rows. Lock is active if lock start != ROWINDEX_INVALID). ROWINDEX m_lockRowStart = ROWINDEX_INVALID, m_lockRowEnd = ROWINDEX_INVALID; // Lock playback between two orders. Lock is active if lock start != ORDERINDEX_INVALID). ORDERINDEX m_lockOrderStart = ORDERINDEX_INVALID, m_lockOrderEnd = ORDERINDEX_INVALID; #endif // MODPLUG_TRACKER uint32 m_nSamplePreAmp, m_nVSTiVolume; uint32 m_OPLVolumeFactor; // 16.16 static constexpr uint32 m_OPLVolumeFactorScale = 1 << 16; constexpr bool IsGlobalVolumeUnset() const noexcept { return IsFirstTick(); } #ifndef MODPLUG_TRACKER uint32 m_nFreqFactor = 65536; // Pitch shift factor (65536 = no pitch shifting). Only used in libopenmpt (openmpt::ext::interactive::set_pitch_factor) uint32 m_nTempoFactor = 65536; // Tempo factor (65536 = no tempo adjustment). Only used in libopenmpt (openmpt::ext::interactive::set_tempo_factor) #endif // Row swing factors for modern tempo mode TempoSwing m_tempoSwing; // Min Period = highest possible frequency, Max Period = lowest possible frequency for current format // Note: Period is an Amiga metric that is inverse to frequency. // Periods in MPT are 4 times as fine as Amiga periods because of extra fine frequency slides (introduced in the S3M format). int32 m_nMinPeriod, m_nMaxPeriod; ResamplingMode m_nResampling; // Resampling mode (if overriding the globally set resampling) int32 m_nRepeatCount = 0; // -1 means repeat infinitely. ORDERINDEX m_nMaxOrderPosition; ModChannelSettings ChnSettings[MAX_BASECHANNELS]; // Initial channels settings CPatternContainer Patterns; ModSequenceSet Order; // Pattern sequences (order lists) protected: ModSample Samples[MAX_SAMPLES]; public: ModInstrument *Instruments[MAX_INSTRUMENTS]; // Instrument Headers MIDIMacroConfig m_MidiCfg; // MIDI Macro config table #ifndef NO_PLUGINS SNDMIXPLUGIN m_MixPlugins[MAX_MIXPLUGINS]; // Mix plugins uint32 m_loadedPlugins = 0; // Not a PLUGINDEX because number of loaded plugins may exceed MAX_MIXPLUGINS during MIDI conversion #endif mpt::charbuf m_szNames[MAX_SAMPLES]; // Sample names Version m_dwCreatedWithVersion; Version m_dwLastSavedWithVersion; PlayBehaviourSet m_playBehaviour; protected: mpt::fast_prng m_PRNG; inline mpt::fast_prng & AccessPRNG() const { return const_cast(this)->m_PRNG; } inline mpt::fast_prng & AccessPRNG() { return m_PRNG; } protected: // Mix level stuff CSoundFilePlayConfig m_PlayConfig; MixLevels m_nMixLevels; public: struct PlayState { friend class CSoundFile; public: samplecount_t m_lTotalSampleCount = 0; // Total number of rendered samples protected: samplecount_t m_nBufferCount = 0; // Remaining number samples to render for this tick double m_dBufferDiff = 0.0; // Modern tempo rounding error compensation public: uint32 m_nTickCount = 0; // Current tick being processed protected: uint32 m_nPatternDelay = 0; // Pattern delay (rows) uint32 m_nFrameDelay = 0; // Fine pattern delay (ticks) public: uint32 m_nSamplesPerTick = 0; ROWINDEX m_nCurrentRowsPerBeat = 0; // Current time signature ROWINDEX m_nCurrentRowsPerMeasure = 0; // Current time signature uint32 m_nMusicSpeed = 0; // Current speed TEMPO m_nMusicTempo; // Current tempo // Playback position ROWINDEX m_nRow = 0; // Current row being processed ROWINDEX m_nNextRow = 0; // Next row to process protected: ROWINDEX m_nextPatStartRow = 0; // For FT2's E60 bug ROWINDEX m_breakRow = 0; // Candidate target row for pattern break ROWINDEX m_patLoopRow = 0; // Candidate target row for pattern loop ORDERINDEX m_posJump = 0; // Candidate target order for position jump public: PATTERNINDEX m_nPattern = 0; // Current pattern being processed ORDERINDEX m_nCurrentOrder = 0; // Current order being processed ORDERINDEX m_nNextOrder = 0; // Next order to process ORDERINDEX m_nSeqOverride = ORDERINDEX_INVALID; // Queued order to be processed next, regardless of what order would normally follow // Global volume public: int32 m_nGlobalVolume = MAX_GLOBAL_VOLUME; // Current global volume (0...MAX_GLOBAL_VOLUME) protected: int32 m_nSamplesToGlobalVolRampDest = 0, m_nGlobalVolumeRampAmount = 0, m_nGlobalVolumeDestination = 0; // Global volume ramping int32 m_lHighResRampingGlobalVolume = 0; // Global volume ramping public: bool m_bPositionChanged = true; // Report to plugins that we jumped around in the module public: CHANNELINDEX ChnMix[MAX_CHANNELS]; // Index of channels in Chn to be actually mixed ModChannel Chn[MAX_CHANNELS]; // Mixing channels... First m_nChannels channels are master channels (i.e. they are never NNA channels)! struct MIDIMacroEvaluationResults { std::map pluginDryWetRatio; std::map, PlugParamValue> pluginParameter; }; std::vector m_midiMacroScratchSpace; std::optional m_midiMacroEvaluationResults; public: PlayState(); void ResetGlobalVolumeRamping() { m_lHighResRampingGlobalVolume = m_nGlobalVolume << VOLUMERAMPPRECISION; m_nGlobalVolumeDestination = m_nGlobalVolume; m_nSamplesToGlobalVolRampDest = 0; m_nGlobalVolumeRampAmount = 0; } constexpr uint32 TicksOnRow() const noexcept { return (m_nMusicSpeed + m_nFrameDelay) * std::max(m_nPatternDelay, uint32(1)); } }; PlayState m_PlayState; protected: // For handling backwards jumps and stuff to prevent infinite loops when counting the mod length or rendering to wav. RowVisitor m_visitedRows; public: #ifdef MODPLUG_TRACKER std::bitset m_bChannelMuteTogglePending; std::vector *m_PatternCuePoints = nullptr; // For WAV export (writing pattern positions to file) std::vector *m_SamplePlayLengths = nullptr; // For storing the maximum play length of each sample for automatic sample trimming #endif // MODPLUG_TRACKER std::unique_ptr m_opl; public: #ifdef LIBOPENMPT_BUILD #ifndef NO_PLUGINS std::unique_ptr m_PluginManager; #endif #endif public: std::string m_songName; mpt::ustring m_songArtist; SongMessage m_songMessage; ModFormatDetails m_modFormat; protected: std::vector m_FileHistory; // File edit history public: std::vector &GetFileHistory() { return m_FileHistory; } const std::vector &GetFileHistory() const { return m_FileHistory; } #ifdef MPT_EXTERNAL_SAMPLES // MPTM external on-disk sample paths protected: std::vector m_samplePaths; public: void SetSamplePath(SAMPLEINDEX smp, mpt::PathString filename) { if(m_samplePaths.size() < smp) m_samplePaths.resize(smp); m_samplePaths[smp - 1] = std::move(filename); } void ResetSamplePath(SAMPLEINDEX smp) { if(m_samplePaths.size() >= smp) m_samplePaths[smp - 1] = mpt::PathString(); Samples[smp].uFlags.reset(SMP_KEEPONDISK | SMP_MODIFIED);} mpt::PathString GetSamplePath(SAMPLEINDEX smp) const { if(m_samplePaths.size() >= smp) return m_samplePaths[smp - 1]; else return mpt::PathString(); } bool SampleHasPath(SAMPLEINDEX smp) const { if(m_samplePaths.size() >= smp) return !m_samplePaths[smp - 1].empty(); else return false; } bool IsExternalSampleMissing(SAMPLEINDEX smp) const { return Samples[smp].uFlags[SMP_KEEPONDISK] && !Samples[smp].HasSampleData(); } bool LoadExternalSample(SAMPLEINDEX smp, const mpt::PathString &filename); #endif // MPT_EXTERNAL_SAMPLES bool m_bIsRendering = false; TimingInfo m_TimingInfo; // only valid if !m_bIsRendering private: // logging ILog *m_pCustomLog = nullptr; public: CSoundFile(); CSoundFile(const CSoundFile &) = delete; CSoundFile & operator=(const CSoundFile &) = delete; ~CSoundFile(); public: // logging void SetCustomLog(ILog *pLog) { m_pCustomLog = pLog; } void AddToLog(LogLevel level, const mpt::ustring &text) const; public: enum ModLoadingFlags { onlyVerifyHeader = 0x00, loadPatternData = 0x01, // If unset, advise loaders to not process any pattern data (if possible) loadSampleData = 0x02, // If unset, advise loaders to not process any sample data (if possible) loadPluginData = 0x04, // If unset, plugin data is not loaded (and as a consequence, plugins are not instanciated). loadPluginInstance = 0x08, // If unset, plugins are not instanciated. skipContainer = 0x10, skipModules = 0x20, // Shortcuts loadCompleteModule = loadSampleData | loadPatternData | loadPluginData | loadPluginInstance, loadNoPatternOrPluginData = loadSampleData, loadNoPluginInstance = loadSampleData | loadPatternData | loadPluginData, }; #define PROBE_RECOMMENDED_SIZE 2048u static constexpr std::size_t ProbeRecommendedSize = PROBE_RECOMMENDED_SIZE; enum ProbeFlags { ProbeModules = 0x1, ProbeContainers = 0x2, ProbeFlagsDefault = ProbeModules | ProbeContainers, ProbeFlagsNone = 0 }; enum ProbeResult { ProbeSuccess = 1, ProbeFailure = 0, ProbeWantMoreData = -1 }; static ProbeResult ProbeAdditionalSize(MemoryFileReader &file, const uint64 *pfilesize, uint64 minimumAdditionalSize); static ProbeResult Probe(ProbeFlags flags, mpt::span data, const uint64 *pfilesize); public: #ifdef MODPLUG_TRACKER // Get parent CModDoc. Can be nullptr if previewing from tree view, and is always nullptr if we're not actually compiling OpenMPT. CModDoc *GetpModDoc() const noexcept { return m_pModDoc; } #endif // MODPLUG_TRACKER bool Create(FileReader file, ModLoadingFlags loadFlags = loadCompleteModule, CModDoc *pModDoc = nullptr); private: bool CreateInternal(FileReader file, ModLoadingFlags loadFlags); public: bool Destroy(); Enum GetType() const noexcept { return m_nType; } MODCONTAINERTYPE GetContainerType() const noexcept { return m_ContainerType; } // rough heuristic, could be improved mpt::Charset GetCharsetFile() const // 8bit string encoding of strings in the on-disk file { return m_modFormat.charset; } mpt::Charset GetCharsetInternal() const // 8bit string encoding of strings internal in CSoundFile { #if defined(MODPLUG_TRACKER) return mpt::Charset::Locale; #else // MODPLUG_TRACKER return GetCharsetFile(); #endif // MODPLUG_TRACKER } ModMessageHeuristicOrder GetMessageHeuristic() const; void SetPreAmp(uint32 vol); uint32 GetPreAmp() const noexcept { return m_MixerSettings.m_nPreAmp; } void SetMixLevels(MixLevels levels); MixLevels GetMixLevels() const noexcept { return m_nMixLevels; } const CSoundFilePlayConfig &GetPlayConfig() const noexcept { return m_PlayConfig; } constexpr INSTRUMENTINDEX GetNumInstruments() const noexcept { return m_nInstruments; } constexpr SAMPLEINDEX GetNumSamples() const noexcept { return m_nSamples; } constexpr PATTERNINDEX GetCurrentPattern() const noexcept { return m_PlayState.m_nPattern; } constexpr ORDERINDEX GetCurrentOrder() const noexcept { return m_PlayState.m_nCurrentOrder; } constexpr CHANNELINDEX GetNumChannels() const noexcept { return m_nChannels; } constexpr bool CanAddMoreSamples(SAMPLEINDEX amount = 1) const noexcept { return (amount < MAX_SAMPLES) && m_nSamples < (MAX_SAMPLES - amount); } constexpr bool CanAddMoreInstruments(INSTRUMENTINDEX amount = 1) const noexcept { return (amount < MAX_INSTRUMENTS) && m_nInstruments < (MAX_INSTRUMENTS - amount); } #ifndef NO_PLUGINS IMixPlugin* GetInstrumentPlugin(INSTRUMENTINDEX instr) const noexcept; #endif const CModSpecifications& GetModSpecifications() const {return *m_pModSpecs;} static const CModSpecifications& GetModSpecifications(const MODTYPE type); static ChannelFlags GetChannelMuteFlag(); #ifdef MODPLUG_TRACKER void PatternTranstionChnSolo(const CHANNELINDEX chnIndex); void PatternTransitionChnUnmuteAll(); protected: void HandlePatternTransitionEvents(); #endif // MODPLUG_TRACKER public: double GetCurrentBPM() const; void DontLoopPattern(PATTERNINDEX nPat, ROWINDEX nRow = 0); CHANNELINDEX GetMixStat() const { return m_nMixStat; } void ResetMixStat() { m_nMixStat = 0; } void ResetPlayPos(); void SetCurrentOrder(ORDERINDEX nOrder); std::string GetTitle() const { return m_songName; } bool SetTitle(const std::string &newTitle); // Return true if title was changed. const char *GetSampleName(SAMPLEINDEX nSample) const; const char *GetInstrumentName(INSTRUMENTINDEX nInstr) const; uint32 GetMusicSpeed() const { return m_PlayState.m_nMusicSpeed; } TEMPO GetMusicTempo() const { return m_PlayState.m_nMusicTempo; } constexpr bool IsFirstTick() const noexcept { return (m_PlayState.m_lTotalSampleCount == 0); } // Get song duration in various cases: total length, length to specific order & row, etc. std::vector GetLength(enmGetLengthResetMode adjustMode, GetLengthTarget target = GetLengthTarget()); public: void RecalculateSamplesPerTick(); double GetRowDuration(TEMPO tempo, uint32 speed) const; uint32 GetTickDuration(PlayState &playState) const; // A repeat count value of -1 means infinite loop void SetRepeatCount(int n) { m_nRepeatCount = n; } int GetRepeatCount() const { return m_nRepeatCount; } bool IsPaused() const { return m_SongFlags[SONG_PAUSED | SONG_STEP]; } // Added SONG_STEP as it seems to be desirable in most cases to check for this as well. void LoopPattern(PATTERNINDEX nPat, ROWINDEX nRow = 0); bool InitChannel(CHANNELINDEX nChn); void InitAmigaResampler(); void InitOPL(); static constexpr bool SupportsOPL(MODTYPE type) noexcept { return type & (MOD_TYPE_S3M | MOD_TYPE_MPT); } bool SupportsOPL() const noexcept { return SupportsOPL(m_nType); } #if !defined(MPT_WITH_ANCIENT) static ProbeResult ProbeFileHeaderMMCMP(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderPP20(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderXPK(MemoryFileReader file, const uint64 *pfilesize); #endif // !MPT_WITH_ANCIENT static ProbeResult ProbeFileHeaderUMX(MemoryFileReader file, const uint64* pfilesize); static ProbeResult ProbeFileHeader669(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderAM(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderAMF_Asylum(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderAMF_DSMI(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderAMS(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderAMS2(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderC67(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderDBM(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderDTM(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderDIGI(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderDMF(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderDSM(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderDSym(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderFAR(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderFMT(MemoryFileReader file, const uint64* pfilesize); static ProbeResult ProbeFileHeaderGDM(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderICE(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderIMF(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderIT(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderITP(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderJ2B(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderMUS_KM(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderM15(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderMDL(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderMED(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderMO3(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderMOD(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderMT2(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderMTM(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderOKT(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderPLM(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderPSM(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderPSM16(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderPT36(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderPTM(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderS3M(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderSFX(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderSTM(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderSTP(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderSTX(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderSymMOD(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderULT(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderXM(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderMID(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderUAX(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderWAV(MemoryFileReader file, const uint64 *pfilesize); // Module Loaders bool Read669(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadAM(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadAMF_Asylum(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadAMF_DSMI(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadAMS(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadAMS2(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadC67(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadDBM(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadDTM(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadDIGI(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadDMF(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadDSM(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadDSym(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadFAR(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadFMT(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadGDM(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadICE(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadIMF(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadIT(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadITP(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadJ2B(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadMUS_KM(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadM15(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadMDL(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadMED(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadMO3(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadMOD(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadMT2(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadMTM(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadOKT(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadPLM(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadPSM(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadPSM16(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadPT36(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadPTM(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadS3M(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadSFX(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadSTM(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadSTP(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadSTX(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadSymMOD(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadULT(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadXM(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadMID(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadUAX(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadWAV(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); static std::vector GetSupportedExtensions(bool otherFormats); static bool IsExtensionSupported(std::string_view ext); // UTF8, casing of ext is ignored static mpt::ustring ModContainerTypeToString(MODCONTAINERTYPE containertype); static mpt::ustring ModContainerTypeToTracker(MODCONTAINERTYPE containertype); // Repair non-standard stuff in modules saved with previous ModPlug versions void UpgradeModule(); // Save Functions #ifndef MODPLUG_NO_FILESAVE bool SaveXM(std::ostream &f, bool compatibilityExport = false); bool SaveS3M(std::ostream &f) const; bool SaveMod(std::ostream &f) const; bool SaveIT(std::ostream &f, const mpt::PathString &filename, bool compatibilityExport = false); uint32 SaveMixPlugins(std::ostream *file=nullptr, bool bUpdate=true); void WriteInstrumentPropertyForAllInstruments(uint32 code, uint16 size, std::ostream &f, INSTRUMENTINDEX nInstruments) const; void SaveExtendedInstrumentProperties(INSTRUMENTINDEX nInstruments, std::ostream &f) const; void SaveExtendedSongProperties(std::ostream &f) const; #endif // MODPLUG_NO_FILESAVE void LoadExtendedSongProperties(FileReader &file, bool ignoreChannelCount, bool* pInterpretMptMade = nullptr); void LoadMPTMProperties(FileReader &file, uint16 cwtv); static mpt::ustring GetSchismTrackerVersion(uint16 cwtv, uint32 reserved); // Reads extended instrument properties(XM/IT/MPTM). // Returns true if extended instrument properties were found. bool LoadExtendedInstrumentProperties(FileReader &file); void SetDefaultPlaybackBehaviour(MODTYPE type); static PlayBehaviourSet GetSupportedPlaybackBehaviour(MODTYPE type); static PlayBehaviourSet GetDefaultPlaybackBehaviour(MODTYPE type); // MOD Convert function MODTYPE GetBestSaveFormat() const; static void ConvertModCommand(ModCommand &m); static void S3MConvert(ModCommand &m, bool fromIT); void S3MSaveConvert(uint8 &command, uint8 ¶m, bool toIT, bool compatibilityExport = false) const; void ModSaveCommand(uint8 &command, uint8 ¶m, const bool toXM, const bool compatibilityExport = false) const; static void ReadMODPatternEntry(FileReader &file, ModCommand &m); static void ReadMODPatternEntry(const std::array data, ModCommand &m); void SetupMODPanning(bool bForceSetup = false); // Setup LRRL panning, max channel volume public: // Real-time sound functions void SuspendPlugins(); void ResumePlugins(); void StopAllVsti(); void RecalculateGainForAllPlugs(); void ResetChannels(); samplecount_t Read(samplecount_t count, IAudioTarget &target) { AudioSourceNone source; return Read(count, target, source); } samplecount_t Read( samplecount_t count, IAudioTarget &target, IAudioSource &source, std::optional> outputMonitor = std::nullopt, std::optional> inputMonitor = std::nullopt ); samplecount_t ReadOneTick(); private: void CreateStereoMix(int count); public: bool FadeSong(uint32 msec); private: void ProcessDSP(uint32 countChunk); void ProcessPlugins(uint32 nCount); void ProcessInputChannels(IAudioSource &source, std::size_t countChunk); public: samplecount_t GetTotalSampleCount() const { return m_PlayState.m_lTotalSampleCount; } bool HasPositionChanged() { bool b = m_PlayState.m_bPositionChanged; m_PlayState.m_bPositionChanged = false; return b; } bool IsRenderingToDisc() const { return m_bIsRendering; } void PrecomputeSampleLoops(bool updateChannels = false); public: // Mixer Config void SetMixerSettings(const MixerSettings &mixersettings); void SetResamplerSettings(const CResamplerSettings &resamplersettings); void InitPlayer(bool bReset=false); void SetDspEffects(uint32 DSPMask); uint32 GetSampleRate() const { return m_MixerSettings.gdwMixingFreq; } #ifndef NO_EQ void SetEQGains(const uint32 *pGains, const uint32 *pFreqs, bool bReset = false) { m_EQ.SetEQGains(pGains, pFreqs, bReset, m_MixerSettings.gdwMixingFreq); } // 0=-12dB, 32=+12dB #endif // NO_EQ public: bool ReadNote(); bool ProcessRow(); bool ProcessEffects(); std::pair NextRow(PlayState &playState, const bool breakRow) const; void SetupNextRow(PlayState &playState, const bool patternLoop) const; CHANNELINDEX GetNNAChannel(CHANNELINDEX nChn) const; CHANNELINDEX CheckNNA(CHANNELINDEX nChn, uint32 instr, int note, bool forceCut); void NoteChange(ModChannel &chn, int note, bool bPorta = false, bool bResetEnv = true, bool bManual = false, CHANNELINDEX channelHint = CHANNELINDEX_INVALID) const; void InstrumentChange(ModChannel &chn, uint32 instr, bool bPorta = false, bool bUpdVol = true, bool bResetEnv = true) const; void ApplyInstrumentPanning(ModChannel &chn, const ModInstrument *instr, const ModSample *smp) const; uint32 CalculateXParam(PATTERNINDEX pat, ROWINDEX row, CHANNELINDEX chn, uint32 *extendedRows = nullptr) const; // Channel Effects void KeyOff(ModChannel &chn) const; // Global Effects void SetTempo(TEMPO param, bool setAsNonModcommand = false); void SetSpeed(PlayState &playState, uint32 param) const; static TEMPO ConvertST2Tempo(uint8 tempo); void ProcessRamping(ModChannel &chn) const; protected: // Global variable initializer for loader functions void SetType(MODTYPE type); void InitializeGlobals(MODTYPE type = MOD_TYPE_NONE); void InitializeChannels(); // Channel effect processing int GetVibratoDelta(int type, int position) const; void ProcessVolumeSwing(ModChannel &chn, int &vol) const; void ProcessPanningSwing(ModChannel &chn) const; void ProcessTremolo(ModChannel &chn, int &vol) const; void ProcessTremor(CHANNELINDEX nChn, int &vol); bool IsEnvelopeProcessed(const ModChannel &chn, EnvelopeType env) const; void ProcessVolumeEnvelope(ModChannel &chn, int &vol) const; void ProcessPanningEnvelope(ModChannel &chn) const; int ProcessPitchFilterEnvelope(ModChannel &chn, int32 &period) const; void IncrementEnvelopePosition(ModChannel &chn, EnvelopeType envType) const; void IncrementEnvelopePositions(ModChannel &chn) const; void ProcessInstrumentFade(ModChannel &chn, int &vol) const; static void ProcessPitchPanSeparation(int32 &pan, int note, const ModInstrument &instr); void ProcessPanbrello(ModChannel &chn) const; void ProcessArpeggio(CHANNELINDEX nChn, int32 &period, Tuning::NOTEINDEXTYPE &arpeggioSteps); void ProcessVibrato(CHANNELINDEX nChn, int32 &period, Tuning::RATIOTYPE &vibratoFactor); void ProcessSampleAutoVibrato(ModChannel &chn, int32 &period, Tuning::RATIOTYPE &vibratoFactor, int &nPeriodFrac) const; std::pair GetChannelIncrement(const ModChannel &chn, uint32 period, int periodFrac) const; protected: // Type of panning command enum PanningType { Pan4bit = 4, Pan6bit = 6, Pan8bit = 8, }; // Channel Effects void UpdateS3MEffectMemory(ModChannel &chn, ModCommand::PARAM param) const; void PortamentoUp(CHANNELINDEX nChn, ModCommand::PARAM param, const bool doFinePortamentoAsRegular = false); void PortamentoDown(CHANNELINDEX nChn, ModCommand::PARAM param, const bool doFinePortamentoAsRegular = false); void MidiPortamento(CHANNELINDEX nChn, int param, bool doFineSlides); void FinePortamentoUp(ModChannel &chn, ModCommand::PARAM param) const; void FinePortamentoDown(ModChannel &chn, ModCommand::PARAM param) const; void ExtraFinePortamentoUp(ModChannel &chn, ModCommand::PARAM param) const; void ExtraFinePortamentoDown(ModChannel &chn, ModCommand::PARAM param) const; void PortamentoMPT(ModChannel &chn, int); void PortamentoFineMPT(ModChannel &chn, int); void PortamentoExtraFineMPT(ModChannel &chn, int); void SetFinetune(CHANNELINDEX channel, PlayState &playState, bool isSmooth) const; void NoteSlide(ModChannel &chn, uint32 param, bool slideUp, bool retrig) const; std::pair GetVolCmdTonePorta(const ModCommand &m, uint32 startTick) const; void TonePortamento(ModChannel &chn, uint16 param) const; void Vibrato(ModChannel &chn, uint32 param) const; void FineVibrato(ModChannel &chn, uint32 param) const; void VolumeSlide(ModChannel &chn, ModCommand::PARAM param) const; void PanningSlide(ModChannel &chn, ModCommand::PARAM param, bool memory = true) const; void ChannelVolSlide(ModChannel &chn, ModCommand::PARAM param) const; void FineVolumeUp(ModChannel &chn, ModCommand::PARAM param, bool volCol) const; void FineVolumeDown(ModChannel &chn, ModCommand::PARAM param, bool volCol) const; void Tremolo(ModChannel &chn, uint32 param) const; void Panbrello(ModChannel &chn, uint32 param) const; void Panning(ModChannel &chn, uint32 param, PanningType panBits) const; void RetrigNote(CHANNELINDEX nChn, int param, int offset = 0); void ProcessSampleOffset(ModChannel &chn, CHANNELINDEX nChn, const PlayState &playState) const; void SampleOffset(ModChannel &chn, SmpLength param) const; void ReverseSampleOffset(ModChannel &chn, ModCommand::PARAM param) const; void DigiBoosterSampleReverse(ModChannel &chn, ModCommand::PARAM param) const; void HandleDigiSamplePlayDirection(PlayState &state, CHANNELINDEX chn) const; void NoteCut(CHANNELINDEX nChn, uint32 nTick, bool cutSample); void PatternLoop(PlayState &state, ModChannel &chn, ModCommand::PARAM param) const; bool HandleNextRow(PlayState &state, const ModSequence &order, bool honorPatternLoop) const; void ExtendedMODCommands(CHANNELINDEX nChn, ModCommand::PARAM param); void ExtendedS3MCommands(CHANNELINDEX nChn, ModCommand::PARAM param); void ExtendedChannelEffect(ModChannel &chn, uint32 param); void InvertLoop(ModChannel &chn); void PositionJump(PlayState &state, CHANNELINDEX chn) const; ROWINDEX PatternBreak(PlayState &state, CHANNELINDEX chn, uint8 param) const; void GlobalVolSlide(ModCommand::PARAM param, uint8 &nOldGlobalVolSlide); void ProcessMacroOnChannel(CHANNELINDEX nChn); void ProcessMIDIMacro(PlayState &playState, CHANNELINDEX nChn, bool isSmooth, const MIDIMacroConfigData::Macro ¯o, uint8 param = 0, PLUGINDEX plugin = 0); void ParseMIDIMacro(PlayState &playState, CHANNELINDEX nChn, bool isSmooth, const mpt::span macro, mpt::span &out, uint8 param = 0, PLUGINDEX plugin = 0) const; static float CalculateSmoothParamChange(const PlayState &playState, float currentValue, float param); void SendMIDIData(PlayState &playState, CHANNELINDEX nChn, bool isSmooth, const mpt::span macro, PLUGINDEX plugin); void SendMIDINote(CHANNELINDEX chn, uint16 note, uint16 volume); int SetupChannelFilter(ModChannel &chn, bool bReset, int envModifier = 256) const; // Low-Level effect processing void DoFreqSlide(ModChannel &chn, int32 &period, int32 amount, bool isTonePorta = false) const; void UpdateTimeSignature(); public: // Convert frequency to IT cutoff (0...127) uint8 FrequencyToCutOff(double frequency) const; // Convert IT cutoff (0...127 + modifier) to frequency uint32 CutOffToFrequency(uint32 nCutOff, int envModifier = 256) const; // [0-127] => [1-10KHz] // Returns true if periods are actually plain frequency values in Hz. bool PeriodsAreFrequencies() const noexcept { return m_playBehaviour[kPeriodsAreHertz] && !UseFinetuneAndTranspose(); } // Returns true if the current format uses transpose+finetune rather than frequency in Hz to specify middle-C. static constexpr bool UseFinetuneAndTranspose(MODTYPE type) noexcept { return (type & (MOD_TYPE_AMF0 | MOD_TYPE_DIGI | MOD_TYPE_MED | MOD_TYPE_MOD | MOD_TYPE_MTM | MOD_TYPE_OKT | MOD_TYPE_SFX | MOD_TYPE_STP | MOD_TYPE_XM)); } bool UseFinetuneAndTranspose() const noexcept { return UseFinetuneAndTranspose(GetType()); } bool DestroySample(SAMPLEINDEX nSample); bool DestroySampleThreadsafe(SAMPLEINDEX nSample); // Find an unused sample slot. If it is going to be assigned to an instrument, targetInstrument should be specified. // SAMPLEINDEX_INVLAID is returned if no free sample slot could be found. SAMPLEINDEX GetNextFreeSample(INSTRUMENTINDEX targetInstrument = INSTRUMENTINDEX_INVALID, SAMPLEINDEX start = 1) const; // Find an unused instrument slot. // INSTRUMENTINDEX_INVALID is returned if no free instrument slot could be found. INSTRUMENTINDEX GetNextFreeInstrument(INSTRUMENTINDEX start = 1) const; // Check whether a given sample is used by a given instrument. bool IsSampleReferencedByInstrument(SAMPLEINDEX sample, INSTRUMENTINDEX instr) const; ModInstrument *AllocateInstrument(INSTRUMENTINDEX instr, SAMPLEINDEX assignedSample = 0); bool DestroyInstrument(INSTRUMENTINDEX nInstr, deleteInstrumentSamples removeSamples); bool RemoveInstrumentSamples(INSTRUMENTINDEX nInstr, SAMPLEINDEX keepSample = SAMPLEINDEX_INVALID); SAMPLEINDEX DetectUnusedSamples(std::vector &sampleUsed) const; SAMPLEINDEX RemoveSelectedSamples(const std::vector &keepSamples); // Set the autovibrato settings for all samples associated to the given instrument. void PropagateXMAutoVibrato(INSTRUMENTINDEX ins, VibratoType type, uint8 sweep, uint8 depth, uint8 rate); // Samples file I/O bool ReadSampleFromFile(SAMPLEINDEX nSample, FileReader &file, bool mayNormalize = false, bool includeInstrumentFormats = true); bool ReadWAVSample(SAMPLEINDEX nSample, FileReader &file, bool mayNormalize = false, FileReader *wsmpChunk = nullptr); protected: bool ReadW64Sample(SAMPLEINDEX nSample, FileReader &file, bool mayNormalize = false); bool ReadPATSample(SAMPLEINDEX nSample, FileReader &file); bool ReadS3ISample(SAMPLEINDEX nSample, FileReader &file); bool ReadSBISample(SAMPLEINDEX sample, FileReader &file); bool ReadCAFSample(SAMPLEINDEX nSample, FileReader &file, bool mayNormalize = false); bool ReadAIFFSample(SAMPLEINDEX nSample, FileReader &file, bool mayNormalize = false); bool ReadAUSample(SAMPLEINDEX nSample, FileReader &file, bool mayNormalize = false); bool ReadXISample(SAMPLEINDEX nSample, FileReader &file); bool ReadITSSample(SAMPLEINDEX nSample, FileReader &file, bool rewind = true); bool ReadITISample(SAMPLEINDEX nSample, FileReader &file); bool ReadIFFSample(SAMPLEINDEX sample, FileReader &file); bool ReadBRRSample(SAMPLEINDEX sample, FileReader &file); bool ReadFLACSample(SAMPLEINDEX sample, FileReader &file); bool ReadOpusSample(SAMPLEINDEX sample, FileReader &file); bool ReadVorbisSample(SAMPLEINDEX sample, FileReader &file); bool ReadMP3Sample(SAMPLEINDEX sample, FileReader &file, bool raw = false, bool mo3Decode = false); // raw: ignore all encoder-/decodr-delays, decode just raw frames ; mod3Decode: skip metadata and loop-precompute bool ReadMediaFoundationSample(SAMPLEINDEX sample, FileReader &file, bool mo3Decode = false); // mod3Decode: skip metadata and loop-precompute public: #ifdef MODPLUG_TRACKER static std::vector GetMediaFoundationFileTypes(); #endif // MODPLUG_TRACKER #ifndef MODPLUG_NO_FILESAVE bool SaveWAVSample(SAMPLEINDEX nSample, std::ostream &f) const; bool SaveRAWSample(SAMPLEINDEX nSample, std::ostream &f) const; bool SaveFLACSample(SAMPLEINDEX nSample, std::ostream &f) const; bool SaveS3ISample(SAMPLEINDEX smp, std::ostream &f) const; #endif // Instrument file I/O bool ReadInstrumentFromFile(INSTRUMENTINDEX nInstr, FileReader &file, bool mayNormalize = false); bool ReadSampleAsInstrument(INSTRUMENTINDEX nInstr, FileReader &file, bool mayNormalize = false); protected: bool ReadXIInstrument(INSTRUMENTINDEX nInstr, FileReader &file); bool ReadITIInstrument(INSTRUMENTINDEX nInstr, FileReader &file); bool ReadPATInstrument(INSTRUMENTINDEX nInstr, FileReader &file); bool ReadSFZInstrument(INSTRUMENTINDEX nInstr, FileReader &file); public: #ifndef MODPLUG_NO_FILESAVE bool SaveXIInstrument(INSTRUMENTINDEX nInstr, std::ostream &f) const; bool SaveITIInstrument(INSTRUMENTINDEX nInstr, std::ostream &f, const mpt::PathString &filename, bool compress, bool allowExternal) const; bool SaveSFZInstrument(INSTRUMENTINDEX nInstr, std::ostream &f, const mpt::PathString &filename, bool useFLACsamples) const; #endif // I/O from another sound file bool ReadInstrumentFromSong(INSTRUMENTINDEX targetInstr, const CSoundFile &srcSong, INSTRUMENTINDEX sourceInstr); bool ReadSampleFromSong(SAMPLEINDEX targetSample, const CSoundFile &srcSong, SAMPLEINDEX sourceSample); // Period/Note functions uint32 GetNoteFromPeriod(uint32 period, int32 nFineTune = 0, uint32 nC5Speed = 0) const; uint32 GetPeriodFromNote(uint32 note, int32 nFineTune, uint32 nC5Speed) const; uint32 GetFreqFromPeriod(uint32 period, uint32 c5speed, int32 nPeriodFrac = 0) const; // Misc functions ModSample &GetSample(SAMPLEINDEX sample) { MPT_ASSERT(sample <= m_nSamples && sample < std::size(Samples)); return Samples[sample]; } const ModSample &GetSample(SAMPLEINDEX sample) const { MPT_ASSERT(sample <= m_nSamples && sample < std::size(Samples)); return Samples[sample]; } // Resolve note/instrument combination to real sample index. Return value is guaranteed to be in [0, GetNumSamples()]. SAMPLEINDEX GetSampleIndex(ModCommand::NOTE note, uint32 instr) const noexcept; uint32 MapMidiInstrument(uint8 program, uint16 bank, uint8 midiChannel, uint8 note, bool isXG, std::bitset<16> drumChns); size_t ITInstrToMPT(FileReader &file, ModInstrument &ins, uint16 trkvers); bool LoadMixPlugins(FileReader &file); #ifndef NO_PLUGINS static void ReadMixPluginChunk(FileReader &file, SNDMIXPLUGIN &plugin); void ProcessMidiOut(CHANNELINDEX nChn); #endif // NO_PLUGINS void ProcessGlobalVolume(long countChunk); void ProcessStereoSeparation(long countChunk); private: PLUGINDEX GetChannelPlugin(const PlayState &playState, CHANNELINDEX nChn, PluginMutePriority respectMutes) const; static PLUGINDEX GetActiveInstrumentPlugin(const ModChannel &chn, PluginMutePriority respectMutes); IMixPlugin *GetChannelInstrumentPlugin(const ModChannel &chn) const; public: PLUGINDEX GetBestPlugin(const PlayState &playState, CHANNELINDEX nChn, PluginPriority priority, PluginMutePriority respectMutes) const; }; #ifndef NO_PLUGINS inline IMixPlugin* CSoundFile::GetInstrumentPlugin(INSTRUMENTINDEX instr) const noexcept { if(instr > 0 && instr <= GetNumInstruments() && Instruments[instr] && Instruments[instr]->nMixPlug && Instruments[instr]->nMixPlug <= MAX_MIXPLUGINS) return m_MixPlugins[Instruments[instr]->nMixPlug - 1].pMixPlugin; else return nullptr; } #endif // NO_PLUGINS /////////////////////////////////////////////////////////// // Low-level Mixing functions #define FADESONGDELAY 100 MPT_CONSTEXPRINLINE int8 MOD2XMFineTune(int v) { return static_cast(static_cast(v) << 4); } MPT_CONSTEXPRINLINE int8 XM2MODFineTune(int v) { return static_cast(static_cast(v) >> 4); } // Read instrument property with 'code' and 'size' from 'file' to instrument 'pIns'. void ReadInstrumentExtensionField(ModInstrument* pIns, const uint32 code, const uint16 size, FileReader &file); // Read instrument property with 'code' from 'file' to instrument 'pIns'. void ReadExtendedInstrumentProperty(ModInstrument* pIns, const uint32 code, FileReader &file); // Read extended instrument properties from 'file' to instrument 'pIns'. void ReadExtendedInstrumentProperties(ModInstrument* pIns, FileReader &file); OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Snd_flt.cpp0000644000175000017500000001222514154475105020200 00000000000000/* * Snd_flt.cpp * ----------- * Purpose: Calculation of resonant filter coefficients. * Notes : Extended filter range was introduced in MPT 1.12 and went up to 8652 Hz. * MPT 1.16 upped this to the current 10670 Hz. * We have no way of telling whether a file was made with MPT 1.12 or 1.16 though. * Authors: Olivier Lapicque * OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Sndfile.h" #include "Tables.h" #include "../common/misc_util.h" #include "mpt/base/numbers.hpp" OPENMPT_NAMESPACE_BEGIN // AWE32: cutoff = reg[0-255] * 31.25 + 100 -> [100Hz-8060Hz] // EMU10K1 docs: cutoff = reg[0-127]*62+100 uint8 CSoundFile::FrequencyToCutOff(double frequency) const { // IT Cutoff is computed as cutoff = 110 * 2 ^ (0.25 + x/y), where x is the cutoff and y defines the filter range. // Reversed, this gives us x = (log2(cutoff / 110) - 0.25) * y. // <==========> Rewrite as x = (log2(cutoff) - log2(110) - 0.25) * y. // <==========> Rewrite as x = (ln(cutoff) - ln(110) - 0.25*ln(2)) * y/ln(2). // <4.8737671609324025> double cutoff = (std::log(frequency) - 4.8737671609324025) * (m_SongFlags[SONG_EXFILTERRANGE] ? (20.0 / mpt::numbers::ln2) : (24.0 / mpt::numbers::ln2)); Limit(cutoff, 0.0, 127.0); return mpt::saturate_round(cutoff); } uint32 CSoundFile::CutOffToFrequency(uint32 nCutOff, int envModifier) const { MPT_ASSERT(nCutOff < 128); float computedCutoff = static_cast(nCutOff * (envModifier + 256)); // 0...127*512 float Fc; if(GetType() != MOD_TYPE_IMF) { Fc = 110.0f * std::pow(2.0f, 0.25f + computedCutoff / (m_SongFlags[SONG_EXFILTERRANGE] ? 20.0f * 512.0f : 24.0f * 512.0f)); } else { // EMU8000: Documentation says the cutoff is in quarter semitones, with 0x00 being 125 Hz and 0xFF being 8 kHz // The first half of the sentence contradicts the second, though. Fc = 125.0f * std::pow(2.0f, computedCutoff * 6.0f / (127.0f * 512.0f)); } int freq = mpt::saturate_round(Fc); Limit(freq, 120, 20000); if(freq * 2 > (int)m_MixerSettings.gdwMixingFreq) freq = m_MixerSettings.gdwMixingFreq / 2; return static_cast(freq); } // Simple 2-poles resonant filter. Returns computed cutoff in range [0, 254] or -1 if filter is not applied. int CSoundFile::SetupChannelFilter(ModChannel &chn, bool bReset, int envModifier) const { int cutoff = static_cast(chn.nCutOff) + chn.nCutSwing; int resonance = static_cast(chn.nResonance & 0x7F) + chn.nResSwing; Limit(cutoff, 0, 127); Limit(resonance, 0, 127); if(!m_playBehaviour[kMPTOldSwingBehaviour]) { chn.nCutOff = (uint8)cutoff; chn.nCutSwing = 0; chn.nResonance = (uint8)resonance; chn.nResSwing = 0; } // envModifier is in [-256, 256], so cutoff is in [0, 127 * 2] after this calculation. const int computedCutoff = cutoff * (envModifier + 256) / 256; // Filtering is only ever done in IT if either cutoff is not full or if resonance is set. if(m_playBehaviour[kITFilterBehaviour] && resonance == 0 && computedCutoff >= 254) { if(chn.rowCommand.IsNote() && !chn.rowCommand.IsPortamento() && !chn.nMasterChn && chn.triggerNote) { // Z7F next to a note disables the filter, however in other cases this should not happen. // Test cases: filter-reset.it, filter-reset-carry.it, filter-reset-envelope.it, filter-nna.it, FilterResetPatDelay.it chn.dwFlags.reset(CHN_FILTER); } return -1; } chn.dwFlags.set(CHN_FILTER); // 2 * damping factor const float dmpfac = std::pow(10.0f, -resonance * ((24.0f / 128.0f) / 20.0f)); const float fc = CutOffToFrequency(cutoff, envModifier) * (2.0f * mpt::numbers::pi_v); float d, e; if(m_playBehaviour[kITFilterBehaviour] && !m_SongFlags[SONG_EXFILTERRANGE]) { const float r = m_MixerSettings.gdwMixingFreq / fc; d = dmpfac * r + dmpfac - 1.0f; e = r * r; } else { const float r = fc / m_MixerSettings.gdwMixingFreq; d = (1.0f - 2.0f * dmpfac) * r; LimitMax(d, 2.0f); d = (2.0f * dmpfac - d) / r; e = 1.0f / (r * r); } float fg = 1.0f / (1.0f + d + e); float fb0 = (d + e + e) / (1 + d + e); float fb1 = -e / (1.0f + d + e); #if defined(MPT_INTMIXER) #define MPT_FILTER_CONVERT(x) mpt::saturate_round((x) * (1 << MIXING_FILTER_PRECISION)) #else #define MPT_FILTER_CONVERT(x) (x) #endif switch(chn.nFilterMode) { case FilterMode::HighPass: chn.nFilter_A0 = MPT_FILTER_CONVERT(1.0f - fg); chn.nFilter_B0 = MPT_FILTER_CONVERT(fb0); chn.nFilter_B1 = MPT_FILTER_CONVERT(fb1); #ifdef MPT_INTMIXER chn.nFilter_HP = -1; #else chn.nFilter_HP = 1.0f; #endif // MPT_INTMIXER break; default: chn.nFilter_A0 = MPT_FILTER_CONVERT(fg); chn.nFilter_B0 = MPT_FILTER_CONVERT(fb0); chn.nFilter_B1 = MPT_FILTER_CONVERT(fb1); #ifdef MPT_INTMIXER if(chn.nFilter_A0 == 0) chn.nFilter_A0 = 1; // Prevent silence at low filter cutoff and very high sampling rate chn.nFilter_HP = 0; #else chn.nFilter_HP = 0; #endif // MPT_INTMIXER break; } #undef MPT_FILTER_CONVERT if (bReset) { chn.nFilter_Y[0][0] = chn.nFilter_Y[0][1] = 0; chn.nFilter_Y[1][0] = chn.nFilter_Y[1][1] = 0; } return computedCutoff; } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Snd_fx.cpp0000644000175000017500000062150614175042045020034 00000000000000/* * Snd_fx.cpp * ----------- * Purpose: Processing of pattern commands, song length calculation... * Notes : This needs some heavy refactoring. * I thought of actually adding an effect interface class. Every pattern effect * could then be moved into its own class that inherits from the effect interface. * If effect handling differs severly between module formats, every format would have * its own class for that effect. Then, a call chain of effect classes could be set up * for each format, since effects cannot be processed in the same order in all formats. * Authors: Olivier Lapicque * OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Sndfile.h" #include "mod_specifications.h" #ifdef MODPLUG_TRACKER #include "../mptrack/Moddoc.h" #endif // MODPLUG_TRACKER #include "tuning.h" #include "Tables.h" #include "modsmp_ctrl.h" // For updating the loop wraparound data with the invert loop effect #include "plugins/PlugInterface.h" #include "OPL.h" #include "MIDIEvents.h" OPENMPT_NAMESPACE_BEGIN // Formats which have 7-bit (0...128) instead of 6-bit (0...64) global volume commands, or which are imported to this range (mostly formats which are converted to IT internally) #ifdef MODPLUG_TRACKER static constexpr auto GLOBALVOL_7BIT_FORMATS_EXT = MOD_TYPE_MT2; #else static constexpr auto GLOBALVOL_7BIT_FORMATS_EXT = MOD_TYPE_NONE; #endif // MODPLUG_TRACKER static constexpr auto GLOBALVOL_7BIT_FORMATS = MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_IMF | MOD_TYPE_J2B | MOD_TYPE_MID | MOD_TYPE_AMS | MOD_TYPE_DBM | MOD_TYPE_PTM | MOD_TYPE_MDL | MOD_TYPE_DTM | GLOBALVOL_7BIT_FORMATS_EXT; // Compensate frequency slide LUTs depending on whether we are handling periods or frequency - "up" and "down" in function name are seen from frequency perspective. static uint32 GetLinearSlideDownTable (const CSoundFile *sndFile, uint32 i) { MPT_ASSERT(i < std::size(LinearSlideDownTable)); return sndFile->m_playBehaviour[kPeriodsAreHertz] ? LinearSlideDownTable[i] : LinearSlideUpTable[i]; } static uint32 GetLinearSlideUpTable (const CSoundFile *sndFile, uint32 i) { MPT_ASSERT(i < std::size(LinearSlideDownTable)); return sndFile->m_playBehaviour[kPeriodsAreHertz] ? LinearSlideUpTable[i] : LinearSlideDownTable[i]; } static uint32 GetFineLinearSlideDownTable(const CSoundFile *sndFile, uint32 i) { MPT_ASSERT(i < std::size(FineLinearSlideDownTable)); return sndFile->m_playBehaviour[kPeriodsAreHertz] ? FineLinearSlideDownTable[i] : FineLinearSlideUpTable[i]; } static uint32 GetFineLinearSlideUpTable (const CSoundFile *sndFile, uint32 i) { MPT_ASSERT(i < std::size(FineLinearSlideDownTable)); return sndFile->m_playBehaviour[kPeriodsAreHertz] ? FineLinearSlideUpTable[i] : FineLinearSlideDownTable[i]; } //////////////////////////////////////////////////////////// // Length // Memory class for GetLength() code class GetLengthMemory { protected: const CSoundFile &sndFile; public: std::unique_ptr state; struct ChnSettings { uint32 ticksToRender = 0; // When using sample sync, we still need to render this many ticks bool incChanged = false; // When using sample sync, note frequency has changed uint8 vol = 0xFF; }; std::vector chnSettings; double elapsedTime; static constexpr uint32 IGNORE_CHANNEL = uint32_max; GetLengthMemory(const CSoundFile &sf) : sndFile(sf) , state(std::make_unique(sf.m_PlayState)) { Reset(); } void Reset() { if(state->m_midiMacroEvaluationResults) state->m_midiMacroEvaluationResults.emplace(); elapsedTime = 0.0; state->m_lTotalSampleCount = 0; state->m_nMusicSpeed = sndFile.m_nDefaultSpeed; state->m_nMusicTempo = sndFile.m_nDefaultTempo; state->m_nGlobalVolume = sndFile.m_nDefaultGlobalVolume; chnSettings.assign(sndFile.GetNumChannels(), ChnSettings()); const auto muteFlag = CSoundFile::GetChannelMuteFlag(); for(CHANNELINDEX chn = 0; chn < sndFile.GetNumChannels(); chn++) { state->Chn[chn].Reset(ModChannel::resetTotal, sndFile, chn, muteFlag); state->Chn[chn].nOldGlobalVolSlide = 0; state->Chn[chn].nOldChnVolSlide = 0; state->Chn[chn].nNote = state->Chn[chn].nNewNote = state->Chn[chn].nLastNote = NOTE_NONE; } } // Increment playback position of sample and envelopes on a channel void RenderChannel(CHANNELINDEX channel, uint32 tickDuration, uint32 portaStart = uint32_max) { ModChannel &chn = state->Chn[channel]; uint32 numTicks = chnSettings[channel].ticksToRender; if(numTicks == IGNORE_CHANNEL || numTicks == 0 || (!chn.IsSamplePlaying() && !chnSettings[channel].incChanged) || chn.pModSample == nullptr) { return; } const SamplePosition loopStart(chn.dwFlags[CHN_LOOP] ? chn.nLoopStart : 0u, 0); const SamplePosition sampleEnd(chn.dwFlags[CHN_LOOP] ? chn.nLoopEnd : chn.nLength, 0); const SmpLength loopLength = chn.nLoopEnd - chn.nLoopStart; const bool itEnvMode = sndFile.m_playBehaviour[kITEnvelopePositionHandling]; const bool updatePitchEnv = (chn.PitchEnv.flags & (ENV_ENABLED | ENV_FILTER)) == ENV_ENABLED; bool stopNote = false; SamplePosition inc = chn.increment * tickDuration; if(chn.dwFlags[CHN_PINGPONGFLAG]) inc.Negate(); for(uint32 i = 0; i < numTicks; i++) { bool updateInc = (chn.PitchEnv.flags & (ENV_ENABLED | ENV_FILTER)) == ENV_ENABLED; if(i >= portaStart) { chn.isFirstTick = false; const ModCommand &m = *sndFile.Patterns[state->m_nPattern].GetpModCommand(state->m_nRow, channel); auto command = m.command; if(m.volcmd == VOLCMD_TONEPORTAMENTO) { const auto [porta, clearEffectCommand] = sndFile.GetVolCmdTonePorta(m, 0); sndFile.TonePortamento(chn, porta); if(clearEffectCommand) command = CMD_NONE; } if(command == CMD_TONEPORTAMENTO) sndFile.TonePortamento(chn, m.param); else if(command == CMD_TONEPORTAVOL) sndFile.TonePortamento(chn, 0); updateInc = true; } int32 period = chn.nPeriod; if(itEnvMode) sndFile.IncrementEnvelopePositions(chn); if(updatePitchEnv) { sndFile.ProcessPitchFilterEnvelope(chn, period); updateInc = true; } if(!itEnvMode) sndFile.IncrementEnvelopePositions(chn); int vol = 0; sndFile.ProcessInstrumentFade(chn, vol); if(chn.dwFlags[CHN_ADLIB]) continue; if(updateInc || chnSettings[channel].incChanged) { if(chn.m_CalculateFreq || chn.m_ReCalculateFreqOnFirstTick) { chn.RecalcTuningFreq(1, 0, sndFile); if(!chn.m_CalculateFreq) chn.m_ReCalculateFreqOnFirstTick = false; else chn.m_CalculateFreq = false; } chn.increment = sndFile.GetChannelIncrement(chn, period, 0).first; chnSettings[channel].incChanged = false; inc = chn.increment * tickDuration; if(chn.dwFlags[CHN_PINGPONGFLAG]) inc.Negate(); } chn.position += inc; if(chn.position >= sampleEnd || (chn.position < loopStart && inc.IsNegative())) { if(!chn.dwFlags[CHN_LOOP]) { // Past sample end. stopNote = true; break; } // We exceeded the sample loop, go back to loop start. if(chn.dwFlags[CHN_PINGPONGLOOP]) { if(chn.position < loopStart) { chn.position = SamplePosition(chn.nLoopStart + chn.nLoopStart, 0) - chn.position; chn.dwFlags.flip(CHN_PINGPONGFLAG); inc.Negate(); } SmpLength posInt = chn.position.GetUInt() - chn.nLoopStart; SmpLength pingpongLength = loopLength * 2; if(sndFile.m_playBehaviour[kITPingPongMode]) pingpongLength--; posInt %= pingpongLength; bool forward = (posInt < loopLength); if(forward) chn.position.SetInt(chn.nLoopStart + posInt); else chn.position.SetInt(chn.nLoopEnd - (posInt - loopLength)); if(forward == chn.dwFlags[CHN_PINGPONGFLAG]) { chn.dwFlags.flip(CHN_PINGPONGFLAG); inc.Negate(); } } else { SmpLength posInt = chn.position.GetUInt(); if(posInt >= chn.nLoopEnd + loopLength) { const SmpLength overshoot = posInt - chn.nLoopEnd; posInt -= (overshoot / loopLength) * loopLength; } while(posInt >= chn.nLoopEnd) { posInt -= loopLength; } chn.position.SetInt(posInt); } } } if(stopNote) { chn.Stop(); chn.nPortamentoDest = 0; } chnSettings[channel].ticksToRender = 0; } }; // Get mod length in various cases. Parameters: // [in] adjustMode: See enmGetLengthResetMode for possible adjust modes. // [in] target: Time or position target which should be reached, or no target to get length of the first sub song. Use GetLengthTarget::StartPos to also specify a position from where the seeking should begin. // [out] See definition of type GetLengthType for the returned values. std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMode, GetLengthTarget target) { std::vector results; GetLengthType retval; // Are we trying to reach a certain pattern position? const bool hasSearchTarget = target.mode != GetLengthTarget::NoTarget && target.mode != GetLengthTarget::GetAllSubsongs; const bool adjustSamplePos = (adjustMode & eAdjustSamplePositions) == eAdjustSamplePositions; SEQUENCEINDEX sequence = target.sequence; if(sequence >= Order.GetNumSequences()) sequence = Order.GetCurrentSequenceIndex(); const ModSequence &orderList = Order(sequence); GetLengthMemory memory(*this); CSoundFile::PlayState &playState = *memory.state; // Temporary visited rows vector (so that GetLength() won't interfere with the player code if the module is playing at the same time) RowVisitor visitedRows(*this, sequence); ROWINDEX allowedPatternLoopComplexity = 32768; // If sequence starts with some non-existent patterns, find a better start while(target.startOrder < orderList.size() && !orderList.IsValidPat(target.startOrder)) { target.startOrder++; target.startRow = 0; } retval.startRow = playState.m_nNextRow = playState.m_nRow = target.startRow; retval.startOrder = playState.m_nNextOrder = playState.m_nCurrentOrder = target.startOrder; // Fast LUTs for commands that are too weird / complicated / whatever to emulate in sample position adjust mode. std::bitset forbiddenCommands; std::bitset forbiddenVolCommands; if(adjustSamplePos) { forbiddenCommands.set(CMD_ARPEGGIO); forbiddenCommands.set(CMD_PORTAMENTOUP); forbiddenCommands.set(CMD_PORTAMENTODOWN); forbiddenCommands.set(CMD_XFINEPORTAUPDOWN); forbiddenCommands.set(CMD_NOTESLIDEUP); forbiddenCommands.set(CMD_NOTESLIDEUPRETRIG); forbiddenCommands.set(CMD_NOTESLIDEDOWN); forbiddenCommands.set(CMD_NOTESLIDEDOWNRETRIG); forbiddenVolCommands.set(VOLCMD_PORTAUP); forbiddenVolCommands.set(VOLCMD_PORTADOWN); if(target.mode == GetLengthTarget::SeekPosition && target.pos.order < orderList.size()) { // If we know where to seek, we can directly rule out any channels on which a new note would be triggered right at the start. const PATTERNINDEX seekPat = orderList[target.pos.order]; if(Patterns.IsValidPat(seekPat) && Patterns[seekPat].IsValidRow(target.pos.row)) { const ModCommand *m = Patterns[seekPat].GetpModCommand(target.pos.row, 0); for(CHANNELINDEX i = 0; i < GetNumChannels(); i++, m++) { if(m->note == NOTE_NOTECUT || m->note == NOTE_KEYOFF || (m->note == NOTE_FADE && GetNumInstruments()) || (m->IsNote() && !m->IsPortamento())) { memory.chnSettings[i].ticksToRender = GetLengthMemory::IGNORE_CHANNEL; } } } } } if(adjustMode & eAdjust) playState.m_midiMacroEvaluationResults.emplace(); // If samples are being synced, force them to resync if tick duration changes uint32 oldTickDuration = 0; bool breakToRow = false; for (;;) { const bool ignoreRow = NextRow(playState, breakToRow).first; // Time target reached. if(target.mode == GetLengthTarget::SeekSeconds && memory.elapsedTime >= target.time) { retval.targetReached = true; break; } // Check if pattern is valid playState.m_nPattern = playState.m_nCurrentOrder < orderList.size() ? orderList[playState.m_nCurrentOrder] : orderList.GetInvalidPatIndex(); if(!Patterns.IsValidPat(playState.m_nPattern) && playState.m_nPattern != orderList.GetInvalidPatIndex() && target.mode == GetLengthTarget::SeekPosition && playState.m_nCurrentOrder == target.pos.order) { // Early test: Target is inside +++ or non-existing pattern retval.targetReached = true; break; } while(playState.m_nPattern >= Patterns.Size()) { // End of song? if((playState.m_nPattern == orderList.GetInvalidPatIndex()) || (playState.m_nCurrentOrder >= orderList.size())) { if(playState.m_nCurrentOrder == orderList.GetRestartPos()) break; else playState.m_nCurrentOrder = orderList.GetRestartPos(); } else { playState.m_nCurrentOrder++; } playState.m_nPattern = (playState.m_nCurrentOrder < orderList.size()) ? orderList[playState.m_nCurrentOrder] : orderList.GetInvalidPatIndex(); playState.m_nNextOrder = playState.m_nCurrentOrder; if((!Patterns.IsValidPat(playState.m_nPattern)) && visitedRows.Visit(playState.m_nCurrentOrder, 0, playState.Chn, ignoreRow)) { if(!hasSearchTarget) { retval.lastOrder = playState.m_nCurrentOrder; retval.lastRow = 0; } if(target.mode == GetLengthTarget::NoTarget || !visitedRows.GetFirstUnvisitedRow(playState.m_nNextOrder, playState.m_nRow, true)) { // We aren't searching for a specific row, or we couldn't find any more unvisited rows. break; } else { // We haven't found the target row yet, but we found some other unplayed row... continue searching from here. retval.duration = memory.elapsedTime; results.push_back(retval); retval.startRow = playState.m_nRow; retval.startOrder = playState.m_nNextOrder; memory.Reset(); playState.m_nCurrentOrder = playState.m_nNextOrder; playState.m_nPattern = orderList[playState.m_nCurrentOrder]; playState.m_nNextRow = playState.m_nRow; break; } } } if(playState.m_nNextOrder == ORDERINDEX_INVALID) { // GetFirstUnvisitedRow failed, so there is nothing more to play break; } // Skip non-existing patterns if(!Patterns.IsValidPat(playState.m_nPattern)) { // If there isn't even a tune, we should probably stop here. if(playState.m_nCurrentOrder == orderList.GetRestartPos()) { if(target.mode == GetLengthTarget::NoTarget || !visitedRows.GetFirstUnvisitedRow(playState.m_nNextOrder, playState.m_nRow, true)) { // We aren't searching for a specific row, or we couldn't find any more unvisited rows. break; } else { // We haven't found the target row yet, but we found some other unplayed row... continue searching from here. retval.duration = memory.elapsedTime; results.push_back(retval); retval.startRow = playState.m_nRow; retval.startOrder = playState.m_nNextOrder; memory.Reset(); playState.m_nNextRow = playState.m_nRow; continue; } } playState.m_nNextOrder = playState.m_nCurrentOrder + 1; continue; } // Should never happen if(playState.m_nRow >= Patterns[playState.m_nPattern].GetNumRows()) playState.m_nRow = 0; // Check whether target was reached. if(target.mode == GetLengthTarget::SeekPosition && playState.m_nCurrentOrder == target.pos.order && playState.m_nRow == target.pos.row) { retval.targetReached = true; break; } // If pattern loops are nested too deeply, they can cause an effectively infinite amount of loop evalations to be generated. // As we don't want the user to wait forever, we bail out if the pattern loops are too complex. const bool moduleTooComplex = target.mode != GetLengthTarget::SeekSeconds && visitedRows.ModuleTooComplex(allowedPatternLoopComplexity); if(moduleTooComplex) { memory.elapsedTime = std::numeric_limits::infinity(); // Decrease allowed complexity with each subsong, as this seems to be a malicious module if(allowedPatternLoopComplexity > 256) allowedPatternLoopComplexity /= 2; visitedRows.ResetComplexity(); } if(visitedRows.Visit(playState.m_nCurrentOrder, playState.m_nRow, playState.Chn, ignoreRow) || moduleTooComplex) { if(!hasSearchTarget) { retval.lastOrder = playState.m_nCurrentOrder; retval.lastRow = playState.m_nRow; } if(target.mode == GetLengthTarget::NoTarget || !visitedRows.GetFirstUnvisitedRow(playState.m_nNextOrder, playState.m_nRow, true)) { // We aren't searching for a specific row, or we couldn't find any more unvisited rows. break; } else { // We haven't found the target row yet, but we found some other unplayed row... continue searching from here. retval.duration = memory.elapsedTime; results.push_back(retval); retval.startRow = playState.m_nRow; retval.startOrder = playState.m_nNextOrder; memory.Reset(); playState.m_nNextRow = playState.m_nRow; continue; } } retval.endOrder = playState.m_nCurrentOrder; retval.endRow = playState.m_nRow; // Update next position SetupNextRow(playState, false); // Jumped to invalid pattern row? if(playState.m_nRow >= Patterns[playState.m_nPattern].GetNumRows()) { playState.m_nRow = 0; } if(ignoreRow) continue; // For various effects, we need to know first how many ticks there are in this row. const ModCommand *p = Patterns[playState.m_nPattern].GetpModCommand(playState.m_nRow, 0); const bool ignoreMutedChn = m_playBehaviour[kST3NoMutedChannels]; for(CHANNELINDEX nChn = 0; nChn < GetNumChannels(); nChn++, p++) { ModChannel &chn = playState.Chn[nChn]; if(p->IsEmpty() || (ignoreMutedChn && ChnSettings[nChn].dwFlags[CHN_MUTE])) // not even effects are processed on muted S3M channels { chn.rowCommand.Clear(); continue; } if(p->IsPcNote()) { #ifndef NO_PLUGINS if(playState.m_midiMacroEvaluationResults && p->instr > 0 && p->instr <= MAX_MIXPLUGINS) { playState.m_midiMacroEvaluationResults->pluginParameter[{static_cast(p->instr - 1), p->GetValueVolCol()}] = p->GetValueEffectCol() / PlugParamValue(ModCommand::maxColumnValue); } #endif // NO_PLUGINS chn.rowCommand.Clear(); continue; } chn.rowCommand = *p; switch(p->command) { case CMD_SPEED: SetSpeed(playState, p->param); break; case CMD_TEMPO: if(m_playBehaviour[kMODVBlankTiming]) { // ProTracker MODs with VBlank timing: All Fxx parameters set the tick count. if(p->param != 0) SetSpeed(playState, p->param); } break; case CMD_S3MCMDEX: if((p->param & 0xF0) == 0x60) { // Fine Pattern Delay playState.m_nFrameDelay += (p->param & 0x0F); } else if((p->param & 0xF0) == 0xE0 && !playState.m_nPatternDelay) { // Pattern Delay if(!(GetType() & MOD_TYPE_S3M) || (p->param & 0x0F) != 0) { // While Impulse Tracker *does* count S60 as a valid row delay (and thus ignores any other row delay commands on the right), // Scream Tracker 3 simply ignores such commands. playState.m_nPatternDelay = 1 + (p->param & 0x0F); } } break; case CMD_MODCMDEX: if((p->param & 0xF0) == 0xE0) { // Pattern Delay playState.m_nPatternDelay = 1 + (p->param & 0x0F); } break; } } const uint32 numTicks = playState.TicksOnRow(); const uint32 nonRowTicks = numTicks - std::max(playState.m_nPatternDelay, uint32(1)); playState.m_patLoopRow = ROWINDEX_INVALID; playState.m_breakRow = ROWINDEX_INVALID; playState.m_posJump = ORDERINDEX_INVALID; for(CHANNELINDEX nChn = 0; nChn < GetNumChannels(); nChn++) { ModChannel &chn = playState.Chn[nChn]; if(chn.rowCommand.IsEmpty()) continue; ModCommand::COMMAND command = chn.rowCommand.command; ModCommand::PARAM param = chn.rowCommand.param; ModCommand::NOTE note = chn.rowCommand.note; if(adjustMode & eAdjust) { if(chn.rowCommand.instr) { chn.nNewIns = chn.rowCommand.instr; chn.nLastNote = NOTE_NONE; memory.chnSettings[nChn].vol = 0xFF; } if(chn.rowCommand.IsNote()) chn.nLastNote = note; // Update channel panning if(chn.rowCommand.IsNote() || chn.rowCommand.instr) { ModInstrument *pIns; if(chn.nNewIns > 0 && chn.nNewIns <= GetNumInstruments() && (pIns = Instruments[chn.nNewIns]) != nullptr) { if(pIns->dwFlags[INS_SETPANNING]) chn.SetInstrumentPan(pIns->nPan, *this); } const SAMPLEINDEX smp = GetSampleIndex(note, chn.nNewIns); if(smp > 0) { if(Samples[smp].uFlags[CHN_PANNING]) chn.SetInstrumentPan(Samples[smp].nPan, *this); } } switch(chn.rowCommand.volcmd) { case VOLCMD_VOLUME: memory.chnSettings[nChn].vol = chn.rowCommand.vol; break; case VOLCMD_VOLSLIDEUP: case VOLCMD_VOLSLIDEDOWN: if(chn.rowCommand.vol != 0) chn.nOldVolParam = chn.rowCommand.vol; break; case VOLCMD_TONEPORTAMENTO: if(chn.rowCommand.vol) { const auto [porta, clearEffectCommand] = GetVolCmdTonePorta(chn.rowCommand, 0); chn.portamentoSlide = porta; if(clearEffectCommand) command = CMD_NONE; } break; } } switch(command) { // Position Jump case CMD_POSITIONJUMP: PositionJump(playState, nChn); break; // Pattern Break case CMD_PATTERNBREAK: if(ROWINDEX row = PatternBreak(playState, nChn, param); row != ROWINDEX_INVALID) playState.m_breakRow = row; break; // Set Tempo case CMD_TEMPO: if(!m_playBehaviour[kMODVBlankTiming]) { TEMPO tempo(CalculateXParam(playState.m_nPattern, playState.m_nRow, nChn), 0); if ((adjustMode & eAdjust) && (GetType() & (MOD_TYPE_S3M | MOD_TYPE_IT | MOD_TYPE_MPT))) { if (tempo.GetInt()) chn.nOldTempo = static_cast(tempo.GetInt()); else tempo.Set(chn.nOldTempo); } if (tempo.GetInt() >= 0x20) playState.m_nMusicTempo = tempo; else { // Tempo Slide TEMPO tempoDiff((tempo.GetInt() & 0x0F) * nonRowTicks, 0); if ((tempo.GetInt() & 0xF0) == 0x10) { playState.m_nMusicTempo += tempoDiff; } else { if(tempoDiff < playState.m_nMusicTempo) playState.m_nMusicTempo -= tempoDiff; else playState.m_nMusicTempo.Set(0); } } TEMPO tempoMin = GetModSpecifications().GetTempoMin(), tempoMax = GetModSpecifications().GetTempoMax(); if(m_playBehaviour[kTempoClamp]) // clamp tempo correctly in compatible mode { tempoMax.Set(255); } Limit(playState.m_nMusicTempo, tempoMin, tempoMax); } break; case CMD_S3MCMDEX: switch(param & 0xF0) { case 0x90: if(param <= 0x91) chn.dwFlags.set(CHN_SURROUND, param == 0x91); break; case 0xA0: // High sample offset chn.nOldHiOffset = param & 0x0F; break; case 0xB0: // Pattern Loop PatternLoop(playState, chn, param & 0x0F); break; case 0xF0: // Active macro chn.nActiveMacro = param & 0x0F; break; } break; case CMD_MODCMDEX: switch(param & 0xF0) { case 0x60: // Pattern Loop PatternLoop(playState, chn, param & 0x0F); break; case 0xF0: // Active macro chn.nActiveMacro = param & 0x0F; break; } break; case CMD_XFINEPORTAUPDOWN: // ignore high offset in compatible mode if(((param & 0xF0) == 0xA0) && !m_playBehaviour[kFT2RestrictXCommand]) chn.nOldHiOffset = param & 0x0F; break; } // The following calculations are not interesting if we just want to get the song length. if(!(adjustMode & eAdjust)) continue; switch(command) { // Portamento Up/Down case CMD_PORTAMENTOUP: if(param) { // FT2 compatibility: Separate effect memory for all portamento commands // Test case: Porta-LinkMem.xm if(!m_playBehaviour[kFT2PortaUpDownMemory]) chn.nOldPortaDown = param; chn.nOldPortaUp = param; } break; case CMD_PORTAMENTODOWN: if(param) { // FT2 compatibility: Separate effect memory for all portamento commands // Test case: Porta-LinkMem.xm if(!m_playBehaviour[kFT2PortaUpDownMemory]) chn.nOldPortaUp = param; chn.nOldPortaDown = param; } break; // Tone-Portamento case CMD_TONEPORTAMENTO: if (param) chn.portamentoSlide = param; break; // Offset case CMD_OFFSET: if(param) chn.oldOffset = param << 8; break; // Volume Slide case CMD_VOLUMESLIDE: case CMD_TONEPORTAVOL: if (param) chn.nOldVolumeSlide = param; break; // Set Volume case CMD_VOLUME: memory.chnSettings[nChn].vol = param; break; // Global Volume case CMD_GLOBALVOLUME: if(!(GetType() & GLOBALVOL_7BIT_FORMATS) && param < 128) param *= 2; // IT compatibility 16. ST3 and IT ignore out-of-range values if(param <= 128) { playState.m_nGlobalVolume = param * 2; } else if(!(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_S3M))) { playState.m_nGlobalVolume = 256; } break; // Global Volume Slide case CMD_GLOBALVOLSLIDE: if(m_playBehaviour[kPerChannelGlobalVolSlide]) { // IT compatibility 16. Global volume slide params are stored per channel (FT2/IT) if (param) chn.nOldGlobalVolSlide = param; else param = chn.nOldGlobalVolSlide; } else { if (param) playState.Chn[0].nOldGlobalVolSlide = param; else param = playState.Chn[0].nOldGlobalVolSlide; } if (((param & 0x0F) == 0x0F) && (param & 0xF0)) { param >>= 4; if (!(GetType() & GLOBALVOL_7BIT_FORMATS)) param <<= 1; playState.m_nGlobalVolume += param << 1; } else if (((param & 0xF0) == 0xF0) && (param & 0x0F)) { param = (param & 0x0F) << 1; if (!(GetType() & GLOBALVOL_7BIT_FORMATS)) param <<= 1; playState.m_nGlobalVolume -= param; } else if (param & 0xF0) { param >>= 4; param <<= 1; if (!(GetType() & GLOBALVOL_7BIT_FORMATS)) param <<= 1; playState.m_nGlobalVolume += param * nonRowTicks; } else { param = (param & 0x0F) << 1; if (!(GetType() & GLOBALVOL_7BIT_FORMATS)) param <<= 1; playState.m_nGlobalVolume -= param * nonRowTicks; } Limit(playState.m_nGlobalVolume, 0, 256); break; case CMD_CHANNELVOLUME: if (param <= 64) chn.nGlobalVol = param; break; case CMD_CHANNELVOLSLIDE: { if (param) chn.nOldChnVolSlide = param; else param = chn.nOldChnVolSlide; int32 volume = chn.nGlobalVol; if((param & 0x0F) == 0x0F && (param & 0xF0)) volume += (param >> 4); // Fine Up else if((param & 0xF0) == 0xF0 && (param & 0x0F)) volume -= (param & 0x0F); // Fine Down else if(param & 0x0F) // Down volume -= (param & 0x0F) * nonRowTicks; else // Up volume += ((param & 0xF0) >> 4) * nonRowTicks; Limit(volume, 0, 64); chn.nGlobalVol = volume; } break; case CMD_PANNING8: Panning(chn, param, Pan8bit); break; case CMD_MODCMDEX: if(param < 0x10) { // LED filter for(CHANNELINDEX channel = 0; channel < GetNumChannels(); channel++) { playState.Chn[channel].dwFlags.set(CHN_AMIGAFILTER, !(param & 1)); } } [[fallthrough]]; case CMD_S3MCMDEX: if((param & 0xF0) == 0x80) { Panning(chn, (param & 0x0F), Pan4bit); } break; case CMD_VIBRATOVOL: if (param) chn.nOldVolumeSlide = param; param = 0; [[fallthrough]]; case CMD_VIBRATO: Vibrato(chn, param); break; case CMD_FINEVIBRATO: FineVibrato(chn, param); break; case CMD_TREMOLO: Tremolo(chn, param); break; case CMD_PANBRELLO: Panbrello(chn, param); break; case CMD_MIDI: case CMD_SMOOTHMIDI: if(param < 0x80) ProcessMIDIMacro(playState, nChn, false, m_MidiCfg.SFx[chn.nActiveMacro], chn.rowCommand.param, 0); else ProcessMIDIMacro(playState, nChn, false, m_MidiCfg.Zxx[param & 0x7F], chn.rowCommand.param, 0); break; default: break; } switch(chn.rowCommand.volcmd) { case VOLCMD_PANNING: Panning(chn, chn.rowCommand.vol, Pan6bit); break; case VOLCMD_VIBRATOSPEED: // FT2 does not automatically enable vibrato with the "set vibrato speed" command if(m_playBehaviour[kFT2VolColVibrato]) chn.nVibratoSpeed = chn.rowCommand.vol & 0x0F; else Vibrato(chn, chn.rowCommand.vol << 4); break; case VOLCMD_VIBRATODEPTH: Vibrato(chn, chn.rowCommand.vol); break; } // Process vibrato / tremolo / panbrello switch(chn.rowCommand.command) { case CMD_VIBRATO: case CMD_FINEVIBRATO: case CMD_VIBRATOVOL: if(adjustMode & eAdjust) { uint32 vibTicks = ((GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) && !m_SongFlags[SONG_ITOLDEFFECTS]) ? numTicks : nonRowTicks; uint32 inc = chn.nVibratoSpeed * vibTicks; if(m_playBehaviour[kITVibratoTremoloPanbrello]) inc *= 4; chn.nVibratoPos += static_cast(inc); } break; case CMD_TREMOLO: if(adjustMode & eAdjust) { uint32 tremTicks = ((GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) && !m_SongFlags[SONG_ITOLDEFFECTS]) ? numTicks : nonRowTicks; uint32 inc = chn.nTremoloSpeed * tremTicks; if(m_playBehaviour[kITVibratoTremoloPanbrello]) inc *= 4; chn.nTremoloPos += static_cast(inc); } break; case CMD_PANBRELLO: if(adjustMode & eAdjust) { // Panbrello effect is permanent in compatible mode, so actually apply panbrello for the last tick of this row chn.nPanbrelloPos += static_cast(chn.nPanbrelloSpeed * (numTicks - 1)); ProcessPanbrello(chn); } break; } } // Interpret F00 effect in XM files as "stop song" if(GetType() == MOD_TYPE_XM && playState.m_nMusicSpeed == uint16_max) { break; } playState.m_nCurrentRowsPerBeat = m_nDefaultRowsPerBeat; if(Patterns[playState.m_nPattern].GetOverrideSignature()) { playState.m_nCurrentRowsPerBeat = Patterns[playState.m_nPattern].GetRowsPerBeat(); } const uint32 tickDuration = GetTickDuration(playState); const uint32 rowDuration = tickDuration * numTicks; memory.elapsedTime += static_cast(rowDuration) / static_cast(m_MixerSettings.gdwMixingFreq); playState.m_lTotalSampleCount += rowDuration; if(adjustSamplePos) { // Super experimental and dirty sample seeking for(CHANNELINDEX nChn = 0; nChn < GetNumChannels(); nChn++) { if(memory.chnSettings[nChn].ticksToRender == GetLengthMemory::IGNORE_CHANNEL) continue; ModChannel &chn = playState.Chn[nChn]; const ModCommand &m = chn.rowCommand; if(!chn.nPeriod && m.IsEmpty()) continue; uint32 paramHi = m.param >> 4, paramLo = m.param & 0x0F; uint32 startTick = 0; bool porta = m.command == CMD_TONEPORTAMENTO || m.command == CMD_TONEPORTAVOL || m.volcmd == VOLCMD_TONEPORTAMENTO; bool stopNote = false; if(m.instr) chn.prevNoteOffset = 0; if(m.IsNote()) { if(porta && memory.chnSettings[nChn].incChanged) { // If there's a portamento, the current channel increment mustn't be 0 in NoteChange() chn.increment = GetChannelIncrement(chn, chn.nPeriod, 0).first; } int32 setPan = chn.nPan; chn.nNewNote = chn.nLastNote; if(chn.nNewIns != 0) InstrumentChange(chn, chn.nNewIns, porta); NoteChange(chn, m.note, porta); HandleDigiSamplePlayDirection(playState, nChn); memory.chnSettings[nChn].incChanged = true; if((m.command == CMD_MODCMDEX || m.command == CMD_S3MCMDEX) && (m.param & 0xF0) == 0xD0 && paramLo < numTicks) { startTick = paramLo; } else if(m.command == CMD_DELAYCUT && paramHi < numTicks) { startTick = paramHi; } if(playState.m_nPatternDelay > 1 && startTick != 0 && (GetType() & (MOD_TYPE_S3M | MOD_TYPE_IT | MOD_TYPE_MPT))) { startTick += (playState.m_nMusicSpeed + playState.m_nFrameDelay) * (playState.m_nPatternDelay - 1); } if(!porta) memory.chnSettings[nChn].ticksToRender = 0; // Panning commands have to be re-applied after a note change with potential pan change. if(m.command == CMD_PANNING8 || ((m.command == CMD_MODCMDEX || m.command == CMD_S3MCMDEX) && paramHi == 0x8) || m.volcmd == VOLCMD_PANNING) { chn.nPan = setPan; } } if(m.IsNote() || m_playBehaviour[kApplyOffsetWithoutNote]) { if(m.command == CMD_OFFSET) { ProcessSampleOffset(chn, nChn, playState); } else if(m.command == CMD_OFFSETPERCENTAGE) { SampleOffset(chn, Util::muldiv_unsigned(chn.nLength, m.param, 256)); } else if(m.command == CMD_REVERSEOFFSET && chn.pModSample != nullptr) { memory.RenderChannel(nChn, oldTickDuration); // Re-sync what we've got so far ReverseSampleOffset(chn, m.param); startTick = playState.m_nMusicSpeed - 1; } else if(m.volcmd == VOLCMD_OFFSET) { if(chn.pModSample != nullptr && m.vol <= std::size(chn.pModSample->cues)) { SmpLength offset; if(m.vol == 0) offset = chn.oldOffset; else offset = chn.oldOffset = chn.pModSample->cues[m.vol - 1]; SampleOffset(chn, offset); } } } if(m.note == NOTE_KEYOFF || m.note == NOTE_NOTECUT || (m.note == NOTE_FADE && GetNumInstruments()) || ((m.command == CMD_MODCMDEX || m.command == CMD_S3MCMDEX) && (m.param & 0xF0) == 0xC0 && paramLo < numTicks) || (m.command == CMD_DELAYCUT && paramLo != 0 && startTick + paramLo < numTicks) || m.command == CMD_KEYOFF) { stopNote = true; } if(m.command == CMD_VOLUME) { chn.nVolume = m.param * 4; } else if(m.volcmd == VOLCMD_VOLUME) { chn.nVolume = m.vol * 4; } if(chn.pModSample && !stopNote) { // Check if we don't want to emulate some effect and thus stop processing. if(m.command < MAX_EFFECTS) { if(forbiddenCommands[m.command]) { stopNote = true; } else if(m.command == CMD_MODCMDEX) { // Special case: Slides using extended commands switch(m.param & 0xF0) { case 0x10: case 0x20: stopNote = true; } } } if(m.volcmd < forbiddenVolCommands.size() && forbiddenVolCommands[m.volcmd]) { stopNote = true; } } if(stopNote) { chn.Stop(); memory.chnSettings[nChn].ticksToRender = 0; } else { if(oldTickDuration != tickDuration && oldTickDuration != 0) { memory.RenderChannel(nChn, oldTickDuration); // Re-sync what we've got so far } switch(m.command) { case CMD_TONEPORTAVOL: case CMD_VOLUMESLIDE: case CMD_VIBRATOVOL: if(m.param || (GetType() != MOD_TYPE_MOD)) { for(uint32 i = 0; i < numTicks; i++) { chn.isFirstTick = (i == 0); VolumeSlide(chn, m.param); } } break; case CMD_MODCMDEX: if((m.param & 0x0F) || (GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2))) { chn.isFirstTick = true; switch(m.param & 0xF0) { case 0xA0: FineVolumeUp(chn, m.param & 0x0F, false); break; case 0xB0: FineVolumeDown(chn, m.param & 0x0F, false); break; } } break; case CMD_S3MCMDEX: if(m.param == 0x9E) { // Play forward memory.RenderChannel(nChn, oldTickDuration); // Re-sync what we've got so far chn.dwFlags.reset(CHN_PINGPONGFLAG); } else if(m.param == 0x9F) { // Reverse memory.RenderChannel(nChn, oldTickDuration); // Re-sync what we've got so far chn.dwFlags.set(CHN_PINGPONGFLAG); if(!chn.position.GetInt() && chn.nLength && (m.IsNote() || !chn.dwFlags[CHN_LOOP])) { chn.position.Set(chn.nLength - 1, SamplePosition::fractMax); } } else if((m.param & 0xF0) == 0x70) { if(m.param >= 0x73) chn.InstrumentControl(m.param, *this); } break; case CMD_DIGIREVERSESAMPLE: DigiBoosterSampleReverse(chn, m.param); break; case CMD_FINETUNE: case CMD_FINETUNE_SMOOTH: memory.RenderChannel(nChn, oldTickDuration); // Re-sync what we've got so far SetFinetune(nChn, playState, false); // TODO should render each tick individually for CMD_FINETUNE_SMOOTH for higher sync accuracy break; } chn.isFirstTick = true; switch(m.volcmd) { case VOLCMD_FINEVOLUP: FineVolumeUp(chn, m.vol, m_playBehaviour[kITVolColMemory]); break; case VOLCMD_FINEVOLDOWN: FineVolumeDown(chn, m.vol, m_playBehaviour[kITVolColMemory]); break; case VOLCMD_VOLSLIDEUP: case VOLCMD_VOLSLIDEDOWN: { // IT Compatibility: Volume column volume slides have their own memory // Test case: VolColMemory.it ModCommand::VOL vol = m.vol; if(vol == 0 && m_playBehaviour[kITVolColMemory]) { vol = chn.nOldVolParam; if(vol == 0) break; } if(m.volcmd == VOLCMD_VOLSLIDEUP) vol <<= 4; for(uint32 i = 0; i < numTicks; i++) { chn.isFirstTick = (i == 0); VolumeSlide(chn, vol); } } break; case VOLCMD_PLAYCONTROL: if(m.vol <= 1) chn.isPaused = (m.vol == 0); break; } if(chn.isPaused) continue; if(porta) { // Portamento needs immediate syncing, as the pitch changes on each tick uint32 portaTick = memory.chnSettings[nChn].ticksToRender + startTick + 1; memory.chnSettings[nChn].ticksToRender += numTicks; memory.RenderChannel(nChn, tickDuration, portaTick); } else { memory.chnSettings[nChn].ticksToRender += (numTicks - startTick); } } } } oldTickDuration = tickDuration; breakToRow = HandleNextRow(playState, orderList, false); } // Now advance the sample positions for sample seeking on channels that are still playing if(adjustSamplePos) { for(CHANNELINDEX nChn = 0; nChn < GetNumChannels(); nChn++) { if(memory.chnSettings[nChn].ticksToRender != GetLengthMemory::IGNORE_CHANNEL) { memory.RenderChannel(nChn, oldTickDuration); } } } if(retval.targetReached) { retval.lastOrder = playState.m_nCurrentOrder; retval.lastRow = playState.m_nRow; } retval.duration = memory.elapsedTime; results.push_back(retval); // Store final variables if(adjustMode & eAdjust) { if(retval.targetReached || target.mode == GetLengthTarget::NoTarget) { const auto midiMacroEvaluationResults = std::move(playState.m_midiMacroEvaluationResults); playState.m_midiMacroEvaluationResults.reset(); // Target found, or there is no target (i.e. play whole song)... m_PlayState = std::move(playState); m_PlayState.ResetGlobalVolumeRamping(); m_PlayState.m_nNextRow = m_PlayState.m_nRow; m_PlayState.m_nFrameDelay = m_PlayState.m_nPatternDelay = 0; m_PlayState.m_nTickCount = TICKS_ROW_FINISHED; m_PlayState.m_bPositionChanged = true; if(m_opl != nullptr) m_opl->Reset(); for(CHANNELINDEX n = 0; n < GetNumChannels(); n++) { auto &chn = m_PlayState.Chn[n]; if(chn.nLastNote != NOTE_NONE) { chn.nNewNote = chn.nLastNote; } if(memory.chnSettings[n].vol != 0xFF && !adjustSamplePos) { chn.nVolume = std::min(memory.chnSettings[n].vol, uint8(64)) * 4; } if(chn.pModSample != nullptr && chn.pModSample->uFlags[CHN_ADLIB] && m_opl) { m_opl->Patch(n, chn.pModSample->adlib); m_opl->NoteCut(n); } chn.pCurrentSample = nullptr; } #ifndef NO_PLUGINS // If there were any PC events or MIDI macros updating plugin parameters, update plugin parameters to their latest value. std::bitset plugSetProgram; for(const auto &[plugParam, value] : midiMacroEvaluationResults->pluginParameter) { PLUGINDEX plug = plugParam.first; IMixPlugin *plugin = m_MixPlugins[plug].pMixPlugin; if(plugin != nullptr) { if(!plugSetProgram[plug]) { // Used for bridged plugins to avoid sending out individual messages for each parameter. plugSetProgram.set(plug); plugin->BeginSetProgram(); } plugin->SetParameter(plugParam.second, value); } } if(plugSetProgram.any()) { for(PLUGINDEX i = 0; i < MAX_MIXPLUGINS; i++) { if(plugSetProgram[i]) { m_MixPlugins[i].pMixPlugin->EndSetProgram(); } } } // Do the same for dry/wet ratios for(const auto &[plug, dryWetRatio] : midiMacroEvaluationResults->pluginDryWetRatio) { m_MixPlugins[plug].fDryRatio = dryWetRatio; } #endif // NO_PLUGINS } else if(adjustMode != eAdjustOnSuccess) { // Target not found (e.g. when jumping to a hidden sub song), reset global variables... m_PlayState.m_nMusicSpeed = m_nDefaultSpeed; m_PlayState.m_nMusicTempo = m_nDefaultTempo; m_PlayState.m_nGlobalVolume = m_nDefaultGlobalVolume; } // When adjusting the playback status, we will also want to update the visited rows vector according to the current position. if(sequence != Order.GetCurrentSequenceIndex()) { Order.SetSequence(sequence); } } if(adjustMode & (eAdjust | eAdjustOnlyVisitedRows)) m_visitedRows.MoveVisitedRowsFrom(visitedRows); return results; } ////////////////////////////////////////////////////////////////////////////////////////////////// // Effects // Change sample or instrument number. void CSoundFile::InstrumentChange(ModChannel &chn, uint32 instr, bool bPorta, bool bUpdVol, bool bResetEnv) const { const ModInstrument *pIns = instr <= GetNumInstruments() ? Instruments[instr] : nullptr; const ModSample *pSmp = &Samples[instr]; const auto oldInsVol = chn.nInsVol; ModCommand::NOTE note = chn.nNewNote; if(note == NOTE_NONE && m_playBehaviour[kITInstrWithoutNote]) return; if(pIns != nullptr && ModCommand::IsNote(note)) { // Impulse Tracker ignores empty slots. // We won't ignore them if a plugin is assigned to this slot, so that VSTis still work as intended. // Test case: emptyslot.it, PortaInsNum.it, gxsmp.it, gxsmp2.it if(pIns->Keyboard[note - NOTE_MIN] == 0 && m_playBehaviour[kITEmptyNoteMapSlot] && !pIns->HasValidMIDIChannel()) { chn.pModInstrument = pIns; return; } if(pIns->NoteMap[note - NOTE_MIN] > NOTE_MAX) return; uint32 n = pIns->Keyboard[note - NOTE_MIN]; pSmp = ((n) && (n < MAX_SAMPLES)) ? &Samples[n] : nullptr; } else if(GetNumInstruments()) { // No valid instrument, or not a valid note. if (note >= NOTE_MIN_SPECIAL) return; if(m_playBehaviour[kITEmptyNoteMapSlot] && (pIns == nullptr || !pIns->HasValidMIDIChannel())) { // Impulse Tracker ignores empty slots. // We won't ignore them if a plugin is assigned to this slot, so that VSTis still work as intended. // Test case: emptyslot.it, PortaInsNum.it, gxsmp.it, gxsmp2.it chn.pModInstrument = nullptr; chn.nNewIns = 0; return; } pSmp = nullptr; } bool returnAfterVolumeAdjust = false; // instrumentChanged is used for IT carry-on env option bool instrumentChanged = (pIns != chn.pModInstrument); const bool sampleChanged = (chn.pModSample != nullptr) && (pSmp != chn.pModSample); const bool newTuning = (GetType() == MOD_TYPE_MPT && pIns && pIns->pTuning); if(!bPorta || instrumentChanged || sampleChanged) chn.microTuning = 0; // Playback behavior change for MPT: With portamento don't change sample if it is in // the same instrument as previous sample. if(bPorta && newTuning && pIns == chn.pModInstrument && sampleChanged) return; if(sampleChanged && bPorta) { // IT compatibility: No sample change (also within multi-sample instruments) during portamento when using Compatible Gxx. // Test case: PortaInsNumCompat.it, PortaSampleCompat.it, PortaCutCompat.it if(m_playBehaviour[kITPortamentoInstrument] && m_SongFlags[SONG_ITCOMPATGXX] && !chn.increment.IsZero()) { pSmp = chn.pModSample; } // Special XM hack (also applies to MOD / S3M, except when playing IT-style S3Ms, such as k_vision.s3m) // Test case: PortaSmpChange.mod, PortaSmpChange.s3m, PortaSwap.s3m if((!instrumentChanged && (GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2)) && pIns) || (GetType() == MOD_TYPE_PLM) || (GetType() == MOD_TYPE_MOD && chn.IsSamplePlaying()) || (m_playBehaviour[kST3PortaSampleChange] && chn.IsSamplePlaying())) { // FT2 doesn't change the sample in this case, // but still uses the sample info from the old one (bug?) returnAfterVolumeAdjust = true; } } // IT compatibility: A lone instrument number should only reset sample properties to those of the corresponding sample in instrument mode. // C#5 01 ... <-- sample 1 // C-5 .. g02 <-- sample 2 // ... 01 ... <-- still sample 1, but with properties of sample 2 // In the above example, no sample change happens on the second row. In the third row, sample 1 keeps playing but with the // volume and panning properties of sample 2. // Test case: InstrAfterMultisamplePorta.it if(m_nInstruments && !instrumentChanged && sampleChanged && chn.pCurrentSample != nullptr && m_playBehaviour[kITMultiSampleInstrumentNumber] && !chn.rowCommand.IsNote()) { returnAfterVolumeAdjust = true; } // IT Compatibility: Envelope pickup after SCx cut (but don't do this when working with plugins, or else envelope carry stops working) // Test case: cut-carry.it if(!chn.IsSamplePlaying() && (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) && (!pIns || !pIns->HasValidMIDIChannel())) { instrumentChanged = true; } // FT2 compatibility: new instrument + portamento = ignore new instrument number, but reload old instrument settings (the world of XM is upside down...) // And this does *not* happen if volume column portamento is used together with note delay... (handled in ProcessEffects(), where all the other note delay stuff is.) // Test case: porta-delay.xm, SamplePortaInInstrument.xm if((instrumentChanged || sampleChanged) && bPorta && m_playBehaviour[kFT2PortaIgnoreInstr] && (chn.pModInstrument != nullptr || chn.pModSample != nullptr)) { pIns = chn.pModInstrument; pSmp = chn.pModSample; instrumentChanged = false; } else { chn.pModInstrument = pIns; } // Update Volume if (bUpdVol && (!(GetType() & (MOD_TYPE_MOD | MOD_TYPE_S3M)) || ((pSmp != nullptr && pSmp->HasSampleData()) || chn.HasMIDIOutput()))) { if(pSmp) { if(!pSmp->uFlags[SMP_NODEFAULTVOLUME]) chn.nVolume = pSmp->nVolume; } else if(pIns && pIns->nMixPlug) { chn.nVolume = chn.GetVSTVolume(); } else { chn.nVolume = 0; } } if(returnAfterVolumeAdjust && sampleChanged && pSmp != nullptr) { // ProTracker applies new instrument's finetune but keeps the old sample playing. // Test case: PortaSwapPT.mod if(m_playBehaviour[kMODSampleSwap]) chn.nFineTune = pSmp->nFineTune; // ST3 does it similarly for middle-C speed. // Test case: PortaSwap.s3m, SampleSwap.s3m if(GetType() == MOD_TYPE_S3M && pSmp->HasSampleData()) chn.nC5Speed = pSmp->nC5Speed; } if(returnAfterVolumeAdjust) return; // Instrument adjust chn.nNewIns = 0; // IT Compatiblity: NNA is reset on every note change, not every instrument change (fixes s7xinsnum.it). if (pIns && ((!m_playBehaviour[kITNNAReset] && pSmp) || pIns->nMixPlug || instrumentChanged)) chn.nNNA = pIns->nNNA; // Update volume chn.UpdateInstrumentVolume(pSmp, pIns); // Update panning // FT2 compatibility: Only reset panning on instrument numbers, not notes (bUpdVol condition) // Test case: PanMemory.xm // IT compatibility: Sample and instrument panning is only applied on note change, not instrument change // Test case: PanReset.it if((bUpdVol || !(GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2))) && !m_playBehaviour[kITPanningReset]) { ApplyInstrumentPanning(chn, pIns, pSmp); } // Reset envelopes if(bResetEnv) { // Blurb by Storlek (from the SchismTracker code): // Conditions experimentally determined to cause envelope reset in Impulse Tracker: // - no note currently playing (of course) // - note given, no portamento // - instrument number given, portamento, compat gxx enabled // - instrument number given, no portamento, after keyoff, old effects enabled // If someone can enlighten me to what the logic really is here, I'd appreciate it. // Seems like it's just a total mess though, probably to get XMs to play right. bool reset, resetAlways; // IT Compatibility: Envelope reset // Test case: EnvReset.it if(m_playBehaviour[kITEnvelopeReset]) { const bool insNumber = (instr != 0); reset = (!chn.nLength || (insNumber && bPorta && m_SongFlags[SONG_ITCOMPATGXX]) || (insNumber && !bPorta && chn.dwFlags[CHN_NOTEFADE | CHN_KEYOFF] && m_SongFlags[SONG_ITOLDEFFECTS])); // NOTE: IT2.14 with SB/GUS/etc. output is different. We are going after IT's WAV writer here. // For SB/GUS/etc. emulation, envelope carry should only apply when the NNA isn't set to "Note Cut". // Test case: CarryNNA.it resetAlways = (!chn.nFadeOutVol || instrumentChanged || chn.dwFlags[CHN_KEYOFF]); } else { reset = (!bPorta || !(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_DBM)) || m_SongFlags[SONG_ITCOMPATGXX] || !chn.nLength || (chn.dwFlags[CHN_NOTEFADE] && !chn.nFadeOutVol)); resetAlways = !(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_DBM)) || instrumentChanged || pIns == nullptr || chn.dwFlags[CHN_KEYOFF | CHN_NOTEFADE]; } if(reset) { chn.dwFlags.set(CHN_FASTVOLRAMP); if(pIns != nullptr) { if(resetAlways) { chn.ResetEnvelopes(); } else { if(!pIns->VolEnv.dwFlags[ENV_CARRY]) chn.VolEnv.Reset(); if(!pIns->PanEnv.dwFlags[ENV_CARRY]) chn.PanEnv.Reset(); if(!pIns->PitchEnv.dwFlags[ENV_CARRY]) chn.PitchEnv.Reset(); } } // IT Compatibility: Autovibrato reset if(!m_playBehaviour[kITVibratoTremoloPanbrello]) { chn.nAutoVibDepth = 0; chn.nAutoVibPos = 0; } } else if(pIns != nullptr && !pIns->VolEnv.dwFlags[ENV_ENABLED]) { if(m_playBehaviour[kITPortamentoInstrument]) { chn.VolEnv.Reset(); } else { chn.ResetEnvelopes(); } } } // Invalid sample ? if(pSmp == nullptr && (pIns == nullptr || !pIns->HasValidMIDIChannel())) { chn.pModSample = nullptr; chn.nInsVol = 0; return; } // Tone-Portamento doesn't reset the pingpong direction flag if(bPorta && pSmp == chn.pModSample && pSmp != nullptr) { // If channel length is 0, we cut a previous sample using SCx. In that case, we have to update sample length, loop points, etc... if(GetType() & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_MPT) && chn.nLength != 0) return; // FT2 compatibility: Do not reset key-off status on portamento without instrument number // Test case: Off-Porta.xm if(GetType() != MOD_TYPE_XM || !m_playBehaviour[kITFT2DontResetNoteOffOnPorta] || chn.rowCommand.instr != 0) chn.dwFlags.reset(CHN_KEYOFF | CHN_NOTEFADE); chn.dwFlags = (chn.dwFlags & (CHN_CHANNELFLAGS | CHN_PINGPONGFLAG)); } else //if(!instrumentChanged || chn.rowCommand.instr != 0 || !IsCompatibleMode(TRK_FASTTRACKER2)) // SampleChange.xm? { chn.dwFlags.reset(CHN_KEYOFF | CHN_NOTEFADE); // IT compatibility: Don't change bidi loop direction when no sample nor instrument is changed. if((m_playBehaviour[kITPingPongNoReset] || !(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT))) && pSmp == chn.pModSample && !instrumentChanged) chn.dwFlags = (chn.dwFlags & (CHN_CHANNELFLAGS | CHN_PINGPONGFLAG)); else chn.dwFlags = (chn.dwFlags & CHN_CHANNELFLAGS); if(pIns) { // Copy envelope flags (we actually only need the "enabled" and "pitch" flag) chn.VolEnv.flags = pIns->VolEnv.dwFlags; chn.PanEnv.flags = pIns->PanEnv.dwFlags; chn.PitchEnv.flags = pIns->PitchEnv.dwFlags; // A cutoff frequency of 0 should not be reset just because the filter envelope is enabled. // Test case: FilterEnvReset.it if((pIns->PitchEnv.dwFlags & (ENV_ENABLED | ENV_FILTER)) == (ENV_ENABLED | ENV_FILTER) && !m_playBehaviour[kITFilterBehaviour]) { if(!chn.nCutOff) chn.nCutOff = 0x7F; } if(pIns->IsCutoffEnabled()) chn.nCutOff = pIns->GetCutoff(); if(pIns->IsResonanceEnabled()) chn.nResonance = pIns->GetResonance(); } } if(pSmp == nullptr) { chn.pModSample = nullptr; chn.nLength = 0; return; } if(bPorta && chn.nLength == 0 && (m_playBehaviour[kFT2PortaNoNote] || m_playBehaviour[kITPortaNoNote])) { // IT/FT2 compatibility: If the note just stopped on the previous tick, prevent it from restarting. // Test cases: PortaJustStoppedNote.xm, PortaJustStoppedNote.it chn.increment.Set(0); } // IT compatibility: Note-off with instrument number + Old Effects retriggers envelopes. // If the instrument changes, keep playing the previous sample, but load the new instrument's envelopes. // Test case: ResetEnvNoteOffOldFx.it if(chn.rowCommand.note == NOTE_KEYOFF && m_playBehaviour[kITInstrWithNoteOffOldEffects] && m_SongFlags[SONG_ITOLDEFFECTS] && sampleChanged) { if(chn.pModSample) { chn.dwFlags |= (chn.pModSample->uFlags & CHN_SAMPLEFLAGS); } chn.nInsVol = oldInsVol; chn.nVolume = pSmp->nVolume; if(pSmp->uFlags[CHN_PANNING]) chn.SetInstrumentPan(pSmp->nPan, *this); return; } chn.pModSample = pSmp; chn.nLength = pSmp->nLength; chn.nLoopStart = pSmp->nLoopStart; chn.nLoopEnd = pSmp->nLoopEnd; // ProTracker "oneshot" loops (if loop start is 0, play the whole sample once and then repeat until loop end) if(m_playBehaviour[kMODOneShotLoops] && chn.nLoopStart == 0) chn.nLoopEnd = pSmp->nLength; chn.dwFlags |= (pSmp->uFlags & CHN_SAMPLEFLAGS); // IT Compatibility: Autovibrato reset if(m_playBehaviour[kITVibratoTremoloPanbrello]) { chn.nAutoVibDepth = 0; chn.nAutoVibPos = 0; } if(newTuning) { chn.nC5Speed = pSmp->nC5Speed; chn.m_CalculateFreq = true; chn.nFineTune = 0; } else if(!bPorta || sampleChanged || !(GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM))) { // Don't reset finetune changed by "set finetune" command. // Test case: finetune.xm, finetune.mod // But *do* change the finetune if we switch to a different sample, to fix // Miranda`s axe by Jamson (jam007.xm). chn.nC5Speed = pSmp->nC5Speed; chn.nFineTune = pSmp->nFineTune; } chn.nTranspose = UseFinetuneAndTranspose() ? pSmp->RelativeTone : 0; // FT2 compatibility: Don't reset portamento target with new instrument numbers. // Test case: Porta-Pickup.xm // ProTracker does the same. // Test case: PortaTarget.mod if(!m_playBehaviour[kFT2PortaTargetNoReset] && GetType() != MOD_TYPE_MOD) { chn.nPortamentoDest = 0; } chn.m_PortamentoFineSteps = 0; if(chn.dwFlags[CHN_SUSTAINLOOP]) { chn.nLoopStart = pSmp->nSustainStart; chn.nLoopEnd = pSmp->nSustainEnd; if(chn.dwFlags[CHN_PINGPONGSUSTAIN]) chn.dwFlags.set(CHN_PINGPONGLOOP); chn.dwFlags.set(CHN_LOOP); } if(chn.dwFlags[CHN_LOOP] && chn.nLoopEnd < chn.nLength) chn.nLength = chn.nLoopEnd; // Fix sample position on instrument change. This is needed for IT "on the fly" sample change. // XXX is this actually called? In ProcessEffects(), a note-on effect is emulated if there's an on the fly sample change! if(chn.position.GetUInt() >= chn.nLength) { if((GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT))) { chn.position.Set(0); } } } void CSoundFile::NoteChange(ModChannel &chn, int note, bool bPorta, bool bResetEnv, bool bManual, CHANNELINDEX channelHint) const { if(note < NOTE_MIN) return; const int origNote = note; const ModSample *pSmp = chn.pModSample; const ModInstrument *pIns = chn.pModInstrument; const bool newTuning = (GetType() == MOD_TYPE_MPT && pIns != nullptr && pIns->pTuning); // save the note that's actually used, as it's necessary to properly calculate PPS and stuff const int realnote = note; if((pIns) && (note - NOTE_MIN < (int)std::size(pIns->Keyboard))) { uint32 n = pIns->Keyboard[note - NOTE_MIN]; if((n) && (n < MAX_SAMPLES)) { pSmp = &Samples[n]; } else if(m_playBehaviour[kITEmptyNoteMapSlot] && !chn.HasMIDIOutput()) { // Impulse Tracker ignores empty slots. // We won't ignore them if a plugin is assigned to this slot, so that VSTis still work as intended. // Test case: emptyslot.it, PortaInsNum.it, gxsmp.it, gxsmp2.it return; } note = pIns->NoteMap[note - NOTE_MIN]; } // Key Off if(note > NOTE_MAX) { // Key Off (+ Invalid Note for XM - TODO is this correct?) if(note == NOTE_KEYOFF || !(GetType() & (MOD_TYPE_IT|MOD_TYPE_MPT))) { KeyOff(chn); // IT compatibility: Note-off + instrument releases sample sustain but does not release envelopes or fade the instrument // Test case: noteoff3.it, ResetEnvNoteOffOldFx2.it if(!bPorta && m_playBehaviour[kITInstrWithNoteOffOldEffects] && m_SongFlags[SONG_ITOLDEFFECTS] && chn.rowCommand.instr) chn.dwFlags.reset(CHN_NOTEFADE | CHN_KEYOFF); } else // Invalid Note -> Note Fade { if(/*note == NOTE_FADE && */ GetNumInstruments()) chn.dwFlags.set(CHN_NOTEFADE); } // Note Cut if (note == NOTE_NOTECUT) { if(chn.dwFlags[CHN_ADLIB] && GetType() == MOD_TYPE_S3M) { // OPL voices are not cut but enter the release portion of their envelope // In S3M we can still modify the volume after note-off, in legacy MPTM mode we can't chn.dwFlags.set(CHN_KEYOFF); } else { chn.dwFlags.set(CHN_NOTEFADE | CHN_FASTVOLRAMP); // IT compatibility: Stopping sample playback by setting sample increment to 0 rather than volume // Test case: NoteOffInstr.it if ((!(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT))) || (m_nInstruments != 0 && !m_playBehaviour[kITInstrWithNoteOff])) chn.nVolume = 0; if (m_playBehaviour[kITInstrWithNoteOff]) chn.increment.Set(0); chn.nFadeOutVol = 0; } } // IT compatibility tentative fix: Clear channel note memory (TRANCE_N.IT by A3F). if(m_playBehaviour[kITClearOldNoteAfterCut]) { chn.nNote = chn.nNewNote = NOTE_NONE; } return; } if(newTuning) { if(!bPorta || chn.nNote == NOTE_NONE) chn.nPortamentoDest = 0; else { chn.nPortamentoDest = pIns->pTuning->GetStepDistance(chn.nNote, chn.m_PortamentoFineSteps, static_cast(note), 0); //Here chn.nPortamentoDest means 'steps to slide'. chn.m_PortamentoFineSteps = -chn.nPortamentoDest; } } if(!bPorta && (GetType() & (MOD_TYPE_XM | MOD_TYPE_MED | MOD_TYPE_MT2))) { if(pSmp) { chn.nTranspose = pSmp->RelativeTone; chn.nFineTune = pSmp->nFineTune; } } // IT Compatibility: Update multisample instruments frequency even if instrument is not specified (fixes the guitars in spx-shuttledeparture.it) // Test case: freqreset-noins.it if(!bPorta && pSmp && m_playBehaviour[kITMultiSampleBehaviour]) chn.nC5Speed = pSmp->nC5Speed; if(bPorta && !chn.IsSamplePlaying()) { if(m_playBehaviour[kFT2PortaNoNote]) { // FT2 Compatibility: Ignore notes with portamento if there was no note playing. // Test case: 3xx-no-old-samp.xm chn.nPeriod = 0; return; } else if(m_playBehaviour[kITPortaNoNote]) { // IT Compatibility: Ignore portamento command if no note was playing (e.g. if a previous note has faded out). // Test case: Fade-Porta.it bPorta = false; } } if(UseFinetuneAndTranspose()) { note += chn.nTranspose; // RealNote = PatternNote + RelativeTone; (0..118, 0 = C-0, 118 = A#9) Limit(note, NOTE_MIN + 11, NOTE_MIN + 130); // 119 possible notes } else { Limit(note, NOTE_MIN, NOTE_MAX); } if(m_playBehaviour[kITRealNoteMapping]) { // need to memorize the original note for various effects (e.g. PPS) chn.nNote = static_cast(Clamp(realnote, NOTE_MIN, NOTE_MAX)); } else { chn.nNote = static_cast(note); } chn.m_CalculateFreq = true; chn.isPaused = false; if ((!bPorta) || (GetType() & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_MPT))) chn.nNewIns = 0; uint32 period = GetPeriodFromNote(note, chn.nFineTune, chn.nC5Speed); chn.nPanbrelloOffset = 0; // IT compatibility: Sample and instrument panning is only applied on note change, not instrument change // Test case: PanReset.it if(m_playBehaviour[kITPanningReset]) ApplyInstrumentPanning(chn, pIns, pSmp); // IT compatibility: Pitch/Pan Separation can be overriden by panning commands, and shouldn't be affected by note-off commands // Test case: PitchPanReset.it if(m_playBehaviour[kITPitchPanSeparation] && pIns && pIns->nPPS) { if(!chn.nRestorePanOnNewNote) chn.nRestorePanOnNewNote = static_cast(chn.nPan + 1); ProcessPitchPanSeparation(chn.nPan, origNote, *pIns); } if(bResetEnv && !bPorta) { chn.nVolSwing = chn.nPanSwing = 0; chn.nResSwing = chn.nCutSwing = 0; if(pIns) { // IT Compatiblity: NNA is reset on every note change, not every instrument change (fixes spx-farspacedance.it). if(m_playBehaviour[kITNNAReset]) chn.nNNA = pIns->nNNA; if(!pIns->VolEnv.dwFlags[ENV_CARRY]) chn.VolEnv.Reset(); if(!pIns->PanEnv.dwFlags[ENV_CARRY]) chn.PanEnv.Reset(); if(!pIns->PitchEnv.dwFlags[ENV_CARRY]) chn.PitchEnv.Reset(); // Volume Swing if(pIns->nVolSwing) { chn.nVolSwing = static_cast(((mpt::random(AccessPRNG()) * pIns->nVolSwing) / 64 + 1) * (m_playBehaviour[kITSwingBehaviour] ? chn.nInsVol : ((chn.nVolume + 1) / 2)) / 199); } // Pan Swing if(pIns->nPanSwing) { chn.nPanSwing = static_cast(((mpt::random(AccessPRNG()) * pIns->nPanSwing * 4) / 128)); if(!m_playBehaviour[kITSwingBehaviour] && chn.nRestorePanOnNewNote == 0) { chn.nRestorePanOnNewNote = static_cast(chn.nPan + 1); } } // Cutoff Swing if(pIns->nCutSwing) { int32 d = ((int32)pIns->nCutSwing * (int32)(static_cast(mpt::random(AccessPRNG())) + 1)) / 128; chn.nCutSwing = static_cast((d * chn.nCutOff + 1) / 128); chn.nRestoreCutoffOnNewNote = chn.nCutOff + 1; } // Resonance Swing if(pIns->nResSwing) { int32 d = ((int32)pIns->nResSwing * (int32)(static_cast(mpt::random(AccessPRNG())) + 1)) / 128; chn.nResSwing = static_cast((d * chn.nResonance + 1) / 128); chn.nRestoreResonanceOnNewNote = chn.nResonance + 1; } } } if(!pSmp) return; if(period) { if((!bPorta) || (!chn.nPeriod)) chn.nPeriod = period; if(!newTuning) { // FT2 compatibility: Don't reset portamento target with new notes. // Test case: Porta-Pickup.xm // ProTracker does the same. // Test case: PortaTarget.mod // IT compatibility: Portamento target is completely cleared with new notes. // Test case: PortaReset.it if(bPorta || !(m_playBehaviour[kFT2PortaTargetNoReset] || m_playBehaviour[kITClearPortaTarget] || GetType() == MOD_TYPE_MOD)) { chn.nPortamentoDest = period; chn.portaTargetReached = false; } } if(!bPorta || (!chn.nLength && !(GetType() & MOD_TYPE_S3M))) { chn.pModSample = pSmp; chn.nLength = pSmp->nLength; chn.nLoopEnd = pSmp->nLength; chn.nLoopStart = 0; chn.position.Set(0); if((m_SongFlags[SONG_PT_MODE] || m_playBehaviour[kST3OffsetWithoutInstrument]) && !chn.rowCommand.instr) { chn.position.SetInt(std::min(chn.prevNoteOffset, chn.nLength - SmpLength(1))); } else { chn.prevNoteOffset = 0; } chn.dwFlags = (chn.dwFlags & CHN_CHANNELFLAGS) | (pSmp->uFlags & CHN_SAMPLEFLAGS); chn.dwFlags.reset(CHN_PORTAMENTO); if(chn.dwFlags[CHN_SUSTAINLOOP]) { chn.nLoopStart = pSmp->nSustainStart; chn.nLoopEnd = pSmp->nSustainEnd; chn.dwFlags.set(CHN_PINGPONGLOOP, chn.dwFlags[CHN_PINGPONGSUSTAIN]); chn.dwFlags.set(CHN_LOOP); if (chn.nLength > chn.nLoopEnd) chn.nLength = chn.nLoopEnd; } else if(chn.dwFlags[CHN_LOOP]) { chn.nLoopStart = pSmp->nLoopStart; chn.nLoopEnd = pSmp->nLoopEnd; if (chn.nLength > chn.nLoopEnd) chn.nLength = chn.nLoopEnd; } // ProTracker "oneshot" loops (if loop start is 0, play the whole sample once and then repeat until loop end) if(m_playBehaviour[kMODOneShotLoops] && chn.nLoopStart == 0) chn.nLoopEnd = chn.nLength = pSmp->nLength; if(chn.dwFlags[CHN_REVERSE] && chn.nLength > 0) { chn.dwFlags.set(CHN_PINGPONGFLAG); chn.position.SetInt(chn.nLength - 1); } // Handle "retrigger" waveform type if(chn.nVibratoType < 4) { // IT Compatibilty: Slightly different waveform offsets (why does MPT have two different offsets here with IT old effects enabled and disabled?) if(!m_playBehaviour[kITVibratoTremoloPanbrello] && (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) && !m_SongFlags[SONG_ITOLDEFFECTS]) chn.nVibratoPos = 0x10; else if(GetType() == MOD_TYPE_MTM) chn.nVibratoPos = 0x20; else if(!(GetType() & (MOD_TYPE_DIGI | MOD_TYPE_DBM))) chn.nVibratoPos = 0; } // IT Compatibility: No "retrigger" waveform here if(!m_playBehaviour[kITVibratoTremoloPanbrello] && chn.nTremoloType < 4) { chn.nTremoloPos = 0; } } if(chn.position.GetUInt() >= chn.nLength) chn.position.SetInt(chn.nLoopStart); } else { bPorta = false; } if (!bPorta || (!(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_DBM))) || (chn.dwFlags[CHN_NOTEFADE] && !chn.nFadeOutVol) || (m_SongFlags[SONG_ITCOMPATGXX] && chn.rowCommand.instr != 0)) { if((GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_DBM)) && chn.dwFlags[CHN_NOTEFADE] && !chn.nFadeOutVol) { chn.ResetEnvelopes(); // IT Compatibility: Autovibrato reset if(!m_playBehaviour[kITVibratoTremoloPanbrello]) { chn.nAutoVibDepth = 0; chn.nAutoVibPos = 0; } chn.dwFlags.reset(CHN_NOTEFADE); chn.nFadeOutVol = 65536; } if ((!bPorta) || (!m_SongFlags[SONG_ITCOMPATGXX]) || (chn.rowCommand.instr)) { if ((!(GetType() & (MOD_TYPE_XM|MOD_TYPE_MT2))) || (chn.rowCommand.instr)) { chn.dwFlags.reset(CHN_NOTEFADE); chn.nFadeOutVol = 65536; } } } // IT compatibility: Don't reset key-off flag on porta notes unless Compat Gxx is enabled. // Test case: Off-Porta.it, Off-Porta-CompatGxx.it, Off-Porta.xm if(m_playBehaviour[kITFT2DontResetNoteOffOnPorta] && bPorta && (!m_SongFlags[SONG_ITCOMPATGXX] || chn.rowCommand.instr == 0)) chn.dwFlags.reset(CHN_EXTRALOUD); else chn.dwFlags.reset(CHN_EXTRALOUD | CHN_KEYOFF); // Enable Ramping if(!bPorta) { chn.nLeftVU = chn.nRightVU = 0xFF; chn.dwFlags.reset(CHN_FILTER); chn.dwFlags.set(CHN_FASTVOLRAMP); // IT compatibility 15. Retrigger is reset in RetrigNote (Tremor doesn't store anything here, so we just don't reset this as well) if(!m_playBehaviour[kITRetrigger] && !m_playBehaviour[kITTremor]) { // FT2 compatibility: Retrigger is reset in RetrigNote, tremor in ProcessEffects if(!m_playBehaviour[kFT2Retrigger] && !m_playBehaviour[kFT2Tremor]) { chn.nRetrigCount = 0; chn.nTremorCount = 0; } } if(bResetEnv) { chn.nAutoVibDepth = 0; chn.nAutoVibPos = 0; } chn.rightVol = chn.leftVol = 0; bool useFilter = !m_SongFlags[SONG_MPTFILTERMODE]; // Setup Initial Filter for this note if(pIns) { if(pIns->IsResonanceEnabled()) { chn.nResonance = pIns->GetResonance(); useFilter = true; } if(pIns->IsCutoffEnabled()) { chn.nCutOff = pIns->GetCutoff(); useFilter = true; } if(useFilter && (pIns->filterMode != FilterMode::Unchanged)) { chn.nFilterMode = pIns->filterMode; } } else { chn.nVolSwing = chn.nPanSwing = 0; chn.nCutSwing = chn.nResSwing = 0; } if((chn.nCutOff < 0x7F || m_playBehaviour[kITFilterBehaviour]) && useFilter) { int cutoff = SetupChannelFilter(chn, true); if(cutoff >= 0 && chn.dwFlags[CHN_ADLIB] && m_opl && channelHint != CHANNELINDEX_INVALID) m_opl->Volume(channelHint, chn.nCutOff / 2u, true); } if(chn.dwFlags[CHN_ADLIB] && m_opl && channelHint != CHANNELINDEX_INVALID) { // Test case: AdlibZeroVolumeNote.s3m if(m_playBehaviour[kOPLNoteOffOnNoteChange]) m_opl->NoteOff(channelHint); else if(m_playBehaviour[kOPLNoteStopWith0Hz]) m_opl->Frequency(channelHint, 0, true, false); } } // Special case for MPT if (bManual) chn.dwFlags.reset(CHN_MUTE); if((chn.dwFlags[CHN_MUTE] && (m_MixerSettings.MixerFlags & SNDMIX_MUTECHNMODE)) || (chn.pModSample != nullptr && chn.pModSample->uFlags[CHN_MUTE] && !bManual) || (chn.pModInstrument != nullptr && chn.pModInstrument->dwFlags[INS_MUTE] && !bManual)) { if (!bManual) chn.nPeriod = 0; } // Reset the Amiga resampler for this channel if(!bPorta) { chn.paulaState.Reset(); } } // Apply sample or instrument panning void CSoundFile::ApplyInstrumentPanning(ModChannel &chn, const ModInstrument *instr, const ModSample *smp) const { int32 newPan = int32_min; // Default instrument panning if(instr != nullptr && instr->dwFlags[INS_SETPANNING]) newPan = instr->nPan; // Default sample panning if(smp != nullptr && smp->uFlags[CHN_PANNING]) newPan = smp->nPan; if(newPan != int32_min) { chn.SetInstrumentPan(newPan, *this); // IT compatibility: Sample and instrument panning overrides channel surround status. // Test case: SmpInsPanSurround.it if(m_playBehaviour[kPanOverride] && !m_SongFlags[SONG_SURROUNDPAN]) { chn.dwFlags.reset(CHN_SURROUND); } } } CHANNELINDEX CSoundFile::GetNNAChannel(CHANNELINDEX nChn) const { // Check for empty channel for(CHANNELINDEX i = m_nChannels; i < MAX_CHANNELS; i++) { const ModChannel &c = m_PlayState.Chn[i]; // No sample and no plugin playing if(!c.nLength && !c.HasMIDIOutput()) return i; // Plugin channel with already released note if(!c.nLength && c.dwFlags[CHN_KEYOFF | CHN_NOTEFADE]) return i; // Stopped OPL channel if(c.dwFlags[CHN_ADLIB] && (!m_opl || !m_opl->IsActive(i))) return i; } uint32 vol = 0x800000; if(nChn < MAX_CHANNELS) { const ModChannel &srcChn = m_PlayState.Chn[nChn]; if(!srcChn.nFadeOutVol && srcChn.nLength) return CHANNELINDEX_INVALID; vol = (srcChn.nRealVolume << 9) | srcChn.nVolume; } // All channels are used: check for lowest volume CHANNELINDEX result = CHANNELINDEX_INVALID; uint32 envpos = 0; for(CHANNELINDEX i = m_nChannels; i < MAX_CHANNELS; i++) { const ModChannel &c = m_PlayState.Chn[i]; if(c.nLength && !c.nFadeOutVol) return i; // Use a combination of real volume [14 bit] (which includes volume envelopes, but also potentially global volume) and note volume [9 bit]. // Rationale: We need volume envelopes in case e.g. all NNA channels are playing at full volume but are looping on a 0-volume envelope node. // But if global volume is not applied to master and the global volume temporarily drops to 0, we would kill arbitrary channels. Hence, add the note volume as well. uint32 v = (c.nRealVolume << 9) | c.nVolume; if(c.dwFlags[CHN_LOOP]) v /= 2; if((v < vol) || ((v == vol) && (c.VolEnv.nEnvPosition > envpos))) { envpos = c.VolEnv.nEnvPosition; vol = v; result = i; } } return result; } CHANNELINDEX CSoundFile::CheckNNA(CHANNELINDEX nChn, uint32 instr, int note, bool forceCut) { ModChannel &srcChn = m_PlayState.Chn[nChn]; const ModInstrument *pIns = nullptr; if(!ModCommand::IsNote(static_cast(note))) return CHANNELINDEX_INVALID; // Always NNA cut - using if((!(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_MT2)) || !m_nInstruments || forceCut) && !srcChn.HasMIDIOutput()) { if(!srcChn.nLength || srcChn.dwFlags[CHN_MUTE] || !(srcChn.rightVol | srcChn.leftVol)) return CHANNELINDEX_INVALID; if(srcChn.dwFlags[CHN_ADLIB] && m_opl) { m_opl->NoteCut(nChn, false); return CHANNELINDEX_INVALID; } const CHANNELINDEX nnaChn = GetNNAChannel(nChn); if(nnaChn == CHANNELINDEX_INVALID) return CHANNELINDEX_INVALID; ModChannel &chn = m_PlayState.Chn[nnaChn]; // Copy Channel chn = srcChn; chn.dwFlags.reset(CHN_VIBRATO | CHN_TREMOLO | CHN_MUTE | CHN_PORTAMENTO); chn.nPanbrelloOffset = 0; chn.nMasterChn = nChn + 1; chn.nCommand = CMD_NONE; chn.rowCommand.Clear(); // Cut the note chn.nFadeOutVol = 0; chn.dwFlags.set(CHN_NOTEFADE | CHN_FASTVOLRAMP); // Stop this channel srcChn.nLength = 0; srcChn.position.Set(0); srcChn.nROfs = srcChn.nLOfs = 0; srcChn.rightVol = srcChn.leftVol = 0; return nnaChn; } if(instr > GetNumInstruments()) instr = 0; const ModSample *pSample = srcChn.pModSample; // If no instrument is given, assume previous instrument to still be valid. // Test case: DNA-NoInstr.it pIns = instr > 0 ? Instruments[instr] : srcChn.pModInstrument; auto dnaNote = note; if(pIns != nullptr) { auto smp = pIns->Keyboard[note - NOTE_MIN]; // IT compatibility: DCT = note uses pattern notes for comparison // Note: This is not applied in case kITRealNoteMapping is not set to keep playback of legacy modules simple (chn.nNote is translated note in that case) // Test case: dct_smp_note_test.it if(!m_playBehaviour[kITDCTBehaviour] || !m_playBehaviour[kITRealNoteMapping]) dnaNote = pIns->NoteMap[note - NOTE_MIN]; if(smp > 0 && smp < MAX_SAMPLES) { pSample = &Samples[smp]; } else if(m_playBehaviour[kITEmptyNoteMapSlot] && !pIns->HasValidMIDIChannel()) { // Impulse Tracker ignores empty slots. // We won't ignore them if a plugin is assigned to this slot, so that VSTis still work as intended. // Test case: emptyslot.it, PortaInsNum.it, gxsmp.it, gxsmp2.it return CHANNELINDEX_INVALID; } } if(srcChn.dwFlags[CHN_MUTE]) return CHANNELINDEX_INVALID; for(CHANNELINDEX i = nChn; i < MAX_CHANNELS; i++) { // Only apply to background channels, or the same pattern channel if(i < m_nChannels && i != nChn) continue; ModChannel &chn = m_PlayState.Chn[i]; bool applyDNAtoPlug = false; if((chn.nMasterChn == nChn + 1 || i == nChn) && chn.pModInstrument != nullptr) { bool applyDNA = false; // Duplicate Check Type switch(chn.pModInstrument->nDCT) { case DuplicateCheckType::None: break; // Note case DuplicateCheckType::Note: if(dnaNote != NOTE_NONE && chn.nNote == dnaNote && pIns == chn.pModInstrument) applyDNA = true; if(pIns && pIns->nMixPlug) applyDNAtoPlug = true; break; // Sample case DuplicateCheckType::Sample: // IT compatibility: DCT = sample only applies to same instrument // Test case: dct_smp_note_test.it if(pSample != nullptr && pSample == chn.pModSample && (pIns == chn.pModInstrument || !m_playBehaviour[kITDCTBehaviour])) applyDNA = true; break; // Instrument case DuplicateCheckType::Instrument: if(pIns == chn.pModInstrument) applyDNA = true; if(pIns && pIns->nMixPlug) applyDNAtoPlug = true; break; // Plugin case DuplicateCheckType::Plugin: if(pIns && (pIns->nMixPlug) && (pIns->nMixPlug == chn.pModInstrument->nMixPlug)) { applyDNAtoPlug = true; applyDNA = true; } break; } // Duplicate Note Action if(applyDNA) { #ifndef NO_PLUGINS if(applyDNAtoPlug && chn.nNote != NOTE_NONE) { switch(chn.pModInstrument->nDNA) { case DuplicateNoteAction::NoteCut: case DuplicateNoteAction::NoteOff: case DuplicateNoteAction::NoteFade: // Switch off duplicated note played on this plugin SendMIDINote(i, chn.GetPluginNote(m_playBehaviour[kITRealNoteMapping]) + NOTE_MAX_SPECIAL, 0); chn.nArpeggioLastNote = NOTE_NONE; break; } } #endif // NO_PLUGINS switch(chn.pModInstrument->nDNA) { // Cut case DuplicateNoteAction::NoteCut: KeyOff(chn); chn.nVolume = 0; if(chn.dwFlags[CHN_ADLIB] && m_opl) m_opl->NoteCut(i); break; // Note Off case DuplicateNoteAction::NoteOff: KeyOff(chn); if(chn.dwFlags[CHN_ADLIB] && m_opl) m_opl->NoteOff(i); break; // Note Fade case DuplicateNoteAction::NoteFade: chn.dwFlags.set(CHN_NOTEFADE); if(chn.dwFlags[CHN_ADLIB] && m_opl && !m_playBehaviour[kOPLwithNNA]) m_opl->NoteOff(i); break; } if(!chn.nVolume) { chn.nFadeOutVol = 0; chn.dwFlags.set(CHN_NOTEFADE | CHN_FASTVOLRAMP); } } } } // Do we need to apply New/Duplicate Note Action to a VSTi? bool applyNNAtoPlug = false; #ifndef NO_PLUGINS IMixPlugin *pPlugin = nullptr; if(srcChn.HasMIDIOutput() && ModCommand::IsNote(srcChn.nNote)) // instro sends to a midi chan { PLUGINDEX plugin = GetBestPlugin(m_PlayState, nChn, PrioritiseInstrument, RespectMutes); if(plugin > 0 && plugin <= MAX_MIXPLUGINS) { pPlugin = m_MixPlugins[plugin - 1].pMixPlugin; if(pPlugin) { // apply NNA to this plugin iff it is currently playing a note on this tracker channel // (and if it is playing a note, we know that would be the last note played on this chan). applyNNAtoPlug = pPlugin->IsNotePlaying(srcChn.GetPluginNote(m_playBehaviour[kITRealNoteMapping]), nChn); } } } #endif // NO_PLUGINS // New Note Action if(!srcChn.IsSamplePlaying() && !applyNNAtoPlug) return CHANNELINDEX_INVALID; CHANNELINDEX nnaChn = GetNNAChannel(nChn); if(nnaChn == CHANNELINDEX_INVALID) return CHANNELINDEX_INVALID; ModChannel &chn = m_PlayState.Chn[nnaChn]; if(chn.dwFlags[CHN_ADLIB] && m_opl) m_opl->NoteCut(nnaChn); // Copy Channel chn = srcChn; chn.dwFlags.reset(CHN_VIBRATO | CHN_TREMOLO | CHN_PORTAMENTO); chn.nPanbrelloOffset = 0; chn.nMasterChn = nChn < GetNumChannels() ? nChn + 1 : 0; chn.nCommand = CMD_NONE; #ifndef NO_PLUGINS if(applyNNAtoPlug && pPlugin) { switch(srcChn.nNNA) { case NewNoteAction::NoteOff: case NewNoteAction::NoteCut: case NewNoteAction::NoteFade: // Switch off note played on this plugin, on this tracker channel and midi channel SendMIDINote(nChn, NOTE_KEYOFF, 0); srcChn.nArpeggioLastNote = NOTE_NONE; break; case NewNoteAction::Continue: break; } } #endif // NO_PLUGINS // Key Off the note switch(srcChn.nNNA) { case NewNoteAction::NoteOff: KeyOff(chn); if(chn.dwFlags[CHN_ADLIB] && m_opl) { m_opl->NoteOff(nChn); if(m_playBehaviour[kOPLwithNNA]) m_opl->MoveChannel(nChn, nnaChn); } break; case NewNoteAction::NoteCut: chn.nFadeOutVol = 0; chn.dwFlags.set(CHN_NOTEFADE); if(chn.dwFlags[CHN_ADLIB] && m_opl) m_opl->NoteCut(nChn); break; case NewNoteAction::NoteFade: chn.dwFlags.set(CHN_NOTEFADE); if(chn.dwFlags[CHN_ADLIB] && m_opl) { if(m_playBehaviour[kOPLwithNNA]) m_opl->MoveChannel(nChn, nnaChn); else m_opl->NoteOff(nChn); } break; case NewNoteAction::Continue: if(chn.dwFlags[CHN_ADLIB] && m_opl) m_opl->MoveChannel(nChn, nnaChn); break; } if(!chn.nVolume) { chn.nFadeOutVol = 0; chn.dwFlags.set(CHN_NOTEFADE | CHN_FASTVOLRAMP); } // Stop this channel srcChn.nLength = 0; srcChn.position.Set(0); srcChn.nROfs = srcChn.nLOfs = 0; return nnaChn; } bool CSoundFile::ProcessEffects() { m_PlayState.m_breakRow = ROWINDEX_INVALID; // Is changed if a break to row command is encountered m_PlayState.m_patLoopRow = ROWINDEX_INVALID; // Is changed if a pattern loop jump-back is executed m_PlayState.m_posJump = ORDERINDEX_INVALID; for(CHANNELINDEX nChn = 0; nChn < GetNumChannels(); nChn++) { ModChannel &chn = m_PlayState.Chn[nChn]; const uint32 tickCount = m_PlayState.m_nTickCount % (m_PlayState.m_nMusicSpeed + m_PlayState.m_nFrameDelay); uint32 instr = chn.rowCommand.instr; ModCommand::VOLCMD volcmd = chn.rowCommand.volcmd; uint32 vol = chn.rowCommand.vol; ModCommand::COMMAND cmd = chn.rowCommand.command; uint32 param = chn.rowCommand.param; bool bPorta = chn.rowCommand.IsPortamento(); uint32 nStartTick = 0; chn.isFirstTick = m_SongFlags[SONG_FIRSTTICK]; // Process parameter control note. if(chn.rowCommand.note == NOTE_PC) { #ifndef NO_PLUGINS const PLUGINDEX plug = chn.rowCommand.instr; const PlugParamIndex plugparam = chn.rowCommand.GetValueVolCol(); const PlugParamValue value = chn.rowCommand.GetValueEffectCol() / PlugParamValue(ModCommand::maxColumnValue); if(plug > 0 && plug <= MAX_MIXPLUGINS && m_MixPlugins[plug - 1].pMixPlugin) m_MixPlugins[plug-1].pMixPlugin->SetParameter(plugparam, value); #endif // NO_PLUGINS } // Process continuous parameter control note. // Row data is cleared after first tick so on following // ticks using channels m_nPlugParamValueStep to identify // the need for parameter control. The condition cmd == 0 // is to make sure that m_nPlugParamValueStep != 0 because // of NOTE_PCS, not because of macro. if(chn.rowCommand.note == NOTE_PCS || (cmd == CMD_NONE && chn.m_plugParamValueStep != 0)) { #ifndef NO_PLUGINS const bool isFirstTick = m_SongFlags[SONG_FIRSTTICK]; if(isFirstTick) chn.m_RowPlug = chn.rowCommand.instr; const PLUGINDEX plugin = chn.m_RowPlug; const bool hasValidPlug = (plugin > 0 && plugin <= MAX_MIXPLUGINS && m_MixPlugins[plugin - 1].pMixPlugin); if(hasValidPlug) { if(isFirstTick) chn.m_RowPlugParam = ModCommand::GetValueVolCol(chn.rowCommand.volcmd, chn.rowCommand.vol); const PlugParamIndex plugparam = chn.m_RowPlugParam; if(isFirstTick) { PlugParamValue targetvalue = ModCommand::GetValueEffectCol(chn.rowCommand.command, chn.rowCommand.param) / PlugParamValue(ModCommand::maxColumnValue); chn.m_plugParamTargetValue = targetvalue; chn.m_plugParamValueStep = (targetvalue - m_MixPlugins[plugin - 1].pMixPlugin->GetParameter(plugparam)) / PlugParamValue(m_PlayState.TicksOnRow()); } if(m_PlayState.m_nTickCount + 1 == m_PlayState.TicksOnRow()) { // On last tick, set parameter exactly to target value. m_MixPlugins[plugin - 1].pMixPlugin->SetParameter(plugparam, chn.m_plugParamTargetValue); } else m_MixPlugins[plugin - 1].pMixPlugin->ModifyParameter(plugparam, chn.m_plugParamValueStep); } #endif // NO_PLUGINS } // Apart from changing parameters, parameter control notes are intended to be 'invisible'. // To achieve this, clearing the note data so that rest of the process sees the row as empty row. if(ModCommand::IsPcNote(chn.rowCommand.note)) { chn.ClearRowCmd(); instr = 0; volcmd = VOLCMD_NONE; vol = 0; cmd = CMD_NONE; param = 0; bPorta = false; } // Process Invert Loop (MOD Effect, called every row if it's active) if(!m_SongFlags[SONG_FIRSTTICK]) { InvertLoop(m_PlayState.Chn[nChn]); } else { if(instr) m_PlayState.Chn[nChn].nEFxOffset = 0; } // Process special effects (note delay, pattern delay, pattern loop) if (cmd == CMD_DELAYCUT) { //:xy --> note delay until tick x, note cut at tick x+y nStartTick = (param & 0xF0) >> 4; const uint32 cutAtTick = nStartTick + (param & 0x0F); NoteCut(nChn, cutAtTick, m_playBehaviour[kITSCxStopsSample]); } else if ((cmd == CMD_MODCMDEX) || (cmd == CMD_S3MCMDEX)) { if ((!param) && (GetType() & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_MPT))) param = chn.nOldCmdEx; else chn.nOldCmdEx = static_cast(param); // Note Delay ? if ((param & 0xF0) == 0xD0) { nStartTick = param & 0x0F; if(nStartTick == 0) { //IT compatibility 22. SD0 == SD1 if(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) nStartTick = 1; //ST3 ignores notes with SD0 completely else if(GetType() == MOD_TYPE_S3M) continue; } else if(nStartTick >= (m_PlayState.m_nMusicSpeed + m_PlayState.m_nFrameDelay) && m_playBehaviour[kITOutOfRangeDelay]) { // IT compatibility 08. Handling of out-of-range delay command. // Additional test case: tickdelay.it if(instr) { chn.nNewIns = static_cast(instr); } continue; } } else if(m_SongFlags[SONG_FIRSTTICK]) { // Pattern Loop ? if((param & 0xF0) == 0xE0) { // Pattern Delay // In Scream Tracker 3 / Impulse Tracker, only the first delay command on this row is considered. // Test cases: PatternDelays.it, PatternDelays.s3m, PatternDelays.xm // XXX In Scream Tracker 3, the "left" channels are evaluated before the "right" channels, which is not emulated here! if(!(GetType() & (MOD_TYPE_S3M | MOD_TYPE_IT | MOD_TYPE_MPT)) || !m_PlayState.m_nPatternDelay) { if(!(GetType() & (MOD_TYPE_S3M)) || (param & 0x0F) != 0) { // While Impulse Tracker *does* count S60 as a valid row delay (and thus ignores any other row delay commands on the right), // Scream Tracker 3 simply ignores such commands. m_PlayState.m_nPatternDelay = 1 + (param & 0x0F); } } } } } if(GetType() == MOD_TYPE_MTM && cmd == CMD_MODCMDEX && (param & 0xF0) == 0xD0) { // Apparently, retrigger and note delay have the same behaviour in MultiTracker: // They both restart the note at tick x, and if there is a note on the same row, // this note is started on the first tick. nStartTick = 0; param = 0x90 | (param & 0x0F); } if(nStartTick != 0 && chn.rowCommand.note == NOTE_KEYOFF && chn.rowCommand.volcmd == VOLCMD_PANNING && m_playBehaviour[kFT2PanWithDelayedNoteOff]) { // FT2 compatibility: If there's a delayed note off, panning commands are ignored. WTF! // Test case: PanOff.xm chn.rowCommand.volcmd = VOLCMD_NONE; } bool triggerNote = (m_PlayState.m_nTickCount == nStartTick); // Can be delayed by a note delay effect if(m_playBehaviour[kFT2OutOfRangeDelay] && nStartTick >= m_PlayState.m_nMusicSpeed) { // FT2 compatibility: Note delays greater than the song speed should be ignored. // However, EEx pattern delay is *not* considered at all. // Test case: DelayCombination.xm, PortaDelay.xm triggerNote = false; } else if(m_playBehaviour[kRowDelayWithNoteDelay] && nStartTick > 0 && tickCount == nStartTick) { // IT compatibility: Delayed notes (using SDx) that are on the same row as a Row Delay effect are retriggered. // ProTracker / Scream Tracker 3 / FastTracker 2 do the same. // Test case: PatternDelay-NoteDelay.it, PatternDelay-NoteDelay.xm, PatternDelaysRetrig.mod triggerNote = true; } // IT compatibility: Tick-0 vs non-tick-0 effect distinction is always based on tick delay. // Test case: SlideDelay.it if(m_playBehaviour[kITFirstTickHandling]) { chn.isFirstTick = tickCount == nStartTick; } chn.triggerNote = triggerNote; // FT2 compatibility: Note + portamento + note delay = no portamento // Test case: PortaDelay.xm if(m_playBehaviour[kFT2PortaDelay] && nStartTick != 0) { bPorta = false; } if(m_SongFlags[SONG_PT_MODE] && instr && !m_PlayState.m_nTickCount) { // Instrument number resets the stacked ProTracker offset. // Test case: ptoffset.mod chn.prevNoteOffset = 0; // ProTracker compatibility: Sample properties are always loaded on the first tick, even when there is a note delay. // Test case: InstrDelay.mod if(!triggerNote && chn.IsSamplePlaying()) { chn.nNewIns = static_cast(instr); if(instr <= GetNumSamples()) { chn.nVolume = Samples[instr].nVolume; chn.nFineTune = Samples[instr].nFineTune; } } } // Handles note/instrument/volume changes if(triggerNote) { ModCommand::NOTE note = chn.rowCommand.note; if(instr) chn.nNewIns = static_cast(instr); if(ModCommand::IsNote(note) && m_playBehaviour[kFT2Transpose]) { // Notes that exceed FT2's limit are completely ignored. // Test case: NoteLimit.xm int transpose = chn.nTranspose; if(instr && !bPorta) { // Refresh transpose // Test case: NoteLimit2.xm const SAMPLEINDEX sample = GetSampleIndex(note, instr); if(sample > 0) transpose = GetSample(sample).RelativeTone; } const int computedNote = note + transpose; if((computedNote < NOTE_MIN + 11 || computedNote > NOTE_MIN + 130)) { note = NOTE_NONE; } } else if((GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_J2B)) && GetNumInstruments() != 0 && ModCommand::IsNoteOrEmpty(static_cast(note))) { // IT compatibility: Invalid instrument numbers do nothing, but they are remembered for upcoming notes and do not trigger a note in that case. // Test case: InstrumentNumberChange.it INSTRUMENTINDEX instrToCheck = static_cast((instr != 0) ? instr : chn.nOldIns); if(instrToCheck != 0 && (instrToCheck > GetNumInstruments() || Instruments[instrToCheck] == nullptr)) { note = NOTE_NONE; instr = 0; } } // XM: FT2 ignores a note next to a K00 effect, and a fade-out seems to be done when no volume envelope is present (not exactly the Kxx behaviour) if(cmd == CMD_KEYOFF && param == 0 && m_playBehaviour[kFT2KeyOff]) { note = NOTE_NONE; instr = 0; } bool retrigEnv = note == NOTE_NONE && instr != 0; // Apparently, any note number in a pattern causes instruments to recall their original volume settings - no matter if there's a Note Off next to it or whatever. // Test cases: keyoff+instr.xm, delay.xm bool reloadSampleSettings = (m_playBehaviour[kFT2ReloadSampleSettings] && instr != 0); // ProTracker Compatibility: If a sample was stopped before, lone instrument numbers can retrigger it // Test case: PTSwapEmpty.mod, PTInstrVolume.mod, SampleSwap.s3m bool keepInstr = (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) || m_playBehaviour[kST3SampleSwap] || (m_playBehaviour[kMODSampleSwap] && !chn.IsSamplePlaying() && (chn.pModSample == nullptr || !chn.pModSample->HasSampleData())); // Now it's time for some FT2 crap... if (GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2)) { // XM: Key-Off + Sample == Note Cut (BUT: Only if no instr number or volume effect is present!) // Test case: NoteOffVolume.xm if(note == NOTE_KEYOFF && ((!instr && volcmd != VOLCMD_VOLUME && cmd != CMD_VOLUME) || !m_playBehaviour[kFT2KeyOff]) && (chn.pModInstrument == nullptr || !chn.pModInstrument->VolEnv.dwFlags[ENV_ENABLED])) { chn.dwFlags.set(CHN_FASTVOLRAMP); chn.nVolume = 0; note = NOTE_NONE; instr = 0; retrigEnv = false; // FT2 Compatbility: Start fading the note for notes with no delay. Only relevant when a volume command is encountered after the note-off. // Test case: NoteOffFadeNoEnv.xm if(m_SongFlags[SONG_FIRSTTICK] && m_playBehaviour[kFT2NoteOffFlags]) chn.dwFlags.set(CHN_NOTEFADE); } else if(m_playBehaviour[kFT2RetrigWithNoteDelay] && !m_SongFlags[SONG_FIRSTTICK]) { // FT2 Compatibility: Some special hacks for rogue note delays... (EDx with x > 0) // Apparently anything that is next to a note delay behaves totally unpredictable in FT2. Swedish tracker logic. :) retrigEnv = true; // Portamento + Note Delay = No Portamento // Test case: porta-delay.xm bPorta = false; if(note == NOTE_NONE) { // If there's a note delay but no real note, retrig the last note. // Test case: delay2.xm, delay3.xm note = static_cast(chn.nNote - chn.nTranspose); } else if(note >= NOTE_MIN_SPECIAL) { // Gah! Even Note Off + Note Delay will cause envelopes to *retrigger*! How stupid is that? // ... Well, and that is actually all it does if there's an envelope. No fade out, no nothing. *sigh* // Test case: OffDelay.xm note = NOTE_NONE; keepInstr = false; reloadSampleSettings = true; } else if(instr || !m_playBehaviour[kFT2NoteDelayWithoutInstr]) { // Normal note (only if there is an instrument, test case: DelayVolume.xm) keepInstr = true; reloadSampleSettings = true; } } } if((retrigEnv && !m_playBehaviour[kFT2ReloadSampleSettings]) || reloadSampleSettings) { const ModSample *oldSample = nullptr; // Reset default volume when retriggering envelopes if(GetNumInstruments()) { oldSample = chn.pModSample; } else if (instr <= GetNumSamples()) { // Case: Only samples are used; no instruments. oldSample = &Samples[instr]; } if(oldSample != nullptr) { if(!oldSample->uFlags[SMP_NODEFAULTVOLUME] && (GetType() != MOD_TYPE_S3M || oldSample->HasSampleData())) chn.nVolume = oldSample->nVolume; if(reloadSampleSettings) { // Also reload panning chn.SetInstrumentPan(oldSample->nPan, *this); } } } // FT2 compatibility: Instrument number disables tremor effect // Test case: TremorInstr.xm, TremoRecover.xm if(m_playBehaviour[kFT2Tremor] && instr != 0) { chn.nTremorCount = 0x20; } // IT compatibility: Envelope retriggering with instrument number based on Old Effects and Compatible Gxx flags: // OldFX CompatGxx Env Behaviour // ----- --------- ------------- // off off never reset // on off reset on instrument without portamento // off on reset on instrument with portamento // on on always reset // Test case: ins-xx.it, ins-ox.it, ins-oc.it, ins-xc.it, ResetEnvNoteOffOldFx.it, ResetEnvNoteOffOldFx2.it, noteoff3.it if(GetNumInstruments() && m_playBehaviour[kITInstrWithNoteOffOldEffects] && instr && !ModCommand::IsNote(note)) { if((bPorta && m_SongFlags[SONG_ITCOMPATGXX]) || (!bPorta && m_SongFlags[SONG_ITOLDEFFECTS])) { chn.ResetEnvelopes(); chn.dwFlags.set(CHN_FASTVOLRAMP); chn.nFadeOutVol = 65536; } } if(retrigEnv) //Case: instrument with no note data. { //IT compatibility: Instrument with no note. if(m_playBehaviour[kITInstrWithoutNote] || GetType() == MOD_TYPE_PLM) { // IT compatibility: Completely retrigger note after sample end to also reset portamento. // Test case: PortaResetAfterRetrigger.it bool triggerAfterSmpEnd = m_playBehaviour[kITMultiSampleInstrumentNumber] && !chn.IsSamplePlaying(); if(GetNumInstruments()) { // Instrument mode if(instr <= GetNumInstruments() && (chn.pModInstrument != Instruments[instr] || triggerAfterSmpEnd)) note = chn.nNote; } else { // Sample mode if(instr < MAX_SAMPLES && (chn.pModSample != &Samples[instr] || triggerAfterSmpEnd)) note = chn.nNote; } } if(GetNumInstruments() && (GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MED))) { chn.ResetEnvelopes(); chn.dwFlags.set(CHN_FASTVOLRAMP); chn.dwFlags.reset(CHN_NOTEFADE); chn.nAutoVibDepth = 0; chn.nAutoVibPos = 0; chn.nFadeOutVol = 65536; // FT2 Compatbility: Reset key-off status with instrument number // Test case: NoteOffInstrChange.xm if(m_playBehaviour[kFT2NoteOffFlags]) chn.dwFlags.reset(CHN_KEYOFF); } if (!keepInstr) instr = 0; } // Note Cut/Off/Fade => ignore instrument if (note >= NOTE_MIN_SPECIAL) { // IT compatibility: Default volume of sample is recalled if instrument number is next to a note-off. // Test case: NoteOffInstr.it, noteoff2.it if(m_playBehaviour[kITInstrWithNoteOff] && instr) { const SAMPLEINDEX smp = GetSampleIndex(chn.nLastNote, instr); if(smp > 0 && !Samples[smp].uFlags[SMP_NODEFAULTVOLUME]) chn.nVolume = Samples[smp].nVolume; } // IT compatibility: Note-off with instrument number + Old Effects retriggers envelopes. // Test case: ResetEnvNoteOffOldFx.it if(!m_playBehaviour[kITInstrWithNoteOffOldEffects] || !m_SongFlags[SONG_ITOLDEFFECTS]) instr = 0; } if(ModCommand::IsNote(note)) { chn.nNewNote = chn.nLastNote = note; // New Note Action ? if(!bPorta) { CheckNNA(nChn, instr, note, false); } if(chn.nRestorePanOnNewNote > 0) { chn.nPan = (chn.nRestorePanOnNewNote & 0x7FFF) - 1; if(chn.nRestorePanOnNewNote & 0x8000) chn.dwFlags.set(CHN_SURROUND); chn.nRestorePanOnNewNote = 0; } if(chn.nRestoreResonanceOnNewNote > 0) { chn.nResonance = chn.nRestoreResonanceOnNewNote - 1; chn.nRestoreResonanceOnNewNote = 0; } if(chn.nRestoreCutoffOnNewNote > 0) { chn.nCutOff = chn.nRestoreCutoffOnNewNote - 1; chn.nRestoreCutoffOnNewNote = 0; } } // Instrument Change ? if(instr) { const ModSample *oldSample = chn.pModSample; //const ModInstrument *oldInstrument = chn.pModInstrument; InstrumentChange(chn, instr, bPorta, true); if(chn.pModSample != nullptr && chn.pModSample->uFlags[CHN_ADLIB] && m_opl) { m_opl->Patch(nChn, chn.pModSample->adlib); } // IT compatibility: Keep new instrument number for next instrument-less note even if sample playback is stopped // Test case: StoppedInstrSwap.it if(GetType() == MOD_TYPE_MOD) { // Test case: PortaSwapPT.mod if(!bPorta || !m_playBehaviour[kMODSampleSwap]) chn.nNewIns = 0; } else { if(!m_playBehaviour[kITInstrWithNoteOff] || ModCommand::IsNote(note)) chn.nNewIns = 0; } if(m_playBehaviour[kITPortamentoSwapResetsPos]) { // Test cases: PortaInsNum.it, PortaSample.it if(ModCommand::IsNote(note) && oldSample != chn.pModSample) { //const bool newInstrument = oldInstrument != chn.pModInstrument && chn.pModInstrument->Keyboard[chn.nNewNote - NOTE_MIN] != 0; chn.position.Set(0); } } else if((GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) && oldSample != chn.pModSample && ModCommand::IsNote(note)) { // Special IT case: portamento+note causes sample change -> ignore portamento bPorta = false; } else if(m_playBehaviour[kST3SampleSwap] && oldSample != chn.pModSample && (bPorta || !ModCommand::IsNote(note)) && chn.position.GetUInt() > chn.nLength) { // ST3 with SoundBlaster does sample swapping and continues playing the new sample where the old sample was stopped. // If the new sample is shorter than that, it is stopped, even if it could be looped. // This also applies to portamento between different samples. // Test case: SampleSwap.s3m chn.nLength = 0; } else if(m_playBehaviour[kMODSampleSwap] && !chn.IsSamplePlaying()) { // If channel was paused and is resurrected by a lone instrument number, reset the sample position. // Test case: PTSwapEmpty.mod chn.position.Set(0); } } // New Note ? if (note != NOTE_NONE) { const bool instrChange = (!instr) && (chn.nNewIns) && ModCommand::IsNote(note); if(instrChange) { InstrumentChange(chn, chn.nNewIns, bPorta, chn.pModSample == nullptr && chn.pModInstrument == nullptr, !(GetType() & (MOD_TYPE_XM|MOD_TYPE_MT2))); chn.nNewIns = 0; } if(chn.pModSample != nullptr && chn.pModSample->uFlags[CHN_ADLIB] && m_opl && (instrChange || !m_opl->IsActive(nChn))) { m_opl->Patch(nChn, chn.pModSample->adlib); } NoteChange(chn, note, bPorta, !(GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2)), false, nChn); HandleDigiSamplePlayDirection(m_PlayState, nChn); if ((bPorta) && (GetType() & (MOD_TYPE_XM|MOD_TYPE_MT2)) && (instr)) { chn.dwFlags.set(CHN_FASTVOLRAMP); chn.ResetEnvelopes(); chn.nAutoVibDepth = 0; chn.nAutoVibPos = 0; } if(chn.dwFlags[CHN_ADLIB] && m_opl && ((note == NOTE_NOTECUT || note == NOTE_KEYOFF) || (note == NOTE_FADE && !m_playBehaviour[kOPLFlexibleNoteOff]))) { if(m_playBehaviour[kOPLNoteStopWith0Hz]) m_opl->Frequency(nChn, 0, true, false); m_opl->NoteOff(nChn); } } // Tick-0 only volume commands if (volcmd == VOLCMD_VOLUME) { if (vol > 64) vol = 64; chn.nVolume = vol << 2; chn.dwFlags.set(CHN_FASTVOLRAMP); } else if (volcmd == VOLCMD_PANNING) { Panning(chn, vol, Pan6bit); } #ifndef NO_PLUGINS if (m_nInstruments) ProcessMidiOut(nChn); #endif // NO_PLUGINS } if(m_playBehaviour[kST3NoMutedChannels] && ChnSettings[nChn].dwFlags[CHN_MUTE]) // not even effects are processed on muted S3M channels continue; // Volume Column Effect (except volume & panning) /* A few notes, paraphrased from ITTECH.TXT by Storlek (creator of schismtracker): Ex/Fx/Gx are shared with Exx/Fxx/Gxx; Ex/Fx are 4x the 'normal' slide value Gx is linked with Ex/Fx if Compat Gxx is off, just like Gxx is with Exx/Fxx Gx values: 1, 4, 8, 16, 32, 64, 96, 128, 255 Ax/Bx/Cx/Dx values are used directly (i.e. D9 == D09), and are NOT shared with Dxx (value is stored into nOldVolParam and used by A0/B0/C0/D0) Hx uses the same value as Hxx and Uxx, and affects the *depth* so... hxx = (hx | (oldhxx & 0xf0)) ??? TODO is this done correctly? */ bool doVolumeColumn = m_PlayState.m_nTickCount >= nStartTick; // FT2 compatibility: If there's a note delay, volume column effects are NOT executed // on the first tick and, if there's an instrument number, on the delayed tick. // Test case: VolColDelay.xm, PortaDelay.xm if(m_playBehaviour[kFT2VolColDelay] && nStartTick != 0) { doVolumeColumn = m_PlayState.m_nTickCount != 0 && (m_PlayState.m_nTickCount != nStartTick || (chn.rowCommand.instr == 0 && volcmd != VOLCMD_TONEPORTAMENTO)); } if(volcmd > VOLCMD_PANNING && doVolumeColumn) { if(volcmd == VOLCMD_TONEPORTAMENTO) { const auto [porta, clearEffectCommand] = GetVolCmdTonePorta(chn.rowCommand, nStartTick); if(clearEffectCommand) cmd = CMD_NONE; TonePortamento(chn, porta); } else { // FT2 Compatibility: FT2 ignores some volume commands with parameter = 0. if(m_playBehaviour[kFT2VolColMemory] && vol == 0) { switch(volcmd) { case VOLCMD_VOLUME: case VOLCMD_PANNING: case VOLCMD_VIBRATODEPTH: break; case VOLCMD_PANSLIDELEFT: // FT2 Compatibility: Pan slide left with zero parameter causes panning to be set to full left on every non-row tick. // Test case: PanSlideZero.xm if(!m_SongFlags[SONG_FIRSTTICK]) { chn.nPan = 0; } [[fallthrough]]; default: // no memory here. volcmd = VOLCMD_NONE; } } else if(!m_playBehaviour[kITVolColMemory]) { // IT Compatibility: Effects in the volume column don't have an unified memory. // Test case: VolColMemory.it if(vol) chn.nOldVolParam = static_cast(vol); else vol = chn.nOldVolParam; } switch(volcmd) { case VOLCMD_VOLSLIDEUP: case VOLCMD_VOLSLIDEDOWN: // IT Compatibility: Volume column volume slides have their own memory // Test case: VolColMemory.it if(vol == 0 && m_playBehaviour[kITVolColMemory]) { vol = chn.nOldVolParam; if(vol == 0) break; } else { chn.nOldVolParam = static_cast(vol); } VolumeSlide(chn, static_cast(volcmd == VOLCMD_VOLSLIDEUP ? (vol << 4) : vol)); break; case VOLCMD_FINEVOLUP: // IT Compatibility: Fine volume slides in the volume column are only executed on the first tick, not on multiples of the first tick in case of pattern delay // Test case: FineVolColSlide.it if(m_PlayState.m_nTickCount == nStartTick || !m_playBehaviour[kITVolColMemory]) { // IT Compatibility: Volume column volume slides have their own memory // Test case: VolColMemory.it FineVolumeUp(chn, static_cast(vol), m_playBehaviour[kITVolColMemory]); } break; case VOLCMD_FINEVOLDOWN: // IT Compatibility: Fine volume slides in the volume column are only executed on the first tick, not on multiples of the first tick in case of pattern delay // Test case: FineVolColSlide.it if(m_PlayState.m_nTickCount == nStartTick || !m_playBehaviour[kITVolColMemory]) { // IT Compatibility: Volume column volume slides have their own memory // Test case: VolColMemory.it FineVolumeDown(chn, static_cast(vol), m_playBehaviour[kITVolColMemory]); } break; case VOLCMD_VIBRATOSPEED: // FT2 does not automatically enable vibrato with the "set vibrato speed" command if(m_playBehaviour[kFT2VolColVibrato]) chn.nVibratoSpeed = vol & 0x0F; else Vibrato(chn, vol << 4); break; case VOLCMD_VIBRATODEPTH: Vibrato(chn, vol); break; case VOLCMD_PANSLIDELEFT: PanningSlide(chn, static_cast(vol), !m_playBehaviour[kFT2VolColMemory]); break; case VOLCMD_PANSLIDERIGHT: PanningSlide(chn, static_cast(vol << 4), !m_playBehaviour[kFT2VolColMemory]); break; case VOLCMD_PORTAUP: // IT compatibility (one of the first testcases - link effect memory) PortamentoUp(nChn, static_cast(vol << 2), m_playBehaviour[kITVolColFinePortamento]); break; case VOLCMD_PORTADOWN: // IT compatibility (one of the first testcases - link effect memory) PortamentoDown(nChn, static_cast(vol << 2), m_playBehaviour[kITVolColFinePortamento]); break; case VOLCMD_OFFSET: if(triggerNote && chn.pModSample && vol <= std::size(chn.pModSample->cues)) { SmpLength offset; if(vol == 0) offset = chn.oldOffset; else offset = chn.oldOffset = chn.pModSample->cues[vol - 1]; SampleOffset(chn, offset); } break; case VOLCMD_PLAYCONTROL: if(vol <= 1) chn.isPaused = (vol == 0); break; } } } // Effects if(cmd != CMD_NONE) switch (cmd) { // Set Volume case CMD_VOLUME: if(m_SongFlags[SONG_FIRSTTICK]) { chn.nVolume = (param < 64) ? param * 4 : 256; chn.dwFlags.set(CHN_FASTVOLRAMP); } break; // Portamento Up case CMD_PORTAMENTOUP: if ((!param) && (GetType() & MOD_TYPE_MOD)) break; PortamentoUp(nChn, static_cast(param)); break; // Portamento Down case CMD_PORTAMENTODOWN: if ((!param) && (GetType() & MOD_TYPE_MOD)) break; PortamentoDown(nChn, static_cast(param)); break; // Volume Slide case CMD_VOLUMESLIDE: if (param || (GetType() != MOD_TYPE_MOD)) VolumeSlide(chn, static_cast(param)); break; // Tone-Portamento case CMD_TONEPORTAMENTO: TonePortamento(chn, static_cast(param)); break; // Tone-Portamento + Volume Slide case CMD_TONEPORTAVOL: if ((param) || (GetType() != MOD_TYPE_MOD)) VolumeSlide(chn, static_cast(param)); TonePortamento(chn, 0); break; // Vibrato case CMD_VIBRATO: Vibrato(chn, param); break; // Vibrato + Volume Slide case CMD_VIBRATOVOL: if ((param) || (GetType() != MOD_TYPE_MOD)) VolumeSlide(chn, static_cast(param)); Vibrato(chn, 0); break; // Set Speed case CMD_SPEED: if(m_SongFlags[SONG_FIRSTTICK]) SetSpeed(m_PlayState, param); break; // Set Tempo case CMD_TEMPO: if(m_playBehaviour[kMODVBlankTiming]) { // ProTracker MODs with VBlank timing: All Fxx parameters set the tick count. if(m_SongFlags[SONG_FIRSTTICK] && param != 0) SetSpeed(m_PlayState, param); break; } { param = CalculateXParam(m_PlayState.m_nPattern, m_PlayState.m_nRow, nChn); if (GetType() & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_MPT)) { if (param) chn.nOldTempo = static_cast(param); else param = chn.nOldTempo; } TEMPO t(param, 0); LimitMax(t, GetModSpecifications().GetTempoMax()); SetTempo(t); } break; // Set Offset case CMD_OFFSET: if(triggerNote) { // FT2 compatibility: Portamento + Offset = Ignore offset // Test case: porta-offset.xm if(bPorta && GetType() == MOD_TYPE_XM) break; ProcessSampleOffset(chn, nChn, m_PlayState); } break; // Disorder Tracker 2 percentage offset case CMD_OFFSETPERCENTAGE: if(triggerNote) { SampleOffset(chn, Util::muldiv_unsigned(chn.nLength, param, 256)); } break; // Arpeggio case CMD_ARPEGGIO: // IT compatibility 01. Don't ignore Arpeggio if no note is playing (also valid for ST3) if(m_PlayState.m_nTickCount) break; if((!chn.nPeriod || !chn.nNote) && (chn.pModInstrument == nullptr || !chn.pModInstrument->HasValidMIDIChannel()) // Plugin arpeggio && !m_playBehaviour[kITArpeggio] && (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT))) break; if (!param && (GetType() & (MOD_TYPE_XM | MOD_TYPE_MOD))) break; // Only important when editing MOD/XM files (000 effects are removed when loading files where this means "no effect") chn.nCommand = CMD_ARPEGGIO; if (param) chn.nArpeggio = static_cast(param); break; // Retrig case CMD_RETRIG: if (GetType() & (MOD_TYPE_XM|MOD_TYPE_MT2)) { if (!(param & 0xF0)) param |= chn.nRetrigParam & 0xF0; if (!(param & 0x0F)) param |= chn.nRetrigParam & 0x0F; param |= 0x100; // increment retrig count on first row } // IT compatibility 15. Retrigger if(m_playBehaviour[kITRetrigger]) { if (param) chn.nRetrigParam = static_cast(param & 0xFF); RetrigNote(nChn, chn.nRetrigParam, (volcmd == VOLCMD_OFFSET) ? vol + 1 : 0); } else { // XM Retrig if (param) chn.nRetrigParam = static_cast(param & 0xFF); else param = chn.nRetrigParam; RetrigNote(nChn, param, (volcmd == VOLCMD_OFFSET) ? vol + 1 : 0); } break; // Tremor case CMD_TREMOR: if(!m_SongFlags[SONG_FIRSTTICK]) { break; } // IT compatibility 12. / 13. Tremor (using modified DUMB's Tremor logic here because of old effects - http://dumb.sf.net/) if(m_playBehaviour[kITTremor]) { if(param && !m_SongFlags[SONG_ITOLDEFFECTS]) { // Old effects have different length interpretation (+1 for both on and off) if(param & 0xF0) param -= 0x10; if(param & 0x0F) param -= 0x01; chn.nTremorParam = static_cast(param); } chn.nTremorCount |= 0x80; // set on/off flag } else if(m_playBehaviour[kFT2Tremor]) { // XM Tremor. Logic is being processed in sndmix.cpp chn.nTremorCount |= 0x80; // set on/off flag } chn.nCommand = CMD_TREMOR; if(param) chn.nTremorParam = static_cast(param); break; // Set Global Volume case CMD_GLOBALVOLUME: // IT compatibility: Only apply global volume on first tick (and multiples) // Test case: GlobalVolFirstTick.it if(!m_SongFlags[SONG_FIRSTTICK]) break; // ST3 applies global volume on tick 1 and does other weird things, but we won't emulate this for now. // if(((GetType() & MOD_TYPE_S3M) && m_nTickCount != 1) // || (!(GetType() & MOD_TYPE_S3M) && !m_SongFlags[SONG_FIRSTTICK])) // { // break; // } // FT2 compatibility: On channels that are "left" of the global volume command, the new global volume is not applied // until the second tick of the row. Since we apply global volume on the mix buffer rather than note volumes, this // cannot be fixed for now. // Test case: GlobalVolume.xm // if(IsCompatibleMode(TRK_FASTTRACKER2) && m_SongFlags[SONG_FIRSTTICK] && m_nMusicSpeed > 1) // { // break; // } if (!(GetType() & GLOBALVOL_7BIT_FORMATS)) param *= 2; // IT compatibility 16. ST3 and IT ignore out-of-range values. // Test case: globalvol-invalid.it if(param <= 128) { m_PlayState.m_nGlobalVolume = param * 2; } else if(!(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_S3M))) { m_PlayState.m_nGlobalVolume = 256; } break; // Global Volume Slide case CMD_GLOBALVOLSLIDE: //IT compatibility 16. Saving last global volume slide param per channel (FT2/IT) if(m_playBehaviour[kPerChannelGlobalVolSlide]) GlobalVolSlide(static_cast(param), chn.nOldGlobalVolSlide); else GlobalVolSlide(static_cast(param), m_PlayState.Chn[0].nOldGlobalVolSlide); break; // Set 8-bit Panning case CMD_PANNING8: if(m_SongFlags[SONG_FIRSTTICK]) { Panning(chn, param, Pan8bit); } break; // Panning Slide case CMD_PANNINGSLIDE: PanningSlide(chn, static_cast(param)); break; // Tremolo case CMD_TREMOLO: Tremolo(chn, param); break; // Fine Vibrato case CMD_FINEVIBRATO: FineVibrato(chn, param); break; // MOD/XM Exx Extended Commands case CMD_MODCMDEX: ExtendedMODCommands(nChn, static_cast(param)); break; // S3M/IT Sxx Extended Commands case CMD_S3MCMDEX: if(m_playBehaviour[kST3EffectMemory] && param == 0) { param = chn.nArpeggio; // S00 uses the last non-zero effect parameter as memory, like other effects including Arpeggio, so we "borrow" our memory there. } ExtendedS3MCommands(nChn, static_cast(param)); break; // Key Off case CMD_KEYOFF: // This is how Key Off is supposed to sound... (in FT2 at least) if(m_playBehaviour[kFT2KeyOff]) { if (m_PlayState.m_nTickCount == param) { // XM: Key-Off + Sample == Note Cut if(chn.pModInstrument == nullptr || !chn.pModInstrument->VolEnv.dwFlags[ENV_ENABLED]) { if(param == 0 && (chn.rowCommand.instr || chn.rowCommand.volcmd != VOLCMD_NONE)) // FT2 is weird.... { chn.dwFlags.set(CHN_NOTEFADE); } else { chn.dwFlags.set(CHN_FASTVOLRAMP); chn.nVolume = 0; } } KeyOff(chn); } } // This is how it's NOT supposed to sound... else { if(m_SongFlags[SONG_FIRSTTICK]) KeyOff(chn); } break; // Extra-fine porta up/down case CMD_XFINEPORTAUPDOWN: switch(param & 0xF0) { case 0x10: ExtraFinePortamentoUp(chn, param & 0x0F); break; case 0x20: ExtraFinePortamentoDown(chn, param & 0x0F); break; // ModPlug XM Extensions (ignore in compatible mode) case 0x50: case 0x60: case 0x70: case 0x90: case 0xA0: if(!m_playBehaviour[kFT2RestrictXCommand]) ExtendedS3MCommands(nChn, static_cast(param)); break; } break; case CMD_FINETUNE: case CMD_FINETUNE_SMOOTH: if(m_SongFlags[SONG_FIRSTTICK] || cmd == CMD_FINETUNE_SMOOTH) { SetFinetune(nChn, m_PlayState, cmd == CMD_FINETUNE_SMOOTH); #ifndef NO_PLUGINS if(IMixPlugin *plugin = GetChannelInstrumentPlugin(m_PlayState.Chn[nChn]); plugin != nullptr) plugin->MidiPitchBendRaw(chn.GetMIDIPitchBend(), nChn); #endif // NO_PLUGINS } break; // Set Channel Global Volume case CMD_CHANNELVOLUME: if(!m_SongFlags[SONG_FIRSTTICK]) break; if (param <= 64) { chn.nGlobalVol = param; chn.dwFlags.set(CHN_FASTVOLRAMP); } break; // Channel volume slide case CMD_CHANNELVOLSLIDE: ChannelVolSlide(chn, static_cast(param)); break; // Panbrello (IT) case CMD_PANBRELLO: Panbrello(chn, param); break; // Set Envelope Position case CMD_SETENVPOSITION: if(m_SongFlags[SONG_FIRSTTICK]) { chn.VolEnv.nEnvPosition = param; // FT2 compatibility: FT2 only sets the position of the panning envelope if the volume envelope's sustain flag is set // Test case: SetEnvPos.xm if(!m_playBehaviour[kFT2SetPanEnvPos] || chn.VolEnv.flags[ENV_SUSTAIN]) { chn.PanEnv.nEnvPosition = param; chn.PitchEnv.nEnvPosition = param; } } break; // Position Jump case CMD_POSITIONJUMP: PositionJump(m_PlayState, nChn); break; // Pattern Break case CMD_PATTERNBREAK: if(ROWINDEX row = PatternBreak(m_PlayState, nChn, static_cast(param)); row != ROWINDEX_INVALID) { m_PlayState.m_breakRow = row; if(m_SongFlags[SONG_PATTERNLOOP]) { //If song is set to loop and a pattern break occurs we should stay on the same pattern. //Use nPosJump to force playback to "jump to this pattern" rather than move to next, as by default. m_PlayState.m_posJump = m_PlayState.m_nCurrentOrder; } } break; // IMF / PTM Note Slides case CMD_NOTESLIDEUP: case CMD_NOTESLIDEDOWN: case CMD_NOTESLIDEUPRETRIG: case CMD_NOTESLIDEDOWNRETRIG: // Note that this command seems to be a bit buggy in Polytracker... Luckily, no tune seems to seriously use this // (Vic uses it e.g. in Spaceman or Perfect Reason to slide effect samples, noone will notice the difference :) NoteSlide(chn, param, cmd == CMD_NOTESLIDEUP || cmd == CMD_NOTESLIDEUPRETRIG, cmd == CMD_NOTESLIDEUPRETRIG || cmd == CMD_NOTESLIDEDOWNRETRIG); break; // PTM Reverse sample + offset (executed on every tick) case CMD_REVERSEOFFSET: ReverseSampleOffset(chn, static_cast(param)); break; #ifndef NO_PLUGINS // DBM: Toggle DSP Echo case CMD_DBMECHO: if(m_PlayState.m_nTickCount == 0) { uint32 echoType = (param >> 4), enable = (param & 0x0F); if(echoType > 2 || enable > 1) { break; } CHANNELINDEX firstChn = nChn, lastChn = nChn; if(echoType == 1) { firstChn = 0; lastChn = m_nChannels - 1; } for(CHANNELINDEX c = firstChn; c <= lastChn; c++) { ChnSettings[c].dwFlags.set(CHN_NOFX, enable == 1); m_PlayState.Chn[c].dwFlags.set(CHN_NOFX, enable == 1); } } break; #endif // NO_PLUGINS // Digi Booster sample reverse case CMD_DIGIREVERSESAMPLE: DigiBoosterSampleReverse(chn, static_cast(param)); break; } if(m_playBehaviour[kST3EffectMemory] && param != 0) { UpdateS3MEffectMemory(chn, static_cast(param)); } if(chn.rowCommand.instr) { // Not necessarily consistent with actually playing instrument for IT compatibility chn.nOldIns = chn.rowCommand.instr; } } // for(...) end // Navigation Effects if(m_SongFlags[SONG_FIRSTTICK]) { if(HandleNextRow(m_PlayState, Order(), true)) m_SongFlags.set(SONG_BREAKTOROW); } return true; } bool CSoundFile::HandleNextRow(PlayState &state, const ModSequence &order, bool honorPatternLoop) const { const bool doPatternLoop = (state.m_patLoopRow != ROWINDEX_INVALID); const bool doBreakRow = (state.m_breakRow != ROWINDEX_INVALID); const bool doPosJump = (state.m_posJump != ORDERINDEX_INVALID); bool breakToRow = false; // Pattern Break / Position Jump only if no loop running // Exception: FastTracker 2 in all cases, Impulse Tracker in case of position jump // Test case for FT2 exception: PatLoop-Jumps.xm, PatLoop-Various.xm // Test case for IT: exception: LoopBreak.it, sbx-priority.it if((doBreakRow || doPosJump) && (!doPatternLoop || m_playBehaviour[kFT2PatternLoopWithJumps] || (m_playBehaviour[kITPatternLoopWithJumps] && doPosJump) || (m_playBehaviour[kITPatternLoopWithJumpsOld] && doPosJump))) { if(!doPosJump) state.m_posJump = state.m_nCurrentOrder + 1; if(!doBreakRow) state.m_breakRow = 0; breakToRow = true; if(state.m_posJump >= order.size()) state.m_posJump = order.GetRestartPos(); // IT / FT2 compatibility: don't reset loop count on pattern break. // Test case: gm-trippy01.it, PatLoop-Break.xm, PatLoop-Weird.xm, PatLoop-Break.mod if(state.m_posJump != state.m_nCurrentOrder && !m_playBehaviour[kITPatternLoopBreak] && !m_playBehaviour[kFT2PatternLoopWithJumps] && GetType() != MOD_TYPE_MOD) { for(CHANNELINDEX i = 0; i < GetNumChannels(); i++) { state.Chn[i].nPatternLoopCount = 0; } } state.m_nNextRow = state.m_breakRow; if(!honorPatternLoop || !m_SongFlags[SONG_PATTERNLOOP]) state.m_nNextOrder = state.m_posJump; } else if(doPatternLoop) { // Pattern Loop state.m_nNextOrder = state.m_nCurrentOrder; state.m_nNextRow = state.m_patLoopRow; // FT2 skips the first row of the pattern loop if there's a pattern delay, ProTracker sometimes does it too (didn't quite figure it out yet). // But IT and ST3 don't do this. // Test cases: PatLoopWithDelay.it, PatLoopWithDelay.s3m if(state.m_nPatternDelay && (GetType() != MOD_TYPE_IT || !m_playBehaviour[kITPatternLoopWithJumps]) && GetType() != MOD_TYPE_S3M) { state.m_nNextRow++; } // IT Compatibility: If the restart row is past the end of the current pattern // (e.g. when continued from a previous pattern without explicit SB0 effect), continue the next pattern. // Test case: LoopStartAfterPatternEnd.it if(state.m_patLoopRow >= Patterns[state.m_nPattern].GetNumRows()) { state.m_nNextOrder++; state.m_nNextRow = 0; } } return breakToRow; } //////////////////////////////////////////////////////////// // Channels effects // Update the effect memory of all S3M effects that use the last non-zero effect parameter as memory (Dxy, Exx, Fxx, Ixy, Jxy, Kxy, Lxy, Qxy, Rxy, Sxy) // Test case: ParamMemory.s3m void CSoundFile::UpdateS3MEffectMemory(ModChannel &chn, ModCommand::PARAM param) const { chn.nOldVolumeSlide = param; // Dxy / Kxy / Lxy chn.nOldPortaUp = param; // Exx / Fxx chn.nOldPortaDown = param; // Exx / Fxx chn.nTremorParam = param; // Ixy chn.nArpeggio = param; // Jxy chn.nRetrigParam = param; // Qxy chn.nTremoloDepth = (param & 0x0F) << 2; // Rxy chn.nTremoloSpeed = (param >> 4) & 0x0F; // Rxy // Sxy is not handled here. } // Calculate full parameter for effects that support parameter extension at the given pattern location. // maxCommands sets the maximum number of XParam commands to look at for this effect // extendedRows returns how many extended rows are used (i.e. a value of 0 means the command is not extended). uint32 CSoundFile::CalculateXParam(PATTERNINDEX pat, ROWINDEX row, CHANNELINDEX chn, uint32 *extendedRows) const { if(extendedRows != nullptr) *extendedRows = 0; if(!Patterns.IsValidPat(pat)) { #ifdef MPT_BUILD_FUZZER // Ending up in this situation implies a logic error std::abort(); #else return 0; #endif } ROWINDEX maxCommands = 4; const ModCommand *m = Patterns[pat].GetpModCommand(row, chn); const auto startCmd = m->command; uint32 val = m->param; switch(m->command) { case CMD_OFFSET: // 24 bit command maxCommands = 2; break; case CMD_TEMPO: case CMD_PATTERNBREAK: case CMD_POSITIONJUMP: case CMD_FINETUNE: case CMD_FINETUNE_SMOOTH: // 16 bit command maxCommands = 1; break; default: return val; } const bool xmTempoFix = m->command == CMD_TEMPO && GetType() == MOD_TYPE_XM; ROWINDEX numRows = std::min(Patterns[pat].GetNumRows() - row - 1, maxCommands); uint32 extRows = 0; while(numRows > 0) { m += Patterns[pat].GetNumChannels(); if(m->command != CMD_XPARAM) break; if(xmTempoFix && val < 256) { // With XM, 0x20 is the lowest tempo. Anything below changes ticks per row. val -= 0x20; } val = (val << 8) | m->param; numRows--; extRows++; } // Always return a full-precision value for finetune if((startCmd == CMD_FINETUNE || startCmd == CMD_FINETUNE_SMOOTH) && !extRows) val <<= 8; if(extendedRows != nullptr) *extendedRows = extRows; return val; } void CSoundFile::PositionJump(PlayState &state, CHANNELINDEX chn) const { state.m_nextPatStartRow = 0; // FT2 E60 bug state.m_posJump = static_cast(CalculateXParam(state.m_nPattern, state.m_nRow, chn)); // see https://forum.openmpt.org/index.php?topic=2769.0 - FastTracker resets Dxx if Bxx is called _after_ Dxx // Test case: PatternJump.mod if((GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM)) && state.m_breakRow != ROWINDEX_INVALID) { state.m_breakRow = 0; } } ROWINDEX CSoundFile::PatternBreak(PlayState &state, CHANNELINDEX chn, uint8 param) const { if(param >= 64 && (GetType() & MOD_TYPE_S3M)) { // ST3 ignores invalid pattern breaks. return ROWINDEX_INVALID; } state.m_nextPatStartRow = 0; // FT2 E60 bug return static_cast(CalculateXParam(state.m_nPattern, state.m_nRow, chn)); } void CSoundFile::PortamentoUp(CHANNELINDEX nChn, ModCommand::PARAM param, const bool doFinePortamentoAsRegular) { ModChannel &chn = m_PlayState.Chn[nChn]; if(param) { // FT2 compatibility: Separate effect memory for all portamento commands // Test case: Porta-LinkMem.xm if(!m_playBehaviour[kFT2PortaUpDownMemory]) chn.nOldPortaDown = param; chn.nOldPortaUp = param; } else { param = chn.nOldPortaUp; } const bool doFineSlides = !doFinePortamentoAsRegular && !(GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MED | MOD_TYPE_AMF0 | MOD_TYPE_DIGI | MOD_TYPE_STP | MOD_TYPE_DTM)); // Process MIDI pitch bend for instrument plugins MidiPortamento(nChn, param, doFineSlides); if(GetType() == MOD_TYPE_MPT && chn.pModInstrument && chn.pModInstrument->pTuning) { // Portamento for instruments with custom tuning if(param >= 0xF0 && !doFinePortamentoAsRegular) PortamentoFineMPT(chn, param - 0xF0); else if(param >= 0xE0 && !doFinePortamentoAsRegular) PortamentoExtraFineMPT(chn, param - 0xE0); else PortamentoMPT(chn, param); return; } else if(GetType() == MOD_TYPE_PLM) { // A normal portamento up or down makes a follow-up tone portamento go the same direction. chn.nPortamentoDest = 1; } if (doFineSlides && param >= 0xE0) { if (param & 0x0F) { if ((param & 0xF0) == 0xF0) { FinePortamentoUp(chn, param & 0x0F); return; } else if ((param & 0xF0) == 0xE0 && GetType() != MOD_TYPE_DBM) { ExtraFinePortamentoUp(chn, param & 0x0F); return; } } if(GetType() != MOD_TYPE_DBM) { // DBM only has fine slides, no extra-fine slides. return; } } // Regular Slide if(!chn.isFirstTick || (m_PlayState.m_nMusicSpeed == 1 && m_playBehaviour[kSlidesAtSpeed1]) || (GetType() & (MOD_TYPE_669 | MOD_TYPE_OKT)) || (GetType() == MOD_TYPE_MED && m_SongFlags[SONG_FASTVOLSLIDES])) { DoFreqSlide(chn, chn.nPeriod, param * 4); } } void CSoundFile::PortamentoDown(CHANNELINDEX nChn, ModCommand::PARAM param, const bool doFinePortamentoAsRegular) { ModChannel &chn = m_PlayState.Chn[nChn]; if(param) { // FT2 compatibility: Separate effect memory for all portamento commands // Test case: Porta-LinkMem.xm if(!m_playBehaviour[kFT2PortaUpDownMemory]) chn.nOldPortaUp = param; chn.nOldPortaDown = param; } else { param = chn.nOldPortaDown; } const bool doFineSlides = !doFinePortamentoAsRegular && !(GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MED | MOD_TYPE_AMF0 | MOD_TYPE_DIGI | MOD_TYPE_STP | MOD_TYPE_DTM)); // Process MIDI pitch bend for instrument plugins MidiPortamento(nChn, -static_cast(param), doFineSlides); if(GetType() == MOD_TYPE_MPT && chn.pModInstrument && chn.pModInstrument->pTuning) { // Portamento for instruments with custom tuning if(param >= 0xF0 && !doFinePortamentoAsRegular) PortamentoFineMPT(chn, -static_cast(param - 0xF0)); else if(param >= 0xE0 && !doFinePortamentoAsRegular) PortamentoExtraFineMPT(chn, -static_cast(param - 0xE0)); else PortamentoMPT(chn, -static_cast(param)); return; } else if(GetType() == MOD_TYPE_PLM) { // A normal portamento up or down makes a follow-up tone portamento go the same direction. chn.nPortamentoDest = 65535; } if(doFineSlides && param >= 0xE0) { if (param & 0x0F) { if ((param & 0xF0) == 0xF0) { FinePortamentoDown(chn, param & 0x0F); return; } else if ((param & 0xF0) == 0xE0 && GetType() != MOD_TYPE_DBM) { ExtraFinePortamentoDown(chn, param & 0x0F); return; } } if(GetType() != MOD_TYPE_DBM) { // DBM only has fine slides, no extra-fine slides. return; } } if(!chn.isFirstTick || (m_PlayState.m_nMusicSpeed == 1 && m_playBehaviour[kSlidesAtSpeed1]) || (GetType() & (MOD_TYPE_669 | MOD_TYPE_OKT)) || (GetType() == MOD_TYPE_MED && m_SongFlags[SONG_FASTVOLSLIDES])) { DoFreqSlide(chn, chn.nPeriod, param * -4); } } // Send portamento commands to plugins void CSoundFile::MidiPortamento(CHANNELINDEX nChn, int param, bool doFineSlides) { int actualParam = std::abs(param); int pitchBend = 0; // Old MIDI Pitch Bends: // - Applied on every tick // - No fine pitch slides (they are interpreted as normal slides) // New MIDI Pitch Bends: // - Behaviour identical to sample pitch bends if the instrument's PWD parameter corresponds to the actual VSTi setting. if(doFineSlides && actualParam >= 0xE0 && !m_playBehaviour[kOldMIDIPitchBends]) { if(m_PlayState.Chn[nChn].isFirstTick) { // Extra fine slide... pitchBend = (actualParam & 0x0F) * mpt::signum(param); if(actualParam >= 0xF0) { // ... or just a fine slide! pitchBend *= 4; } } } else if(!m_PlayState.Chn[nChn].isFirstTick || m_playBehaviour[kOldMIDIPitchBends]) { // Regular slide pitchBend = param * 4; } if(pitchBend) { #ifndef NO_PLUGINS IMixPlugin *plugin = GetChannelInstrumentPlugin(m_PlayState.Chn[nChn]); if(plugin != nullptr) { int8 pwd = 13; // Early OpenMPT legacy... Actually it's not *exactly* 13, but close enough... if(m_PlayState.Chn[nChn].pModInstrument != nullptr) { pwd = m_PlayState.Chn[nChn].pModInstrument->midiPWD; } plugin->MidiPitchBend(pitchBend, pwd, nChn); } #endif // NO_PLUGINS } } void CSoundFile::FinePortamentoUp(ModChannel &chn, ModCommand::PARAM param) const { MPT_ASSERT(!chn.HasCustomTuning()); if(GetType() == MOD_TYPE_XM) { // FT2 compatibility: E1x / E2x / X1x / X2x memory is not linked // Test case: Porta-LinkMem.xm if(param) chn.nOldFinePortaUpDown = (chn.nOldFinePortaUpDown & 0x0F) | (param << 4); else param = (chn.nOldFinePortaUpDown >> 4); } else if(GetType() == MOD_TYPE_MT2) { if(param) chn.nOldFinePortaUpDown = param; else param = chn.nOldFinePortaUpDown; } if(chn.isFirstTick && chn.nPeriod && param) DoFreqSlide(chn, chn.nPeriod, param * 4); } void CSoundFile::FinePortamentoDown(ModChannel &chn, ModCommand::PARAM param) const { MPT_ASSERT(!chn.HasCustomTuning()); if(GetType() == MOD_TYPE_XM) { // FT2 compatibility: E1x / E2x / X1x / X2x memory is not linked // Test case: Porta-LinkMem.xm if(param) chn.nOldFinePortaUpDown = (chn.nOldFinePortaUpDown & 0xF0) | (param & 0x0F); else param = (chn.nOldFinePortaUpDown & 0x0F); } else if(GetType() == MOD_TYPE_MT2) { if(param) chn.nOldFinePortaUpDown = param; else param = chn.nOldFinePortaUpDown; } if(chn.isFirstTick && chn.nPeriod && param) { DoFreqSlide(chn, chn.nPeriod, param * -4); if(chn.nPeriod > 0xFFFF && !m_playBehaviour[kPeriodsAreHertz] && (!m_SongFlags[SONG_LINEARSLIDES] || GetType() == MOD_TYPE_XM)) chn.nPeriod = 0xFFFF; } } void CSoundFile::ExtraFinePortamentoUp(ModChannel &chn, ModCommand::PARAM param) const { MPT_ASSERT(!chn.HasCustomTuning()); if(GetType() == MOD_TYPE_XM) { // FT2 compatibility: E1x / E2x / X1x / X2x memory is not linked // Test case: Porta-LinkMem.xm if(param) chn.nOldExtraFinePortaUpDown = (chn.nOldExtraFinePortaUpDown & 0x0F) | (param << 4); else param = (chn.nOldExtraFinePortaUpDown >> 4); } else if(GetType() == MOD_TYPE_MT2) { if(param) chn.nOldFinePortaUpDown = param; else param = chn.nOldFinePortaUpDown; } if(chn.isFirstTick && chn.nPeriod && param) DoFreqSlide(chn, chn.nPeriod, param); } void CSoundFile::ExtraFinePortamentoDown(ModChannel &chn, ModCommand::PARAM param) const { MPT_ASSERT(!chn.HasCustomTuning()); if(GetType() == MOD_TYPE_XM) { // FT2 compatibility: E1x / E2x / X1x / X2x memory is not linked // Test case: Porta-LinkMem.xm if(param) chn.nOldExtraFinePortaUpDown = (chn.nOldExtraFinePortaUpDown & 0xF0) | (param & 0x0F); else param = (chn.nOldExtraFinePortaUpDown & 0x0F); } else if(GetType() == MOD_TYPE_MT2) { if(param) chn.nOldFinePortaUpDown = param; else param = chn.nOldFinePortaUpDown; } if(chn.isFirstTick && chn.nPeriod && param) { DoFreqSlide(chn, chn.nPeriod, -static_cast(param)); if(chn.nPeriod > 0xFFFF && !m_playBehaviour[kPeriodsAreHertz] && (!m_SongFlags[SONG_LINEARSLIDES] || GetType() == MOD_TYPE_XM)) chn.nPeriod = 0xFFFF; } } void CSoundFile::SetFinetune(CHANNELINDEX channel, PlayState &playState, bool isSmooth) const { ModChannel &chn = playState.Chn[channel]; int16 newTuning = mpt::saturate_cast(static_cast(CalculateXParam(playState.m_nPattern, playState.m_nRow, channel, nullptr)) - 0x8000); if(isSmooth) { const int32 ticksLeft = playState.TicksOnRow() - playState.m_nTickCount; if(ticksLeft > 1) { const int32 step = (newTuning - chn.microTuning) / ticksLeft; newTuning = mpt::saturate_cast(chn.microTuning + step); } } chn.microTuning = newTuning; } // Implemented for IMF / PTM / OKT compatibility, can't actually save this in any formats // Slide up / down every x ticks by y semitones // Oktalyzer: Slide down on first tick only, or on every tick void CSoundFile::NoteSlide(ModChannel &chn, uint32 param, bool slideUp, bool retrig) const { if(m_SongFlags[SONG_FIRSTTICK]) { if(param & 0xF0) chn.noteSlideParam = static_cast(param & 0xF0) | (chn.noteSlideParam & 0x0F); if(param & 0x0F) chn.noteSlideParam = (chn.noteSlideParam & 0xF0) | static_cast(param & 0x0F); chn.noteSlideCounter = (chn.noteSlideParam >> 4); } bool doTrigger = false; if(GetType() == MOD_TYPE_OKT) doTrigger = ((chn.noteSlideParam & 0xF0) == 0x10) || m_SongFlags[SONG_FIRSTTICK]; else doTrigger = !m_SongFlags[SONG_FIRSTTICK] && (--chn.noteSlideCounter == 0); if(doTrigger) { const uint8 speed = (chn.noteSlideParam >> 4), steps = (chn.noteSlideParam & 0x0F); chn.noteSlideCounter = speed; // update it const int32 delta = (slideUp ? steps : -steps); if(chn.HasCustomTuning()) chn.m_PortamentoFineSteps += delta * chn.pModInstrument->pTuning->GetFineStepCount(); else chn.nPeriod = GetPeriodFromNote(delta + GetNoteFromPeriod(chn.nPeriod, chn.nFineTune, chn.nC5Speed), chn.nFineTune, chn.nC5Speed); if(retrig) chn.position.Set(0); } } std::pair CSoundFile::GetVolCmdTonePorta(const ModCommand &m, uint32 startTick) const { if(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_AMS | MOD_TYPE_DMF | MOD_TYPE_DBM | MOD_TYPE_IMF | MOD_TYPE_PSM | MOD_TYPE_J2B | MOD_TYPE_ULT | MOD_TYPE_OKT | MOD_TYPE_MT2 | MOD_TYPE_MDL)) { return {ImpulseTrackerPortaVolCmd[m.vol & 0x0F], false}; } else { bool clearEffectColumn = false; uint16 vol = m.vol; if(m.command == CMD_TONEPORTAMENTO && GetType() == MOD_TYPE_XM) { // Yes, FT2 is *that* weird. If there is a Mx command in the volume column // and a normal 3xx command, the 3xx command is ignored but the Mx command's // effectiveness is doubled. // Test case: TonePortamentoMemory.xm clearEffectColumn = true; vol *= 2; } // FT2 compatibility: If there's a portamento and a note delay, execute the portamento, but don't update the parameter // Test case: PortaDelay.xm if(m_playBehaviour[kFT2PortaDelay] && startTick != 0) return {uint16(0), clearEffectColumn}; else return {static_cast(vol * 16), clearEffectColumn}; } } // Portamento Slide void CSoundFile::TonePortamento(ModChannel &chn, uint16 param) const { chn.dwFlags.set(CHN_PORTAMENTO); //IT compatibility 03: Share effect memory with portamento up/down if((!m_SongFlags[SONG_ITCOMPATGXX] && m_playBehaviour[kITPortaMemoryShare]) || GetType() == MOD_TYPE_PLM) { if(param == 0) param = chn.nOldPortaUp; chn.nOldPortaUp = chn.nOldPortaDown = static_cast(param); } if(param) chn.portamentoSlide = param; if(chn.HasCustomTuning()) { //Behavior: Param tells number of finesteps(or 'fullsteps'(notes) with glissando) //to slide per row(not per tick). if(chn.portamentoSlide == 0) return; const int32 oldPortamentoTickSlide = (m_PlayState.m_nTickCount != 0) ? chn.m_PortamentoTickSlide : 0; int32 delta = chn.portamentoSlide; if(chn.nPortamentoDest < 0) delta = -delta; chn.m_PortamentoTickSlide = static_cast((m_PlayState.m_nTickCount + 1.0) * delta / m_PlayState.m_nMusicSpeed); if(chn.dwFlags[CHN_GLISSANDO]) { chn.m_PortamentoTickSlide *= chn.pModInstrument->pTuning->GetFineStepCount() + 1; //With glissando interpreting param as notes instead of finesteps. } const int32 slide = chn.m_PortamentoTickSlide - oldPortamentoTickSlide; if(std::abs(chn.nPortamentoDest) <= std::abs(slide)) { if(chn.nPortamentoDest != 0) { chn.m_PortamentoFineSteps += chn.nPortamentoDest; chn.nPortamentoDest = 0; chn.m_CalculateFreq = true; } } else { chn.m_PortamentoFineSteps += slide; chn.nPortamentoDest -= slide; chn.m_CalculateFreq = true; } return; } bool doPorta = !chn.isFirstTick || (GetType() & (MOD_TYPE_DBM | MOD_TYPE_669)) || (m_PlayState.m_nMusicSpeed == 1 && m_playBehaviour[kSlidesAtSpeed1]) || (GetType() == MOD_TYPE_MED && m_SongFlags[SONG_FASTVOLSLIDES]); int32 delta = chn.portamentoSlide; if(GetType() == MOD_TYPE_PLM && delta >= 0xF0) { delta -= 0xF0; doPorta = chn.isFirstTick; } if(chn.nPeriod && chn.nPortamentoDest && doPorta) { delta *= (GetType() == MOD_TYPE_669) ? 2 : 4; if(!PeriodsAreFrequencies()) delta = -delta; if(chn.nPeriod < chn.nPortamentoDest || chn.portaTargetReached) { DoFreqSlide(chn, chn.nPeriod, delta, true); if(chn.nPeriod > chn.nPortamentoDest) chn.nPeriod = chn.nPortamentoDest; } else if(chn.nPeriod > chn.nPortamentoDest) { DoFreqSlide(chn, chn.nPeriod, -delta, true); if(chn.nPeriod < chn.nPortamentoDest) chn.nPeriod = chn.nPortamentoDest; // FT2 compatibility: Reaching portamento target from below forces subsequent portamentos on the same note to use the logic for reaching the note from above instead. // Test case: PortaResetDirection.xm if(chn.nPeriod == chn.nPortamentoDest && m_playBehaviour[kFT2PortaResetDirection]) chn.portaTargetReached = true; } } // IT compatibility 23. Portamento with no note // ProTracker also disables portamento once the target is reached. // Test case: PortaTarget.mod if(chn.nPeriod == chn.nPortamentoDest && (m_playBehaviour[kITPortaTargetReached] || GetType() == MOD_TYPE_MOD)) chn.nPortamentoDest = 0; } void CSoundFile::Vibrato(ModChannel &chn, uint32 param) const { if (param & 0x0F) chn.nVibratoDepth = (param & 0x0F) * 4; if (param & 0xF0) chn.nVibratoSpeed = (param >> 4) & 0x0F; chn.dwFlags.set(CHN_VIBRATO); } void CSoundFile::FineVibrato(ModChannel &chn, uint32 param) const { if (param & 0x0F) chn.nVibratoDepth = param & 0x0F; if (param & 0xF0) chn.nVibratoSpeed = (param >> 4) & 0x0F; chn.dwFlags.set(CHN_VIBRATO); // ST3 compatibility: Do not distinguish between vibrato types in effect memory // Test case: VibratoTypeChange.s3m if(m_playBehaviour[kST3VibratoMemory] && (param & 0x0F)) { chn.nVibratoDepth *= 4u; } } void CSoundFile::Panbrello(ModChannel &chn, uint32 param) const { if (param & 0x0F) chn.nPanbrelloDepth = param & 0x0F; if (param & 0xF0) chn.nPanbrelloSpeed = (param >> 4) & 0x0F; } void CSoundFile::Panning(ModChannel &chn, uint32 param, PanningType panBits) const { // No panning in ProTracker mode if(m_playBehaviour[kMODIgnorePanning]) { return; } // IT Compatibility (and other trackers as well): panning disables surround (unless panning in rear channels is enabled, which is not supported by the original trackers anyway) if (!m_SongFlags[SONG_SURROUNDPAN] && (panBits == Pan8bit || m_playBehaviour[kPanOverride])) { chn.dwFlags.reset(CHN_SURROUND); } if(panBits == Pan4bit) { // 0...15 panning chn.nPan = (param * 256 + 8) / 15; } else if(panBits == Pan6bit) { // 0...64 panning if(param > 64) param = 64; chn.nPan = param * 4; } else { if(!(GetType() & (MOD_TYPE_S3M | MOD_TYPE_DSM | MOD_TYPE_AMF0 | MOD_TYPE_AMF | MOD_TYPE_MTM))) { // Real 8-bit panning chn.nPan = param; } else { // 7-bit panning + surround if(param <= 0x80) { chn.nPan = param << 1; } else if(param == 0xA4) { chn.dwFlags.set(CHN_SURROUND); chn.nPan = 0x80; } } } chn.dwFlags.set(CHN_FASTVOLRAMP); chn.nRestorePanOnNewNote = 0; //IT compatibility 20. Set pan overrides random pan if(m_playBehaviour[kPanOverride]) { chn.nPanSwing = 0; chn.nPanbrelloOffset = 0; } } void CSoundFile::VolumeSlide(ModChannel &chn, ModCommand::PARAM param) const { if (param) chn.nOldVolumeSlide = param; else param = chn.nOldVolumeSlide; if((GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MED | MOD_TYPE_DIGI | MOD_TYPE_STP | MOD_TYPE_DTM))) { // MOD / XM nibble priority if((param & 0xF0) != 0) { param &= 0xF0; } else { param &= 0x0F; } } int newVolume = chn.nVolume; if(!(GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM | MOD_TYPE_AMF0 | MOD_TYPE_MED | MOD_TYPE_DIGI))) { if ((param & 0x0F) == 0x0F) //Fine upslide or slide -15 { if (param & 0xF0) //Fine upslide { FineVolumeUp(chn, (param >> 4), false); return; } else //Slide -15 { if(chn.isFirstTick && !m_SongFlags[SONG_FASTVOLSLIDES]) { newVolume -= 0x0F * 4; } } } else if ((param & 0xF0) == 0xF0) //Fine downslide or slide +15 { if (param & 0x0F) //Fine downslide { FineVolumeDown(chn, (param & 0x0F), false); return; } else //Slide +15 { if(chn.isFirstTick && !m_SongFlags[SONG_FASTVOLSLIDES]) { newVolume += 0x0F * 4; } } } } if(!chn.isFirstTick || m_SongFlags[SONG_FASTVOLSLIDES] || (m_PlayState.m_nMusicSpeed == 1 && GetType() == MOD_TYPE_DBM)) { // IT compatibility: Ignore slide commands with both nibbles set. if (param & 0x0F) { if(!(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) || (param & 0xF0) == 0) newVolume -= (int)((param & 0x0F) * 4); } else { newVolume += (int)((param & 0xF0) >> 2); } if (GetType() == MOD_TYPE_MOD) chn.dwFlags.set(CHN_FASTVOLRAMP); } newVolume = Clamp(newVolume, 0, 256); chn.nVolume = newVolume; } void CSoundFile::PanningSlide(ModChannel &chn, ModCommand::PARAM param, bool memory) const { if(memory) { // FT2 compatibility: Use effect memory (lxx and rxx in XM shouldn't use effect memory). // Test case: PanSlideMem.xm if(param) chn.nOldPanSlide = param; else param = chn.nOldPanSlide; } if((GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2))) { // XM nibble priority if((param & 0xF0) != 0) { param &= 0xF0; } else { param &= 0x0F; } } int32 nPanSlide = 0; if(!(GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2))) { if (((param & 0x0F) == 0x0F) && (param & 0xF0)) { if(m_SongFlags[SONG_FIRSTTICK]) { param = (param & 0xF0) / 4u; nPanSlide = - (int)param; } } else if (((param & 0xF0) == 0xF0) && (param & 0x0F)) { if(m_SongFlags[SONG_FIRSTTICK]) { nPanSlide = (param & 0x0F) * 4u; } } else if(!m_SongFlags[SONG_FIRSTTICK]) { if (param & 0x0F) { // IT compatibility: Ignore slide commands with both nibbles set. if(!(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) || (param & 0xF0) == 0) nPanSlide = (int)((param & 0x0F) * 4u); } else { nPanSlide = -(int)((param & 0xF0) / 4u); } } } else { if(!m_SongFlags[SONG_FIRSTTICK]) { if (param & 0xF0) { nPanSlide = (int)((param & 0xF0) / 4u); } else { nPanSlide = -(int)((param & 0x0F) * 4u); } // FT2 compatibility: FT2's panning slide is like IT's fine panning slide (not as deep) if(m_playBehaviour[kFT2PanSlide]) nPanSlide /= 4; } } if (nPanSlide) { nPanSlide += chn.nPan; nPanSlide = Clamp(nPanSlide, 0, 256); chn.nPan = nPanSlide; chn.nRestorePanOnNewNote = 0; } } void CSoundFile::FineVolumeUp(ModChannel &chn, ModCommand::PARAM param, bool volCol) const { if(GetType() == MOD_TYPE_XM) { // FT2 compatibility: EAx / EBx memory is not linked // Test case: FineVol-LinkMem.xm if(param) chn.nOldFineVolUpDown = (param << 4) | (chn.nOldFineVolUpDown & 0x0F); else param = (chn.nOldFineVolUpDown >> 4); } else if(volCol) { if(param) chn.nOldVolParam = param; else param = chn.nOldVolParam; } else { if(param) chn.nOldFineVolUpDown = param; else param = chn.nOldFineVolUpDown; } if(chn.isFirstTick) { chn.nVolume += param * 4; if(chn.nVolume > 256) chn.nVolume = 256; if(GetType() & MOD_TYPE_MOD) chn.dwFlags.set(CHN_FASTVOLRAMP); } } void CSoundFile::FineVolumeDown(ModChannel &chn, ModCommand::PARAM param, bool volCol) const { if(GetType() == MOD_TYPE_XM) { // FT2 compatibility: EAx / EBx memory is not linked // Test case: FineVol-LinkMem.xm if(param) chn.nOldFineVolUpDown = param | (chn.nOldFineVolUpDown & 0xF0); else param = (chn.nOldFineVolUpDown & 0x0F); } else if(volCol) { if(param) chn.nOldVolParam = param; else param = chn.nOldVolParam; } else { if(param) chn.nOldFineVolUpDown = param; else param = chn.nOldFineVolUpDown; } if(chn.isFirstTick) { chn.nVolume -= param * 4; if(chn.nVolume < 0) chn.nVolume = 0; if(GetType() & MOD_TYPE_MOD) chn.dwFlags.set(CHN_FASTVOLRAMP); } } void CSoundFile::Tremolo(ModChannel &chn, uint32 param) const { if (param & 0x0F) chn.nTremoloDepth = (param & 0x0F) << 2; if (param & 0xF0) chn.nTremoloSpeed = (param >> 4) & 0x0F; chn.dwFlags.set(CHN_TREMOLO); } void CSoundFile::ChannelVolSlide(ModChannel &chn, ModCommand::PARAM param) const { int32 nChnSlide = 0; if (param) chn.nOldChnVolSlide = param; else param = chn.nOldChnVolSlide; if (((param & 0x0F) == 0x0F) && (param & 0xF0)) { if(m_SongFlags[SONG_FIRSTTICK]) nChnSlide = param >> 4; } else if (((param & 0xF0) == 0xF0) && (param & 0x0F)) { if(m_SongFlags[SONG_FIRSTTICK]) nChnSlide = - (int)(param & 0x0F); } else { if(!m_SongFlags[SONG_FIRSTTICK]) { if (param & 0x0F) { if(!(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_J2B | MOD_TYPE_DBM)) || (param & 0xF0) == 0) nChnSlide = -(int)(param & 0x0F); } else { nChnSlide = (int)((param & 0xF0) >> 4); } } } if (nChnSlide) { nChnSlide += chn.nGlobalVol; nChnSlide = Clamp(nChnSlide, 0, 64); chn.nGlobalVol = nChnSlide; } } void CSoundFile::ExtendedMODCommands(CHANNELINDEX nChn, ModCommand::PARAM param) { ModChannel &chn = m_PlayState.Chn[nChn]; uint8 command = param & 0xF0; param &= 0x0F; switch(command) { // E0x: Set Filter case 0x00: for(CHANNELINDEX channel = 0; channel < GetNumChannels(); channel++) { m_PlayState.Chn[channel].dwFlags.set(CHN_AMIGAFILTER, !(param & 1)); } break; // E1x: Fine Portamento Up case 0x10: if ((param) || (GetType() & (MOD_TYPE_XM|MOD_TYPE_MT2))) FinePortamentoUp(chn, param); break; // E2x: Fine Portamento Down case 0x20: if ((param) || (GetType() & (MOD_TYPE_XM|MOD_TYPE_MT2))) FinePortamentoDown(chn, param); break; // E3x: Set Glissando Control case 0x30: chn.dwFlags.set(CHN_GLISSANDO, param != 0); break; // E4x: Set Vibrato WaveForm case 0x40: chn.nVibratoType = param & 0x07; break; // E5x: Set FineTune case 0x50: if(!m_SongFlags[SONG_FIRSTTICK]) break; if(GetType() & (MOD_TYPE_MOD | MOD_TYPE_DIGI | MOD_TYPE_AMF0 | MOD_TYPE_MED)) { chn.nFineTune = MOD2XMFineTune(param); if(chn.nPeriod && chn.rowCommand.IsNote()) chn.nPeriod = GetPeriodFromNote(chn.nNote, chn.nFineTune, chn.nC5Speed); } else if(GetType() == MOD_TYPE_MTM) { if(chn.rowCommand.IsNote() && chn.pModSample != nullptr) { // Effect is permanent in MultiTracker const_cast(chn.pModSample)->nFineTune = param; chn.nFineTune = param; if(chn.nPeriod) chn.nPeriod = GetPeriodFromNote(chn.nNote, chn.nFineTune, chn.nC5Speed); } } else if(chn.rowCommand.IsNote()) { chn.nFineTune = MOD2XMFineTune(param - 8); if(chn.nPeriod) chn.nPeriod = GetPeriodFromNote(chn.nNote, chn.nFineTune, chn.nC5Speed); } break; // E6x: Pattern Loop case 0x60: if(m_SongFlags[SONG_FIRSTTICK]) PatternLoop(m_PlayState, chn, param & 0x0F); break; // E7x: Set Tremolo WaveForm case 0x70: chn.nTremoloType = param & 0x07; break; // E8x: Set 4-bit Panning case 0x80: if(m_SongFlags[SONG_FIRSTTICK]) { Panning(chn, param, Pan4bit); } break; // E9x: Retrig case 0x90: RetrigNote(nChn, param); break; // EAx: Fine Volume Up case 0xA0: if ((param) || (GetType() & (MOD_TYPE_XM|MOD_TYPE_MT2))) FineVolumeUp(chn, param, false); break; // EBx: Fine Volume Down case 0xB0: if ((param) || (GetType() & (MOD_TYPE_XM|MOD_TYPE_MT2))) FineVolumeDown(chn, param, false); break; // ECx: Note Cut case 0xC0: NoteCut(nChn, param, false); break; // EDx: Note Delay // EEx: Pattern Delay case 0xF0: if(GetType() == MOD_TYPE_MOD) // MOD: Invert Loop { chn.nEFxSpeed = param; if(m_SongFlags[SONG_FIRSTTICK]) InvertLoop(chn); } else // XM: Set Active Midi Macro { chn.nActiveMacro = param; } break; } } void CSoundFile::ExtendedS3MCommands(CHANNELINDEX nChn, ModCommand::PARAM param) { ModChannel &chn = m_PlayState.Chn[nChn]; uint8 command = param & 0xF0; param &= 0x0F; switch(command) { // S0x: Set Filter // S1x: Set Glissando Control case 0x10: chn.dwFlags.set(CHN_GLISSANDO, param != 0); break; // S2x: Set FineTune case 0x20: if(!m_SongFlags[SONG_FIRSTTICK]) break; if(chn.HasCustomTuning()) { chn.nFineTune = param - 8; chn.m_CalculateFreq = true; } else if(GetType() != MOD_TYPE_669) { chn.nC5Speed = S3MFineTuneTable[param]; chn.nFineTune = MOD2XMFineTune(param); if(chn.nPeriod) chn.nPeriod = GetPeriodFromNote(chn.nNote, chn.nFineTune, chn.nC5Speed); } else if(chn.pModSample != nullptr) { chn.nC5Speed = chn.pModSample->nC5Speed + param * 80; } break; // S3x: Set Vibrato Waveform case 0x30: if(GetType() == MOD_TYPE_S3M) { chn.nVibratoType = param & 0x03; } else { // IT compatibility: Ignore waveform types > 3 if(m_playBehaviour[kITVibratoTremoloPanbrello]) chn.nVibratoType = (param < 0x04) ? param : 0; else chn.nVibratoType = param & 0x07; } break; // S4x: Set Tremolo Waveform case 0x40: if(GetType() == MOD_TYPE_S3M) { chn.nTremoloType = param & 0x03; } else { // IT compatibility: Ignore waveform types > 3 if(m_playBehaviour[kITVibratoTremoloPanbrello]) chn.nTremoloType = (param < 0x04) ? param : 0; else chn.nTremoloType = param & 0x07; } break; // S5x: Set Panbrello Waveform case 0x50: // IT compatibility: Ignore waveform types > 3 if(m_playBehaviour[kITVibratoTremoloPanbrello]) { chn.nPanbrelloType = (param < 0x04) ? param : 0; chn.nPanbrelloPos = 0; } else { chn.nPanbrelloType = param & 0x07; } break; // S6x: Pattern Delay for x frames case 0x60: if(m_SongFlags[SONG_FIRSTTICK] && m_PlayState.m_nTickCount == 0) { // Tick delays are added up. // Scream Tracker 3 does actually not support this command. // We'll use the same behaviour as for Impulse Tracker, as we can assume that // most S3Ms that make use of this command were made with Impulse Tracker. // MPT added this command to the XM format through the X6x effect, so we will use // the same behaviour here as well. // Test cases: PatternDelays.it, PatternDelays.s3m, PatternDelays.xm m_PlayState.m_nFrameDelay += param; } break; // S7x: Envelope Control / Instrument Control case 0x70: if(!m_SongFlags[SONG_FIRSTTICK]) break; switch(param) { case 0: case 1: case 2: { for (CHANNELINDEX i = m_nChannels; i < MAX_CHANNELS; i++) { ModChannel &bkChn = m_PlayState.Chn[i]; if (bkChn.nMasterChn == nChn + 1) { if (param == 1) { KeyOff(bkChn); if(bkChn.dwFlags[CHN_ADLIB] && m_opl) m_opl->NoteOff(i); } else if (param == 2) { bkChn.dwFlags.set(CHN_NOTEFADE); if(bkChn.dwFlags[CHN_ADLIB] && m_opl) m_opl->NoteOff(i); } else { bkChn.dwFlags.set(CHN_NOTEFADE); bkChn.nFadeOutVol = 0; if(bkChn.dwFlags[CHN_ADLIB] && m_opl) m_opl->NoteCut(i); } #ifndef NO_PLUGINS const ModInstrument *pIns = bkChn.pModInstrument; IMixPlugin *pPlugin; if(pIns != nullptr && pIns->nMixPlug && (pPlugin = m_MixPlugins[pIns->nMixPlug - 1].pMixPlugin) != nullptr) { pPlugin->MidiCommand(*pIns, bkChn.nNote + NOTE_MAX_SPECIAL, 0, nChn); } #endif // NO_PLUGINS } } } break; default: // S73-S7E chn.InstrumentControl(param, *this); break; } break; // S8x: Set 4-bit Panning case 0x80: if(m_SongFlags[SONG_FIRSTTICK]) { Panning(chn, param, Pan4bit); } break; // S9x: Sound Control case 0x90: ExtendedChannelEffect(chn, param); break; // SAx: Set 64k Offset case 0xA0: if(m_SongFlags[SONG_FIRSTTICK]) { chn.nOldHiOffset = static_cast(param); if (!m_playBehaviour[kITHighOffsetNoRetrig] && chn.rowCommand.IsNote()) { SmpLength pos = param << 16; if (pos < chn.nLength) chn.position.SetInt(pos); } } break; // SBx: Pattern Loop case 0xB0: if(m_SongFlags[SONG_FIRSTTICK]) PatternLoop(m_PlayState, chn, param & 0x0F); break; // SCx: Note Cut case 0xC0: if(param == 0) { //IT compatibility 22. SC0 == SC1 if(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) param = 1; // ST3 doesn't cut notes with SC0 else if(GetType() == MOD_TYPE_S3M) return; } // S3M/IT compatibility: Note Cut really cuts notes and does not just mute them (so that following volume commands could restore the sample) // Test case: scx.it NoteCut(nChn, param, m_playBehaviour[kITSCxStopsSample] || GetType() == MOD_TYPE_S3M); break; // SDx: Note Delay // SEx: Pattern Delay for x rows // SFx: S3M: Not used, IT: Set Active Midi Macro case 0xF0: if(GetType() != MOD_TYPE_S3M) { chn.nActiveMacro = static_cast(param); } break; } } void CSoundFile::ExtendedChannelEffect(ModChannel &chn, uint32 param) { // S9x and X9x commands (S3M/XM/IT only) if(!m_SongFlags[SONG_FIRSTTICK]) return; switch(param & 0x0F) { // S90: Surround Off case 0x00: chn.dwFlags.reset(CHN_SURROUND); break; // S91: Surround On case 0x01: chn.dwFlags.set(CHN_SURROUND); chn.nPan = 128; break; //////////////////////////////////////////////////////////// // ModPlug Extensions // S98: Reverb Off case 0x08: chn.dwFlags.reset(CHN_REVERB); chn.dwFlags.set(CHN_NOREVERB); break; // S99: Reverb On case 0x09: chn.dwFlags.reset(CHN_NOREVERB); chn.dwFlags.set(CHN_REVERB); break; // S9A: 2-Channels surround mode case 0x0A: m_SongFlags.reset(SONG_SURROUNDPAN); break; // S9B: 4-Channels surround mode case 0x0B: m_SongFlags.set(SONG_SURROUNDPAN); break; // S9C: IT Filter Mode case 0x0C: m_SongFlags.reset(SONG_MPTFILTERMODE); break; // S9D: MPT Filter Mode case 0x0D: m_SongFlags.set(SONG_MPTFILTERMODE); break; // S9E: Go forward case 0x0E: chn.dwFlags.reset(CHN_PINGPONGFLAG); break; // S9F: Go backward (and set playback position to the end if sample just started) case 0x0F: if(chn.position.IsZero() && chn.nLength && (chn.rowCommand.IsNote() || !chn.dwFlags[CHN_LOOP])) { chn.position.Set(chn.nLength - 1, SamplePosition::fractMax); } chn.dwFlags.set(CHN_PINGPONGFLAG); break; } } void CSoundFile::InvertLoop(ModChannel &chn) { // EFx implementation for MOD files (PT 1.1A and up: Invert Loop) // This effect trashes samples. Thanks to 8bitbubsy for making this work. :) if(GetType() != MOD_TYPE_MOD || chn.nEFxSpeed == 0) return; ModSample *pModSample = const_cast(chn.pModSample); if(pModSample == nullptr || !pModSample->HasSampleData() || !pModSample->uFlags[CHN_LOOP | CHN_SUSTAINLOOP]) return; chn.nEFxDelay += ModEFxTable[chn.nEFxSpeed & 0x0F]; if(chn.nEFxDelay < 128) return; chn.nEFxDelay = 0; const SmpLength loopStart = pModSample->uFlags[CHN_LOOP] ? pModSample->nLoopStart : pModSample->nSustainStart; const SmpLength loopEnd = pModSample->uFlags[CHN_LOOP] ? pModSample->nLoopEnd : pModSample->nSustainEnd; if(++chn.nEFxOffset >= loopEnd - loopStart) chn.nEFxOffset = 0; // TRASH IT!!! (Yes, the sample!) const uint8 bps = pModSample->GetBytesPerSample(); uint8 *begin = mpt::byte_cast(pModSample->sampleb()) + (loopStart + chn.nEFxOffset) * bps; for(auto &sample : mpt::as_span(begin, bps)) { sample = ~sample; } pModSample->PrecomputeLoops(*this, false); } // Process a MIDI Macro. // Parameters: // playState: The playback state to operate on. // nChn: Mod channel to apply macro on // isSmooth: If true, internal macros are interpolated between two rows // macro: MIDI Macro string to process // param: Parameter for parametric macros (Zxx / \xx parameter) // plugin: Plugin to send MIDI message to (if not specified but needed, it is autodetected) void CSoundFile::ProcessMIDIMacro(PlayState &playState, CHANNELINDEX nChn, bool isSmooth, const MIDIMacroConfigData::Macro ¯o, uint8 param, PLUGINDEX plugin) { playState.m_midiMacroScratchSpace.resize(macro.Length() + 1); auto out = mpt::as_span(playState.m_midiMacroScratchSpace); ParseMIDIMacro(playState, nChn, isSmooth, macro, out, param, plugin); // Macro string has been parsed and translated, now send the message(s)... uint32 outSize = static_cast(out.size()); uint32 sendPos = 0; uint8 runningStatus = 0; while(sendPos < out.size()) { uint32 sendLen = 0; if(out[sendPos] == 0xF0) { // SysEx start if((outSize - sendPos >= 4) && (out[sendPos + 1] == 0xF0 || out[sendPos + 1] == 0xF1)) { // Internal macro (normal (F0F0) or extended (F0F1)), 4 bytes long sendLen = 4; } else { // SysEx message, find end of message for(uint32 i = sendPos + 1; i < outSize; i++) { if(out[i] == 0xF7) { // Found end of SysEx message sendLen = i - sendPos + 1; break; } } if(sendLen == 0) { // Didn't find end, so "invent" end of SysEx message out[outSize++] = 0xF7; sendLen = outSize - sendPos; } } } else if(!(out[sendPos] & 0x80)) { // Missing status byte? Try inserting running status if(runningStatus != 0) { sendPos--; out[sendPos] = runningStatus; } else { // No running status to re-use; skip this byte sendPos++; } continue; } else { // Other MIDI messages sendLen = std::min(static_cast(MIDIEvents::GetEventLength(out[sendPos])), outSize - sendPos); } if(sendLen == 0) break; if(out[sendPos] < 0xF0) { runningStatus = out[sendPos]; } const auto midiMsg = out.subspan(sendPos, sendLen); SendMIDIData(playState, nChn, isSmooth, midiMsg, plugin); sendPos += sendLen; } } void CSoundFile::ParseMIDIMacro(PlayState &playState, CHANNELINDEX nChn, bool isSmooth, const mpt::span macro, mpt::span &out, uint8 param, PLUGINDEX plugin) const { ModChannel &chn = playState.Chn[nChn]; const ModInstrument *pIns = chn.pModInstrument; const uint8 lastZxxParam = chn.lastZxxParam; // always interpolate based on original value in case z appears multiple times in macro string uint8 updateZxxParam = 0xFF; // avoid updating lastZxxParam immediately if macro contains both internal and external MIDI message bool firstNibble = true; size_t outPos = 0; // output buffer position, which also equals the number of complete bytes for(size_t pos = 0; pos < macro.size() && outPos < out.size(); pos++) { bool isNibble = false; // did we parse a nibble or a byte value? uint8 data = 0; // data that has just been parsed // Parse next macro byte... See Impulse Tracker's MIDI.TXT for detailed information on each possible character. if(macro[pos] >= '0' && macro[pos] <= '9') { isNibble = true; data = static_cast(macro[pos] - '0'); } else if(macro[pos] >= 'A' && macro[pos] <= 'F') { isNibble = true; data = static_cast(macro[pos] - 'A' + 0x0A); } else if(macro[pos] == 'c') { // MIDI channel isNibble = true; data = 0xFF; #ifndef NO_PLUGINS const PLUGINDEX plug = (plugin != 0) ? plugin : GetBestPlugin(playState, nChn, PrioritiseChannel, EvenIfMuted); if(plug > 0 && plug <= MAX_MIXPLUGINS) { auto midiPlug = dynamic_cast(m_MixPlugins[plug - 1u].pMixPlugin); if(midiPlug) data = midiPlug->GetMidiChannel(playState.Chn[nChn], nChn); } #endif // NO_PLUGINS if(data == 0xFF) { // Fallback if no plugin was found if(pIns) data = pIns->GetMIDIChannel(playState.Chn[nChn], nChn); else data = 0; } } else if(macro[pos] == 'n') { // Last triggered note if(ModCommand::IsNote(chn.nLastNote)) { data = chn.nLastNote - NOTE_MIN; } } else if(macro[pos] == 'v') { // Velocity // This is "almost" how IT does it - apparently, IT seems to lag one row behind on global volume or channel volume changes. const int swing = (m_playBehaviour[kITSwingBehaviour] || m_playBehaviour[kMPTOldSwingBehaviour]) ? chn.nVolSwing : 0; const int vol = Util::muldiv((chn.nVolume + swing) * m_PlayState.m_nGlobalVolume, chn.nGlobalVol * chn.nInsVol, 1 << 20); data = static_cast(Clamp(vol / 2, 1, 127)); //data = (unsigned char)std::min((chn.nVolume * chn.nGlobalVol * m_nGlobalVolume) >> (1 + 6 + 8), 127); } else if(macro[pos] == 'u') { // Calculated volume // Same note as with velocity applies here, but apparently also for instrument / sample volumes? const int vol = Util::muldiv(chn.nCalcVolume * m_PlayState.m_nGlobalVolume, chn.nGlobalVol * chn.nInsVol, 1 << 26); data = static_cast(Clamp(vol / 2, 1, 127)); //data = (unsigned char)std::min((chn.nCalcVolume * chn.nGlobalVol * m_nGlobalVolume) >> (7 + 6 + 8), 127); } else if(macro[pos] == 'x') { // Pan set data = static_cast(std::min(static_cast(chn.nPan / 2), 127)); } else if(macro[pos] == 'y') { // Calculated pan data = static_cast(std::min(static_cast(chn.nRealPan / 2), 127)); } else if(macro[pos] == 'a') { // High byte of bank select if(pIns && pIns->wMidiBank) { data = static_cast(((pIns->wMidiBank - 1) >> 7) & 0x7F); } } else if(macro[pos] == 'b') { // Low byte of bank select if(pIns && pIns->wMidiBank) { data = static_cast((pIns->wMidiBank - 1) & 0x7F); } } else if(macro[pos] == 'o') { // Offset (ignoring high offset) data = static_cast((chn.oldOffset >> 8) & 0xFF); } else if(macro[pos] == 'h') { // Host channel number data = static_cast((nChn >= GetNumChannels() ? (chn.nMasterChn - 1) : nChn) & 0x7F); } else if(macro[pos] == 'm') { // Loop direction (judging from the character, it was supposed to be loop type, though) data = chn.dwFlags[CHN_PINGPONGFLAG] ? 1 : 0; } else if(macro[pos] == 'p') { // Program select if(pIns && pIns->nMidiProgram) { data = static_cast((pIns->nMidiProgram - 1) & 0x7F); } } else if(macro[pos] == 'z') { // Zxx parameter data = param; if(isSmooth && chn.lastZxxParam < 0x80 && (outPos < 3 || out[outPos - 3] != 0xF0 || out[outPos - 2] < 0xF0)) { // Interpolation for external MIDI messages - interpolation for internal messages // is handled separately to allow for more than 7-bit granularity where it's possible data = static_cast(CalculateSmoothParamChange(playState, lastZxxParam, data)); chn.lastZxxParam = data; updateZxxParam = 0x80; } else if(updateZxxParam == 0xFF) { updateZxxParam = data; } } else if(macro[pos] == 's') { // SysEx Checksum (not an original Impulse Tracker macro variable, but added for convenience) auto startPos = outPos; while(startPos > 0 && out[--startPos] != 0xF0); if(outPos - startPos < 5 || out[startPos] != 0xF0) { continue; } for(auto p = startPos + 5u; p != outPos; p++) { data += out[p]; } data = (~data + 1) & 0x7F; } else { // Unrecognized byte (e.g. space char) continue; } // Append parsed data if(isNibble) // parsed a nibble (constant or 'c' variable) { if(firstNibble) { out[outPos] = data; } else { out[outPos] = (out[outPos] << 4) | data; outPos++; } firstNibble = !firstNibble; } else // parsed a byte (variable) { if(!firstNibble) // From MIDI.TXT: '9n' is exactly the same as '09 n' or '9 n' -- so finish current byte first { outPos++; } out[outPos++] = data; firstNibble = true; } } if(!firstNibble) { // Finish current byte outPos++; } if(updateZxxParam < 0x80) chn.lastZxxParam = updateZxxParam; out = out.first(outPos); } // Calculate smooth MIDI macro slide parameter for current tick. float CSoundFile::CalculateSmoothParamChange(const PlayState &playState, float currentValue, float param) { MPT_ASSERT(playState.TicksOnRow() > playState.m_nTickCount); const uint32 ticksLeft = playState.TicksOnRow() - playState.m_nTickCount; if(ticksLeft > 1) { // Slide param const float step = (param - currentValue) / static_cast(ticksLeft); return (currentValue + step); } else { // On last tick, set exact value. return param; } } // Process exactly one MIDI message parsed by ProcessMIDIMacro. Returns bytes sent on success, 0 on (parse) failure. void CSoundFile::SendMIDIData(PlayState &playState, CHANNELINDEX nChn, bool isSmooth, const mpt::span macro, PLUGINDEX plugin) { if(macro.size() < 1) return; // Don't do anything that modifies state outside of the playState itself. const bool localOnly = playState.m_midiMacroEvaluationResults.has_value(); if(macro[0] == 0xFA || macro[0] == 0xFC || macro[0] == 0xFF) { // Start Song, Stop Song, MIDI Reset - both interpreted internally and sent to plugins for(CHANNELINDEX chn = 0; chn < GetNumChannels(); chn++) { playState.Chn[chn].nCutOff = 0x7F; playState.Chn[chn].nResonance = 0x00; } } ModChannel &chn = playState.Chn[nChn]; if(macro.size() == 4 && macro[0] == 0xF0 && (macro[1] == 0xF0 || macro[1] == 0xF1)) { // Internal device. const bool isExtended = (macro[1] == 0xF1); const uint8 macroCode = macro[2]; const uint8 param = macro[3]; if(macroCode == 0x00 && !isExtended && param < 0x80) { // F0.F0.00.xx: Set CutOff if(!isSmooth) chn.nCutOff = param; else chn.nCutOff = mpt::saturate_round(CalculateSmoothParamChange(playState, chn.nCutOff, param)); chn.nRestoreCutoffOnNewNote = 0; int cutoff = SetupChannelFilter(chn, !chn.dwFlags[CHN_FILTER]); if(cutoff >= 0 && chn.dwFlags[CHN_ADLIB] && m_opl && !localOnly) { // Cutoff doubles as modulator intensity for FM instruments m_opl->Volume(nChn, static_cast(cutoff / 4), true); } } else if(macroCode == 0x01 && !isExtended && param < 0x80) { // F0.F0.01.xx: Set Resonance if(!isSmooth) chn.nResonance = param; else chn.nResonance = mpt::saturate_round(CalculateSmoothParamChange(playState, chn.nResonance, param)); chn.nRestoreResonanceOnNewNote = 0; SetupChannelFilter(chn, !chn.dwFlags[CHN_FILTER]); } else if(macroCode == 0x02 && !isExtended) { // F0.F0.02.xx: Set filter mode (high nibble determines filter mode) if(param < 0x20) { chn.nFilterMode = static_cast(param >> 4); SetupChannelFilter(chn, !chn.dwFlags[CHN_FILTER]); } #ifndef NO_PLUGINS } else if(macroCode == 0x03 && !isExtended) { // F0.F0.03.xx: Set plug dry/wet PLUGINDEX plug = (plugin != 0) ? plugin : GetBestPlugin(playState, nChn, PrioritiseChannel, EvenIfMuted); if(plug > 0 && plug <= MAX_MIXPLUGINS && param < 0x80) { plug--; const float newRatio = (127 - param) / 127.0f; if(localOnly) playState.m_midiMacroEvaluationResults->pluginDryWetRatio[plug] = newRatio; else if(!isSmooth) m_MixPlugins[plug].fDryRatio = newRatio; else m_MixPlugins[plug].fDryRatio = CalculateSmoothParamChange(playState, m_MixPlugins[plug].fDryRatio, newRatio); } } else if((macroCode & 0x80) || isExtended) { // F0.F0.{80|n}.xx / F0.F1.n.xx: Set VST effect parameter n to xx PLUGINDEX plug = (plugin != 0) ? plugin : GetBestPlugin(playState, nChn, PrioritiseChannel, EvenIfMuted); if(plug > 0 && plug <= MAX_MIXPLUGINS && param < 0x80) { plug--; IMixPlugin *pPlugin = m_MixPlugins[plug].pMixPlugin; if(pPlugin) { const PlugParamIndex plugParam = isExtended ? (0x80 + macroCode) : (macroCode & 0x7F); const PlugParamValue value = param / 127.0f; if(localOnly) playState.m_midiMacroEvaluationResults->pluginParameter[{plug, plugParam}] = value; else if(!isSmooth) pPlugin->SetParameter(plugParam, value); else pPlugin->SetParameter(plugParam, CalculateSmoothParamChange(playState, pPlugin->GetParameter(plugParam), value)); } } #endif // NO_PLUGINS } } else if(!localOnly) { #ifndef NO_PLUGINS // Not an internal device. Pass on to appropriate plugin. const CHANNELINDEX plugChannel = (nChn < GetNumChannels()) ? nChn + 1 : chn.nMasterChn; if(plugChannel > 0 && plugChannel <= GetNumChannels()) // XXX do we need this? I guess it might be relevant for previewing notes in the pattern... Or when using this mechanism for volume/panning! { PLUGINDEX plug = 0; if(!chn.dwFlags[CHN_NOFX]) { plug = (plugin != 0) ? plugin : GetBestPlugin(playState, nChn, PrioritiseChannel, EvenIfMuted); } if(plug > 0 && plug <= MAX_MIXPLUGINS) { IMixPlugin *pPlugin = m_MixPlugins[plug - 1].pMixPlugin; if (pPlugin != nullptr) { if(macro[0] == 0xF0) { pPlugin->MidiSysexSend(mpt::byte_cast(macro)); } else { size_t len = std::min(static_cast(MIDIEvents::GetEventLength(macro[0])), macro.size()); uint32 curData = 0; memcpy(&curData, macro.data(), len); pPlugin->MidiSend(curData); } } } } #else MPT_UNREFERENCED_PARAMETER(plugin); #endif // NO_PLUGINS } } void CSoundFile::SendMIDINote(CHANNELINDEX chn, uint16 note, uint16 volume) { #ifndef NO_PLUGINS auto &channel = m_PlayState.Chn[chn]; const ModInstrument *pIns = channel.pModInstrument; // instro sends to a midi chan if (pIns && pIns->HasValidMIDIChannel()) { PLUGINDEX plug = pIns->nMixPlug; if(plug > 0 && plug <= MAX_MIXPLUGINS) { IMixPlugin *pPlug = m_MixPlugins[plug - 1].pMixPlugin; if (pPlug != nullptr) { pPlug->MidiCommand(*pIns, note, volume, chn); if(note < NOTE_MIN_SPECIAL) channel.nLeftVU = channel.nRightVU = 0xFF; } } } #endif // NO_PLUGINS } void CSoundFile::ProcessSampleOffset(ModChannel& chn, CHANNELINDEX nChn, const PlayState& playState) const { const ModCommand &m = chn.rowCommand; uint32 extendedRows = 0; SmpLength offset = CalculateXParam(playState.m_nPattern, playState.m_nRow, nChn, &extendedRows), highOffset = 0; if(!extendedRows) { // No X-param (normal behaviour) const bool isPercentageOffset = (m.volcmd == VOLCMD_OFFSET && m.vol == 0); offset <<= 8; if(offset) chn.oldOffset = offset; else if(m.volcmd != VOLCMD_OFFSET) offset = chn.oldOffset; if(!isPercentageOffset) highOffset = static_cast(chn.nOldHiOffset) << 16; } if(m.volcmd == VOLCMD_OFFSET) { if(m.vol == 0) offset = Util::muldivr_unsigned(chn.nLength, offset, 256u << (8u * std::max(uint32(1), extendedRows))); // o00 + Oxx = Percentage Offset else if(m.vol <= std::size(ModSample().cues) && chn.pModSample != nullptr) offset += chn.pModSample->cues[m.vol - 1]; // Offset relative to cue point chn.oldOffset = offset; } SampleOffset(chn, offset + highOffset); } void CSoundFile::SampleOffset(ModChannel &chn, SmpLength param) const { // ST3 compatibility: Instrument-less note recalls previous note's offset // Test case: OxxMemory.s3m if(m_playBehaviour[kST3OffsetWithoutInstrument]) chn.prevNoteOffset = 0; chn.prevNoteOffset += param; if(param >= chn.nLoopEnd && (GetType() & (MOD_TYPE_S3M | MOD_TYPE_MTM)) && chn.dwFlags[CHN_LOOP] && chn.nLoopEnd > 0) { // Offset wrap-around // Note that ST3 only does this in GUS mode. SoundBlaster stops the sample entirely instead. // Test case: OffsetLoopWraparound.s3m param = (param - chn.nLoopStart) % (chn.nLoopEnd - chn.nLoopStart) + chn.nLoopStart; } if(GetType() == MOD_TYPE_MDL && chn.dwFlags[CHN_16BIT]) { // Digitrakker really uses byte offsets, not sample offsets. WTF! param /= 2u; } if(chn.rowCommand.IsNote() || m_playBehaviour[kApplyOffsetWithoutNote]) { // IT compatibility: If this note is not mapped to a sample, ignore it. // Test case: empty_sample_offset.it if(chn.pModInstrument != nullptr && chn.rowCommand.IsNote()) { SAMPLEINDEX smp = chn.pModInstrument->Keyboard[chn.rowCommand.note - NOTE_MIN]; if(smp == 0 || smp > GetNumSamples()) return; } if(m_SongFlags[SONG_PT_MODE]) { // ProTracker compatbility: PT1/2-style funky 9xx offset command // Test case: ptoffset.mod chn.position.Set(chn.prevNoteOffset); chn.prevNoteOffset += param; } else { chn.position.Set(param); } if (chn.position.GetUInt() >= chn.nLength || (chn.dwFlags[CHN_LOOP] && chn.position.GetUInt() >= chn.nLoopEnd)) { // Offset beyond sample size if(m_playBehaviour[kFT2ST3OffsetOutOfRange] || GetType() == MOD_TYPE_MTM) { // FT2 Compatibility: Don't play note if offset is beyond sample length // ST3 Compatibility: Don't play note if offset is beyond sample length (non-looped samples only) // Test cases: 3xx-no-old-samp.xm, OffsetPastSampleEnd.s3m chn.dwFlags.set(CHN_FASTVOLRAMP); chn.nPeriod = 0; } else if(!(GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MOD))) { // IT Compatibility: Offset if(m_playBehaviour[kITOffset]) { if(m_SongFlags[SONG_ITOLDEFFECTS]) chn.position.Set(chn.nLength); // Old FX: Clip to end of sample else chn.position.Set(0); // Reset to beginning of sample } else { chn.position.Set(chn.nLoopStart); if(m_SongFlags[SONG_ITOLDEFFECTS] && chn.nLength > 4) { chn.position.Set(chn.nLength - 2); } } } else if(GetType() == MOD_TYPE_MOD && chn.dwFlags[CHN_LOOP]) { chn.position.Set(chn.nLoopStart); } } } else if ((param < chn.nLength) && (GetType() & (MOD_TYPE_MTM | MOD_TYPE_DMF | MOD_TYPE_MDL | MOD_TYPE_PLM))) { // Some trackers can also call offset effects without notes next to them... chn.position.Set(param); } } void CSoundFile::ReverseSampleOffset(ModChannel &chn, ModCommand::PARAM param) const { if(chn.pModSample != nullptr && chn.pModSample->nLength > 0) { chn.dwFlags.set(CHN_PINGPONGFLAG); chn.dwFlags.reset(CHN_LOOP); chn.nLength = chn.pModSample->nLength; // If there was a loop, extend sample to whole length. chn.position.Set((chn.nLength - 1) - std::min(SmpLength(param) << 8, chn.nLength - SmpLength(1)), 0); } } void CSoundFile::DigiBoosterSampleReverse(ModChannel &chn, ModCommand::PARAM param) const { if(chn.isFirstTick && chn.pModSample != nullptr && chn.pModSample->nLength > 0) { chn.dwFlags.set(CHN_PINGPONGFLAG); chn.nLength = chn.pModSample->nLength; // If there was a loop, extend sample to whole length. chn.position.Set(chn.nLength - 1, 0); chn.dwFlags.set(CHN_LOOP | CHN_PINGPONGLOOP, param > 0); if(param > 0) { chn.nLoopStart = 0; chn.nLoopEnd = chn.nLength; // TODO: When the sample starts playing in forward direction again, the loop should be updated to the normal sample loop. } } } void CSoundFile::HandleDigiSamplePlayDirection(PlayState &state, CHANNELINDEX chn) const { // Digi Booster mixes two channels into one Paula channel, and when a note is triggered on one of them it resets the reverse play flag on the other. if(GetType() == MOD_TYPE_DIGI) { state.Chn[chn].dwFlags.reset(CHN_PINGPONGFLAG); const CHANNELINDEX otherChn = chn ^ 1; if(otherChn < GetNumChannels()) state.Chn[otherChn].dwFlags.reset(CHN_PINGPONGFLAG); } } void CSoundFile::RetrigNote(CHANNELINDEX nChn, int param, int offset) { // Retrig: bit 8 is set if it's the new XM retrig ModChannel &chn = m_PlayState.Chn[nChn]; int retrigSpeed = param & 0x0F; uint8 retrigCount = chn.nRetrigCount; bool doRetrig = false; // IT compatibility 15. Retrigger if(m_playBehaviour[kITRetrigger]) { if(m_PlayState.m_nTickCount == 0 && chn.rowCommand.note) { chn.nRetrigCount = param & 0x0F; } else if(!chn.nRetrigCount || !--chn.nRetrigCount) { chn.nRetrigCount = param & 0x0F; doRetrig = true; } } else if(m_playBehaviour[kFT2Retrigger] && (param & 0x100)) { // Buggy-like-hell FT2 Rxy retrig! // Test case: retrig.xm if(m_SongFlags[SONG_FIRSTTICK]) { // Here are some really stupid things FT2 does on the first tick. // Test case: RetrigTick0.xm if(chn.rowCommand.instr > 0 && chn.rowCommand.IsNoteOrEmpty()) retrigCount = 1; if(chn.rowCommand.volcmd == VOLCMD_VOLUME && chn.rowCommand.vol != 0) { // I guess this condition simply checked if the volume byte was != 0 in FT2. chn.nRetrigCount = retrigCount; return; } } if(retrigCount >= retrigSpeed) { if(!m_SongFlags[SONG_FIRSTTICK] || !chn.rowCommand.IsNote()) { doRetrig = true; retrigCount = 0; } } } else { // old routines if (GetType() & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_MPT)) { if(!retrigSpeed) retrigSpeed = 1; if(retrigCount && !(retrigCount % retrigSpeed)) doRetrig = true; retrigCount++; } else if(GetType() == MOD_TYPE_MOD) { // ProTracker-style retrigger // Test case: PTRetrigger.mod const auto tick = m_PlayState.m_nTickCount % m_PlayState.m_nMusicSpeed; if(!tick && chn.rowCommand.IsNote()) return; if(retrigSpeed && !(tick % retrigSpeed)) doRetrig = true; } else if(GetType() == MOD_TYPE_MTM) { // In MultiTracker, E9x retriggers the last note at exactly the x-th tick of the row doRetrig = m_PlayState.m_nTickCount == static_cast(param & 0x0F) && retrigSpeed != 0; } else { int realspeed = retrigSpeed; // FT2 bug: if a retrig (Rxy) occurs together with a volume command, the first retrig interval is increased by one tick if((param & 0x100) && (chn.rowCommand.volcmd == VOLCMD_VOLUME) && (chn.rowCommand.param & 0xF0)) realspeed++; if(!m_SongFlags[SONG_FIRSTTICK] || (param & 0x100)) { if(!realspeed) realspeed = 1; if(!(param & 0x100) && m_PlayState.m_nMusicSpeed && !(m_PlayState.m_nTickCount % realspeed)) doRetrig = true; retrigCount++; } else if(GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2)) retrigCount = 0; if (retrigCount >= realspeed) { if(m_PlayState.m_nTickCount || ((param & 0x100) && !chn.rowCommand.note)) doRetrig = true; } if(m_playBehaviour[kFT2Retrigger] && param == 0) { // E90 = Retrig instantly, and only once doRetrig = (m_PlayState.m_nTickCount == 0); } } } // IT compatibility: If a sample is shorter than the retrig time (i.e. it stops before the retrig counter hits zero), it is not retriggered. // Test case: retrig-short.it if(chn.nLength == 0 && m_playBehaviour[kITShortSampleRetrig] && !chn.HasMIDIOutput()) return; // ST3 compatibility: No retrig after Note Cut // Test case: RetrigAfterNoteCut.s3m if(m_playBehaviour[kST3RetrigAfterNoteCut] && !chn.nFadeOutVol) return; if(doRetrig) { uint32 dv = (param >> 4) & 0x0F; int vol = chn.nVolume; if(dv) { // FT2 compatibility: Retrig + volume will not change volume of retrigged notes if(!m_playBehaviour[kFT2Retrigger] || !(chn.rowCommand.volcmd == VOLCMD_VOLUME)) { if(retrigTable1[dv]) vol = (vol * retrigTable1[dv]) / 16; else vol += ((int)retrigTable2[dv]) * 4; } Limit(vol, 0, 256); chn.dwFlags.set(CHN_FASTVOLRAMP); } uint32 note = chn.nNewNote; int32 oldPeriod = chn.nPeriod; // ST3 doesn't retrigger OPL notes // Test case: RetrigSlide.s3m const bool oplRealRetrig = chn.dwFlags[CHN_ADLIB] && m_playBehaviour[kOPLRealRetrig]; if(note >= NOTE_MIN && note <= NOTE_MAX && chn.nLength && (GetType() != MOD_TYPE_S3M || oplRealRetrig)) CheckNNA(nChn, 0, note, true); bool resetEnv = false; if(GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2)) { if(chn.rowCommand.instr && param < 0x100) { InstrumentChange(chn, chn.rowCommand.instr, false, false); resetEnv = true; } if(param < 0x100) resetEnv = true; } const bool fading = chn.dwFlags[CHN_NOTEFADE]; const auto oldPrevNoteOffset = chn.prevNoteOffset; chn.prevNoteOffset = 0; // Retriggered notes should not use previous offset (test case: OxxMemoryWithRetrig.s3m) // IT compatibility: Really weird combination of envelopes and retrigger (see Storlek's q.it testcase) // Test cases: retrig.it, RetrigSlide.s3m const bool itS3Mstyle = m_playBehaviour[kITRetrigger] || (GetType() == MOD_TYPE_S3M && chn.nLength && !oplRealRetrig); NoteChange(chn, note, itS3Mstyle, resetEnv, false, nChn); if(!chn.rowCommand.instr) chn.prevNoteOffset = oldPrevNoteOffset; // XM compatibility: Prevent NoteChange from resetting the fade flag in case an instrument number + note-off is present. // Test case: RetrigFade.xm if(fading && GetType() == MOD_TYPE_XM) chn.dwFlags.set(CHN_NOTEFADE); chn.nVolume = vol; if(m_nInstruments) { chn.rowCommand.note = static_cast(note); // No retrig without note... #ifndef NO_PLUGINS ProcessMidiOut(nChn); //Send retrig to Midi #endif // NO_PLUGINS } if((GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) && chn.rowCommand.note == NOTE_NONE && oldPeriod != 0) chn.nPeriod = oldPeriod; if(!(GetType() & (MOD_TYPE_S3M | MOD_TYPE_IT | MOD_TYPE_MPT))) retrigCount = 0; // IT compatibility: see previous IT compatibility comment =) if(itS3Mstyle) chn.position.Set(0); offset--; if(chn.pModSample != nullptr && offset >= 0 && offset <= static_cast(std::size(chn.pModSample->cues))) { if(offset == 0) offset = chn.oldOffset; else offset = chn.oldOffset = chn.pModSample->cues[offset - 1]; SampleOffset(chn, offset); } } // buggy-like-hell FT2 Rxy retrig! if(m_playBehaviour[kFT2Retrigger] && (param & 0x100)) retrigCount++; // Now we can also store the retrig value for IT... if(!m_playBehaviour[kITRetrigger]) chn.nRetrigCount = retrigCount; } // Execute a frequency slide on given channel. // Positive amounts increase the frequency, negative amounts decrease it. // The period or frequency that is read and written is in the period variable, chn.nPeriod is not touched. void CSoundFile::DoFreqSlide(ModChannel &chn, int32 &period, int32 amount, bool isTonePorta) const { if(!period || !amount) return; MPT_ASSERT(!chn.HasCustomTuning()); if(GetType() == MOD_TYPE_669) { // Like other oldskool trackers, Composer 669 doesn't have linear slides... // But the slides are done in Hertz rather than periods, meaning that they // are more effective in the lower notes (rather than the higher notes). period += amount * 20; } else if(GetType() == MOD_TYPE_FAR) { period += (amount * 36318 / 1024); } else if(m_SongFlags[SONG_LINEARSLIDES] && GetType() != MOD_TYPE_XM) { // IT Linear slides const auto oldPeriod = period; uint32 n = std::abs(amount); LimitMax(n, 255u * 4u); // Note: IT ignores the lower 2 bits when abs(mount) > 16 (it either uses the fine *or* the regular table, not both) // This means that vibratos are slightly less accurate in this range than they could be. // Other code paths will *either* have an amount that's a multiple of 4 *or* it's less than 16. if(amount > 0) { if(n < 16) period = Util::muldivr(period, GetFineLinearSlideUpTable(this, n), 65536); else period = Util::muldivr(period, GetLinearSlideUpTable(this, n / 4u), 65536); } else { if(n < 16) period = Util::muldivr(period, GetFineLinearSlideDownTable(this, n), 65536); else period = Util::muldivr(period, GetLinearSlideDownTable(this, n / 4u), 65536); } if(period == oldPeriod) { const bool incPeriod = m_playBehaviour[kPeriodsAreHertz] == (amount > 0); if(incPeriod && period < Util::MaxValueOfType(period)) period++; else if(!incPeriod && period > 1) period--; } } else if(!m_SongFlags[SONG_LINEARSLIDES] && m_playBehaviour[kPeriodsAreHertz]) { // IT Amiga slides if(amount < 0) { // Go down period = mpt::saturate_cast(Util::mul32to64_unsigned(1712 * 8363, period) / (Util::mul32to64_unsigned(period, -amount) + 1712 * 8363)); } else if(amount > 0) { // Go up const auto periodDiv = 1712 * 8363 - Util::mul32to64(period, amount); if(periodDiv <= 0) { if(isTonePorta) { period = int32_max; return; } else { period = 0; chn.nFadeOutVol = 0; chn.dwFlags.set(CHN_NOTEFADE | CHN_FASTVOLRAMP); } return; } period = mpt::saturate_cast(Util::mul32to64_unsigned(1712 * 8363, period) / periodDiv); } } else { period -= amount; } if(period < 1) { period = 1; if(GetType() == MOD_TYPE_S3M && !isTonePorta) { chn.nFadeOutVol = 0; chn.dwFlags.set(CHN_NOTEFADE | CHN_FASTVOLRAMP); } } } void CSoundFile::NoteCut(CHANNELINDEX nChn, uint32 nTick, bool cutSample) { if (m_PlayState.m_nTickCount == nTick) { ModChannel &chn = m_PlayState.Chn[nChn]; if(cutSample) { chn.increment.Set(0); chn.nFadeOutVol = 0; chn.dwFlags.set(CHN_NOTEFADE); } else { chn.nVolume = 0; } chn.dwFlags.set(CHN_FASTVOLRAMP); // instro sends to a midi chan SendMIDINote(nChn, /*chn.nNote+*/NOTE_MAX_SPECIAL, 0); if(chn.dwFlags[CHN_ADLIB] && m_opl) { m_opl->NoteCut(nChn, false); } } } void CSoundFile::KeyOff(ModChannel &chn) const { const bool keyIsOn = !chn.dwFlags[CHN_KEYOFF]; chn.dwFlags.set(CHN_KEYOFF); if(chn.pModInstrument != nullptr && !chn.VolEnv.flags[ENV_ENABLED]) { chn.dwFlags.set(CHN_NOTEFADE); } if (!chn.nLength) return; if (chn.dwFlags[CHN_SUSTAINLOOP] && chn.pModSample && keyIsOn) { const ModSample *pSmp = chn.pModSample; if(pSmp->uFlags[CHN_LOOP]) { if (pSmp->uFlags[CHN_PINGPONGLOOP]) chn.dwFlags.set(CHN_PINGPONGLOOP); else chn.dwFlags.reset(CHN_PINGPONGLOOP | CHN_PINGPONGFLAG); chn.dwFlags.set(CHN_LOOP); chn.nLength = pSmp->nLength; chn.nLoopStart = pSmp->nLoopStart; chn.nLoopEnd = pSmp->nLoopEnd; if (chn.nLength > chn.nLoopEnd) chn.nLength = chn.nLoopEnd; if(chn.position.GetUInt() > chn.nLength) { // Test case: SusAfterLoop.it chn.position.Set(chn.nLoopStart + ((chn.position.GetInt() - chn.nLoopStart) % (chn.nLoopEnd - chn.nLoopStart))); } } else { chn.dwFlags.reset(CHN_LOOP | CHN_PINGPONGLOOP | CHN_PINGPONGFLAG); chn.nLength = pSmp->nLength; } } if (chn.pModInstrument) { const ModInstrument *pIns = chn.pModInstrument; if((pIns->VolEnv.dwFlags[ENV_LOOP] || (GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MDL))) && pIns->nFadeOut != 0) { chn.dwFlags.set(CHN_NOTEFADE); } if (pIns->VolEnv.nReleaseNode != ENV_RELEASE_NODE_UNSET && chn.VolEnv.nEnvValueAtReleaseJump == NOT_YET_RELEASED) { chn.VolEnv.nEnvValueAtReleaseJump = mpt::saturate_cast(pIns->VolEnv.GetValueFromPosition(chn.VolEnv.nEnvPosition, 256)); chn.VolEnv.nEnvPosition = pIns->VolEnv[pIns->VolEnv.nReleaseNode].tick; } } } ////////////////////////////////////////////////////////// // CSoundFile: Global Effects void CSoundFile::SetSpeed(PlayState &playState, uint32 param) const { #ifdef MODPLUG_TRACKER // FT2 appears to be decrementing the tick count before checking for zero, // so it effectively counts down 65536 ticks with speed = 0 (song speed is a 16-bit variable in FT2) if(GetType() == MOD_TYPE_XM && !param) { playState.m_nMusicSpeed = uint16_max; } #endif // MODPLUG_TRACKER if(param > 0) playState.m_nMusicSpeed = param; if(GetType() == MOD_TYPE_STM && param > 0) { playState.m_nMusicSpeed = std::max(param >> 4, uint32(1)); playState.m_nMusicTempo = ConvertST2Tempo(static_cast(param)); } } // Convert a ST2 tempo byte to classic tempo and speed combination TEMPO CSoundFile::ConvertST2Tempo(uint8 tempo) { static constexpr uint8 ST2TempoFactor[] = { 140, 50, 25, 15, 10, 7, 6, 4, 3, 3, 2, 2, 2, 2, 1, 1 }; static constexpr uint32 st2MixingRate = 23863; // Highest possible setting in ST2 // This underflows at tempo 06...0F, and the resulting tick lengths depend on the mixing rate. // Note: ST2.3 uses the constant 50 below, earlier versions use 49 but they also play samples at a different speed. int32 samplesPerTick = st2MixingRate / (50 - ((ST2TempoFactor[tempo >> 4u] * (tempo & 0x0F)) >> 4u)); if(samplesPerTick <= 0) samplesPerTick += 65536; return TEMPO().SetRaw(Util::muldivrfloor(st2MixingRate, 5 * TEMPO::fractFact, samplesPerTick * 2)); } void CSoundFile::SetTempo(TEMPO param, bool setFromUI) { const CModSpecifications &specs = GetModSpecifications(); // Anything lower than the minimum tempo is considered to be a tempo slide const TEMPO minTempo = (GetType() & (MOD_TYPE_MDL | MOD_TYPE_MED | MOD_TYPE_MOD)) ? TEMPO(1, 0) : TEMPO(32, 0); if(setFromUI) { // Set tempo from UI - ignore slide commands and such. m_PlayState.m_nMusicTempo = Clamp(param, specs.GetTempoMin(), specs.GetTempoMax()); } else if(param >= minTempo && m_SongFlags[SONG_FIRSTTICK] == !m_playBehaviour[kMODTempoOnSecondTick]) { // ProTracker sets the tempo after the first tick. // Note: The case of one tick per row is handled in ProcessRow() instead. // Test case: TempoChange.mod m_PlayState.m_nMusicTempo = std::min(param, specs.GetTempoMax()); } else if(param < minTempo && !m_SongFlags[SONG_FIRSTTICK]) { // Tempo Slide TEMPO tempDiff(param.GetInt() & 0x0F, 0); if((param.GetInt() & 0xF0) == 0x10) m_PlayState.m_nMusicTempo += tempDiff; else m_PlayState.m_nMusicTempo -= tempDiff; TEMPO tempoMin = specs.GetTempoMin(), tempoMax = specs.GetTempoMax(); if(m_playBehaviour[kTempoClamp]) // clamp tempo correctly in compatible mode { tempoMax.Set(255); } Limit(m_PlayState.m_nMusicTempo, tempoMin, tempoMax); } } void CSoundFile::PatternLoop(PlayState &state, ModChannel &chn, ModCommand::PARAM param) const { if(m_playBehaviour[kST3NoMutedChannels] && chn.dwFlags[CHN_MUTE | CHN_SYNCMUTE]) return; // not even effects are processed on muted S3M channels if(!param) { // Loop Start chn.nPatternLoop = state.m_nRow; return; } // Loop Repeat if(chn.nPatternLoopCount) { // There's a loop left chn.nPatternLoopCount--; if(!chn.nPatternLoopCount) { // IT compatibility 10. Pattern loops (+ same fix for S3M files) // When finishing a pattern loop, the next loop without a dedicated SB0 starts on the first row after the previous loop. if(m_playBehaviour[kITPatternLoopTargetReset] || (GetType() == MOD_TYPE_S3M)) chn.nPatternLoop = state.m_nRow + 1; return; } } else { // First time we get into the loop => Set loop count. // IT compatibility 10. Pattern loops (+ same fix for XM / MOD / S3M files) if(!m_playBehaviour[kITFT2PatternLoop] && !(GetType() & (MOD_TYPE_MOD | MOD_TYPE_S3M))) { auto p = std::cbegin(state.Chn); for(CHANNELINDEX i = 0; i < GetNumChannels(); i++, p++) { // Loop on other channel if(p != &chn && p->nPatternLoopCount) return; } } chn.nPatternLoopCount = param; } state.m_nextPatStartRow = chn.nPatternLoop; // Nasty FT2 E60 bug emulation! const auto loopTarget = chn.nPatternLoop; if(loopTarget != ROWINDEX_INVALID) { // FT2 compatibility: E6x overwrites jump targets of Dxx effects that are located left of the E6x effect. // Test cases: PatLoop-Jumps.xm, PatLoop-Various.xm if(state.m_breakRow != ROWINDEX_INVALID && m_playBehaviour[kFT2PatternLoopWithJumps]) state.m_breakRow = loopTarget; state.m_patLoopRow = loopTarget; // IT compatibility: SBx is prioritized over Position Jump (Bxx) effects that are located left of the SBx effect. // Test case: sbx-priority.it, LoopBreak.it if(m_playBehaviour[kITPatternLoopWithJumps]) state.m_posJump = ORDERINDEX_INVALID; } if(GetType() == MOD_TYPE_S3M) { // ST3 doesn't have per-channel pattern loop memory, so spam all changes to other channels as well. for(CHANNELINDEX i = 0; i < GetNumChannels(); i++) { state.Chn[i].nPatternLoop = chn.nPatternLoop; state.Chn[i].nPatternLoopCount = chn.nPatternLoopCount; } } } void CSoundFile::GlobalVolSlide(ModCommand::PARAM param, uint8 &nOldGlobalVolSlide) { int32 nGlbSlide = 0; if (param) nOldGlobalVolSlide = param; else param = nOldGlobalVolSlide; if((GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2))) { // XM nibble priority if((param & 0xF0) != 0) { param &= 0xF0; } else { param &= 0x0F; } } if (((param & 0x0F) == 0x0F) && (param & 0xF0)) { if(m_SongFlags[SONG_FIRSTTICK]) nGlbSlide = (param >> 4) * 2; } else if (((param & 0xF0) == 0xF0) && (param & 0x0F)) { if(m_SongFlags[SONG_FIRSTTICK]) nGlbSlide = - (int)((param & 0x0F) * 2); } else { if(!m_SongFlags[SONG_FIRSTTICK]) { if (param & 0xF0) { // IT compatibility: Ignore slide commands with both nibbles set. if(!(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_IMF | MOD_TYPE_J2B | MOD_TYPE_MID | MOD_TYPE_AMS | MOD_TYPE_DBM)) || (param & 0x0F) == 0) nGlbSlide = (int)((param & 0xF0) >> 4) * 2; } else { nGlbSlide = -(int)((param & 0x0F) * 2); } } } if (nGlbSlide) { if(!(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_IMF | MOD_TYPE_J2B | MOD_TYPE_MID | MOD_TYPE_AMS | MOD_TYPE_DBM))) nGlbSlide *= 2; nGlbSlide += m_PlayState.m_nGlobalVolume; Limit(nGlbSlide, 0, 256); m_PlayState.m_nGlobalVolume = nGlbSlide; } } ////////////////////////////////////////////////////// // Note/Period/Frequency functions // Find lowest note which has same or lower period as a given period (i.e. the note has the same or higher frequency) uint32 CSoundFile::GetNoteFromPeriod(uint32 period, int32 nFineTune, uint32 nC5Speed) const { if(!period) return 0; if(m_playBehaviour[kFT2Periods]) { // FT2's "RelocateTon" function actually rounds up and down, while GetNoteFromPeriod normally just truncates. nFineTune += 64; } // This essentially implements std::lower_bound, with the difference that we don't need an iterable container. uint32 minNote = NOTE_MIN, maxNote = NOTE_MAX, count = maxNote - minNote + 1; const bool periodIsFreq = PeriodsAreFrequencies(); while(count > 0) { const uint32 step = count / 2, midNote = minNote + step; uint32 n = GetPeriodFromNote(midNote, nFineTune, nC5Speed); if((n > period && !periodIsFreq) || (n < period && periodIsFreq) || !n) { minNote = midNote + 1; count -= step + 1; } else { count = step; } } return minNote; } uint32 CSoundFile::GetPeriodFromNote(uint32 note, int32 nFineTune, uint32 nC5Speed) const { if (note == NOTE_NONE || (note >= NOTE_MIN_SPECIAL)) return 0; note -= NOTE_MIN; if(!UseFinetuneAndTranspose()) { if(GetType() & (MOD_TYPE_MDL | MOD_TYPE_DTM)) { // MDL uses non-linear slides, but their effectiveness does not depend on the middle-C frequency. return (FreqS3MTable[note % 12u] << 4) >> (note / 12); } if(!nC5Speed) nC5Speed = 8363; if(PeriodsAreFrequencies()) { // Compute everything in Hertz rather than periods. uint32 freq = Util::muldiv_unsigned(nC5Speed, LinearSlideUpTable[(note % 12u) * 16u] << (note / 12u), 65536 << 5); LimitMax(freq, static_cast(int32_max)); return freq; } else if(m_SongFlags[SONG_LINEARSLIDES]) { return (FreqS3MTable[note % 12u] << 5) >> (note / 12); } else { LimitMax(nC5Speed, uint32_max >> (note / 12u)); //(a*b)/c return Util::muldiv_unsigned(8363, (FreqS3MTable[note % 12u] << 5), nC5Speed << (note / 12u)); //8363 * freq[note%12] / nC5Speed * 2^(5-note/12) } } else if(GetType() & (MOD_TYPE_XM | MOD_TYPE_MTM)) { if (note < 12) note = 12; note -= 12; if(GetType() == MOD_TYPE_MTM) { nFineTune *= 16; } else if(m_playBehaviour[kFT2FinetunePrecision]) { // FT2 Compatibility: The lower three bits of the finetune are truncated. // Test case: Finetune-Precision.xm nFineTune &= ~7; } if(m_SongFlags[SONG_LINEARSLIDES]) { int l = ((NOTE_MAX - note) << 6) - (nFineTune / 2); if (l < 1) l = 1; return static_cast(l); } else { int finetune = nFineTune; uint32 rnote = (note % 12) << 3; uint32 roct = note / 12; int rfine = finetune / 16; int i = rnote + rfine + 8; Limit(i , 0, 103); uint32 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; uint32 per2 = XMPeriodTable[i]; rfine = finetune & 0x0F; per1 *= 16-rfine; per2 *= rfine; return ((per1 + per2) << 1) >> roct; } } else { nFineTune = XM2MODFineTune(nFineTune); if ((nFineTune) || (note < 24) || (note >= 24 + std::size(ProTrackerPeriodTable))) return (ProTrackerTunedPeriods[nFineTune * 12u + note % 12u] << 5) >> (note / 12u); else return (ProTrackerPeriodTable[note - 24] << 2); } } // Converts period value to sample frequency. Return value is fixed point, with FREQ_FRACBITS fractional bits. uint32 CSoundFile::GetFreqFromPeriod(uint32 period, uint32 c5speed, int32 nPeriodFrac) const { if (!period) return 0; if (GetType() & (MOD_TYPE_XM | MOD_TYPE_MTM)) { if(m_playBehaviour[kFT2Periods]) { // FT2 compatibility: Period is a 16-bit value in FT2, and it overflows happily. // Test case: FreqWraparound.xm period &= 0xFFFF; } if(m_SongFlags[SONG_LINEARSLIDES]) { uint32 octave; if(m_playBehaviour[kFT2Periods]) { // Under normal circumstances, this calculation returns the same values as the non-compatible one. // However, once the 12 octaves are exceeded (through portamento slides), the octave shift goes // crazy in FT2, meaning that the frequency wraps around randomly... // The entries in FT2's conversion table are four times as big, hence we have to do an additional shift by two bits. // Test case: FreqWraparound.xm // 12 octaves * (12 * 64) LUT entries = 9216, add 767 for rounding uint32 div = ((9216u + 767u - period) / 768); octave = ((14 - div) & 0x1F); } else { octave = (period / 768) + 2; } return (XMLinearTable[period % 768] << (FREQ_FRACBITS + 2)) >> octave; } else { if(!period) period = 1; return ((8363 * 1712L) << FREQ_FRACBITS) / period; } } else if(UseFinetuneAndTranspose()) { return ((3546895L * 4) << FREQ_FRACBITS) / period; } else if(GetType() == MOD_TYPE_669) { // We only really use c5speed for the finetune pattern command. All samples in 669 files have the same middle-C speed (imported as 8363 Hz). return (period + c5speed - 8363) << FREQ_FRACBITS; } else if(GetType() & (MOD_TYPE_MDL | MOD_TYPE_DTM)) { LimitMax(period, Util::MaxValueOfType(period) >> 8); if (!c5speed) c5speed = 8363; return Util::muldiv_unsigned(c5speed, (1712L << 7) << FREQ_FRACBITS, (period << 8) + nPeriodFrac); } else { LimitMax(period, Util::MaxValueOfType(period) >> 8); if(PeriodsAreFrequencies()) { // Input is already a frequency in Hertz, not a period. static_assert(FREQ_FRACBITS <= 8, "Check this shift operator"); return uint32(((uint64(period) << 8) + nPeriodFrac) >> (8 - FREQ_FRACBITS)); } else if(m_SongFlags[SONG_LINEARSLIDES]) { if(!c5speed) c5speed = 8363; return Util::muldiv_unsigned(c5speed, (1712L << 8) << FREQ_FRACBITS, (period << 8) + nPeriodFrac); } else { return Util::muldiv_unsigned(8363, (1712L << 8) << FREQ_FRACBITS, (period << 8) + nPeriodFrac); } } } PLUGINDEX CSoundFile::GetBestPlugin(const PlayState &playState, CHANNELINDEX nChn, PluginPriority priority, PluginMutePriority respectMutes) const { if (nChn >= MAX_CHANNELS) //Check valid channel number { return 0; } //Define search source order PLUGINDEX plugin = 0; switch (priority) { case ChannelOnly: plugin = GetChannelPlugin(playState, nChn, respectMutes); break; case InstrumentOnly: plugin = GetActiveInstrumentPlugin(playState.Chn[nChn], respectMutes); break; case PrioritiseInstrument: plugin = GetActiveInstrumentPlugin(playState.Chn[nChn], respectMutes); if(!plugin || plugin > MAX_MIXPLUGINS) { plugin = GetChannelPlugin(playState, nChn, respectMutes); } break; case PrioritiseChannel: plugin = GetChannelPlugin(playState, nChn, respectMutes); if(!plugin || plugin > MAX_MIXPLUGINS) { plugin = GetActiveInstrumentPlugin(playState.Chn[nChn], respectMutes); } break; } return plugin; // 0 Means no plugin found. } PLUGINDEX CSoundFile::GetChannelPlugin(const PlayState &playState, CHANNELINDEX nChn, PluginMutePriority respectMutes) const { const ModChannel &channel = playState.Chn[nChn]; PLUGINDEX plugin; if((respectMutes == RespectMutes && channel.dwFlags[CHN_MUTE | CHN_SYNCMUTE]) || channel.dwFlags[CHN_NOFX]) { plugin = 0; } else { // If it looks like this is an NNA channel, we need to find the master channel. // This ensures we pick up the right ChnSettings. if(channel.nMasterChn > 0) { nChn = channel.nMasterChn - 1; } if(nChn < MAX_BASECHANNELS) { plugin = ChnSettings[nChn].nMixPlugin; } else { plugin = 0; } } return plugin; } PLUGINDEX CSoundFile::GetActiveInstrumentPlugin(const ModChannel &chn, PluginMutePriority respectMutes) { // Unlike channel settings, pModInstrument is copied from the original chan to the NNA chan, // so we don't need to worry about finding the master chan. PLUGINDEX plug = 0; if(chn.pModInstrument != nullptr) { // TODO this looks fishy. Shouldn't it check the mute status of the instrument itself?! if(respectMutes == RespectMutes && chn.pModSample && chn.pModSample->uFlags[CHN_MUTE]) { plug = 0; } else { plug = chn.pModInstrument->nMixPlug; } } return plug; } // Retrieve the plugin that is associated with the channel's current instrument. // No plugin is returned if the channel is muted or if the instrument doesn't have a MIDI channel set up, // As this is meant to be used with instrument plugins. IMixPlugin *CSoundFile::GetChannelInstrumentPlugin(const ModChannel &chn) const { #ifndef NO_PLUGINS if(chn.dwFlags[CHN_MUTE | CHN_SYNCMUTE]) { // Don't process portamento on muted channels. Note that this might have a side-effect // on other channels which trigger notes on the same MIDI channel of the same plugin, // as those won't be pitch-bent anymore. return nullptr; } if(chn.HasMIDIOutput()) { const ModInstrument *pIns = chn.pModInstrument; // Instrument sends to a MIDI channel if(pIns->nMixPlug != 0 && pIns->nMixPlug <= MAX_MIXPLUGINS) { return m_MixPlugins[pIns->nMixPlug - 1].pMixPlugin; } } #else MPT_UNREFERENCED_PARAMETER(chn); #endif // NO_PLUGINS return nullptr; } #ifdef MODPLUG_TRACKER void CSoundFile::HandlePatternTransitionEvents() { // MPT sequence override if(m_PlayState.m_nSeqOverride != ORDERINDEX_INVALID && m_PlayState.m_nSeqOverride < Order().size()) { if(m_SongFlags[SONG_PATTERNLOOP]) { m_PlayState.m_nPattern = Order()[m_PlayState.m_nSeqOverride]; } m_PlayState.m_nCurrentOrder = m_PlayState.m_nSeqOverride; m_PlayState.m_nSeqOverride = ORDERINDEX_INVALID; } // Channel mutes for (CHANNELINDEX chan = 0; chan < GetNumChannels(); chan++) { if (m_bChannelMuteTogglePending[chan]) { if(GetpModDoc()) { GetpModDoc()->MuteChannel(chan, !GetpModDoc()->IsChannelMuted(chan)); } m_bChannelMuteTogglePending[chan] = false; } } } #endif // MODPLUG_TRACKER // Update time signatures (global or pattern-specific). Don't forget to call this when changing the RPB/RPM settings anywhere! void CSoundFile::UpdateTimeSignature() { if(!Patterns.IsValidIndex(m_PlayState.m_nPattern) || !Patterns[m_PlayState.m_nPattern].GetOverrideSignature()) { m_PlayState.m_nCurrentRowsPerBeat = m_nDefaultRowsPerBeat; m_PlayState.m_nCurrentRowsPerMeasure = m_nDefaultRowsPerMeasure; } else { m_PlayState.m_nCurrentRowsPerBeat = Patterns[m_PlayState.m_nPattern].GetRowsPerBeat(); m_PlayState.m_nCurrentRowsPerMeasure = Patterns[m_PlayState.m_nPattern].GetRowsPerMeasure(); } } void CSoundFile::PortamentoMPT(ModChannel &chn, int param) { //Behavior: Modifies portamento by param-steps on every tick. //Note that step meaning depends on tuning. chn.m_PortamentoFineSteps += param; chn.m_CalculateFreq = true; } void CSoundFile::PortamentoFineMPT(ModChannel &chn, int param) { //Behavior: Divides portamento change between ticks/row. For example //if Ticks/row == 6, and param == +-6, portamento goes up/down by one tuning-dependent //fine step every tick. if(m_PlayState.m_nTickCount == 0) chn.nOldFinePortaUpDown = 0; const int tickParam = static_cast((m_PlayState.m_nTickCount + 1.0) * param / m_PlayState.m_nMusicSpeed); chn.m_PortamentoFineSteps += (param >= 0) ? tickParam - chn.nOldFinePortaUpDown : tickParam + chn.nOldFinePortaUpDown; if(m_PlayState.m_nTickCount + 1 == m_PlayState.m_nMusicSpeed) chn.nOldFinePortaUpDown = static_cast(std::abs(param)); else chn.nOldFinePortaUpDown = static_cast(std::abs(tickParam)); chn.m_CalculateFreq = true; } void CSoundFile::PortamentoExtraFineMPT(ModChannel &chn, int param) { // This kinda behaves like regular fine portamento. // It changes the pitch by n finetune steps on the first tick. if(chn.isFirstTick) { chn.m_PortamentoFineSteps += param; chn.m_CalculateFreq = true; } } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Sndmix.cpp0000644000175000017500000026312014175042045020047 00000000000000/* * Sndmix.cpp * ----------- * Purpose: Pattern playback, effect processing * Notes : (currently none) * Authors: Olivier Lapicque * OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Sndfile.h" #include "MixerLoops.h" #include "MIDIEvents.h" #include "Tables.h" #ifdef MODPLUG_TRACKER #include "../mptrack/TrackerSettings.h" #endif // MODPLUG_TRACKER #ifndef NO_PLUGINS #include "plugins/PlugInterface.h" #endif // NO_PLUGINS #include "OPL.h" OPENMPT_NAMESPACE_BEGIN // Log tables for pre-amp // Pre-amp (or more precisely: Pre-attenuation) depends on the number of channels, // Which this table takes care of. static constexpr uint8 PreAmpTable[16] = { 0x60, 0x60, 0x60, 0x70, // 0-7 0x80, 0x88, 0x90, 0x98, // 8-15 0xA0, 0xA4, 0xA8, 0xAC, // 16-23 0xB0, 0xB4, 0xB8, 0xBC, // 24-31 }; #ifndef NO_AGC static constexpr uint8 PreAmpAGCTable[16] = { 0x60, 0x60, 0x60, 0x64, 0x68, 0x70, 0x78, 0x80, 0x84, 0x88, 0x8C, 0x90, 0x92, 0x94, 0x96, 0x98, }; #endif void CSoundFile::SetMixerSettings(const MixerSettings &mixersettings) { SetPreAmp(mixersettings.m_nPreAmp); // adjust agc bool reset = false; if( (mixersettings.gdwMixingFreq != m_MixerSettings.gdwMixingFreq) || (mixersettings.gnChannels != m_MixerSettings.gnChannels) || (mixersettings.MixerFlags != m_MixerSettings.MixerFlags)) reset = true; m_MixerSettings = mixersettings; InitPlayer(reset); } void CSoundFile::SetResamplerSettings(const CResamplerSettings &resamplersettings) { m_Resampler.m_Settings = resamplersettings; m_Resampler.UpdateTables(); InitAmigaResampler(); } void CSoundFile::InitPlayer(bool bReset) { if(bReset) { ResetMixStat(); m_dryLOfsVol = m_dryROfsVol = 0; m_surroundLOfsVol = m_surroundROfsVol = 0; InitAmigaResampler(); } m_Resampler.UpdateTables(); #ifndef NO_REVERB m_Reverb.Initialize(bReset, m_RvbROfsVol, m_RvbLOfsVol, m_MixerSettings.gdwMixingFreq); #endif #ifndef NO_DSP m_Surround.Initialize(bReset, m_MixerSettings.gdwMixingFreq); #endif #ifndef NO_DSP m_MegaBass.Initialize(bReset, m_MixerSettings.gdwMixingFreq); #endif #ifndef NO_EQ m_EQ.Initialize(bReset, m_MixerSettings.gdwMixingFreq); #endif #ifndef NO_AGC m_AGC.Initialize(bReset, m_MixerSettings.gdwMixingFreq); #endif #ifndef NO_DSP m_BitCrush.Initialize(bReset, m_MixerSettings.gdwMixingFreq); #endif if(m_opl) { m_opl->Initialize(m_MixerSettings.gdwMixingFreq); } } bool CSoundFile::FadeSong(uint32 msec) { samplecount_t nsamples = Util::muldiv(msec, m_MixerSettings.gdwMixingFreq, 1000); if (nsamples <= 0) return false; if (nsamples > 0x100000) nsamples = 0x100000; m_PlayState.m_nBufferCount = nsamples; int32 nRampLength = static_cast(m_PlayState.m_nBufferCount); // Ramp everything down for (uint32 noff=0; noff < m_nMixChannels; noff++) { ModChannel &pramp = m_PlayState.Chn[m_PlayState.ChnMix[noff]]; pramp.newRightVol = pramp.newLeftVol = 0; pramp.leftRamp = -pramp.leftVol * (1 << VOLUMERAMPPRECISION) / nRampLength; pramp.rightRamp = -pramp.rightVol * (1 << VOLUMERAMPPRECISION) / nRampLength; pramp.rampLeftVol = pramp.leftVol * (1 << VOLUMERAMPPRECISION); pramp.rampRightVol = pramp.rightVol * (1 << VOLUMERAMPPRECISION); pramp.nRampLength = nRampLength; pramp.dwFlags.set(CHN_VOLUMERAMP); } return true; } // Apply stereo separation factor on an interleaved stereo/quad stream. // count = Number of stereo sample pairs to process // separation = -256...256 (negative values = swap L/R, 0 = mono, 128 = normal) static void ApplyStereoSeparation(mixsample_t *mixBuf, std::size_t count, int32 separation) { #ifdef MPT_INTMIXER const mixsample_t factor_num = separation; // 128 =^= 1.0f const mixsample_t factor_den = MixerSettings::StereoSeparationScale; // 128 const mixsample_t normalize_den = 2; // mid/side pre/post normalization const mixsample_t mid_den = normalize_den; const mixsample_t side_num = factor_num; const mixsample_t side_den = factor_den * normalize_den; #else const float normalize_factor = 0.5f; // cumulative mid/side normalization factor (1/sqrt(2))*(1/sqrt(2)) const float factor = static_cast(separation) / static_cast(MixerSettings::StereoSeparationScale); // sep / 128 const float mid_factor = normalize_factor; const float side_factor = factor * normalize_factor; #endif for(std::size_t i = 0; i < count; i++) { mixsample_t l = mixBuf[0]; mixsample_t r = mixBuf[1]; mixsample_t m = l + r; mixsample_t s = l - r; #ifdef MPT_INTMIXER m /= mid_den; s = Util::muldiv(s, side_num, side_den); #else m *= mid_factor; s *= side_factor; #endif l = m + s; r = m - s; mixBuf[0] = l; mixBuf[1] = r; mixBuf += 2; } } static void ApplyStereoSeparation(mixsample_t *SoundFrontBuffer, mixsample_t *SoundRearBuffer, std::size_t channels, std::size_t countChunk, int32 separation) { if(separation == MixerSettings::StereoSeparationScale) { // identity return; } if(channels >= 2) ApplyStereoSeparation(SoundFrontBuffer, countChunk, separation); if(channels >= 4) ApplyStereoSeparation(SoundRearBuffer , countChunk, separation); } void CSoundFile::ProcessInputChannels(IAudioSource &source, std::size_t countChunk) { for(std::size_t channel = 0; channel < NUMMIXINPUTBUFFERS; ++channel) { std::fill(&(MixInputBuffer[channel][0]), &(MixInputBuffer[channel][countChunk]), 0); } mixsample_t * buffers[NUMMIXINPUTBUFFERS]; for(std::size_t channel = 0; channel < NUMMIXINPUTBUFFERS; ++channel) { buffers[channel] = MixInputBuffer[channel]; } source.Process(mpt::audio_span_planar(buffers, m_MixerSettings.NumInputChannels, countChunk)); } // Read one tick but skip all expensive rendering options CSoundFile::samplecount_t CSoundFile::ReadOneTick() { const auto origMaxMixChannels = m_MixerSettings.m_nMaxMixChannels; m_MixerSettings.m_nMaxMixChannels = 0; while(m_PlayState.m_nBufferCount) { auto framesToRender = std::min(m_PlayState.m_nBufferCount, samplecount_t(MIXBUFFERSIZE)); CreateStereoMix(framesToRender); m_PlayState.m_nBufferCount -= framesToRender; m_PlayState.m_lTotalSampleCount += framesToRender; } m_MixerSettings.m_nMaxMixChannels = origMaxMixChannels; if(ReadNote()) return m_PlayState.m_nBufferCount; else return 0; } CSoundFile::samplecount_t CSoundFile::Read(samplecount_t count, IAudioTarget &target, IAudioSource &source, std::optional> outputMonitor, std::optional> inputMonitor) { MPT_ASSERT_ALWAYS(m_MixerSettings.IsValid()); samplecount_t countRendered = 0; samplecount_t countToRender = count; while(!m_SongFlags[SONG_ENDREACHED] && countToRender > 0) { // Update Channel Data if(!m_PlayState.m_nBufferCount) { // Last tick or fade completely processed, find out what to do next if(m_SongFlags[SONG_FADINGSONG]) { // Song was faded out m_SongFlags.set(SONG_ENDREACHED); } else if(ReadNote()) { // Render next tick (normal progress) MPT_ASSERT(m_PlayState.m_nBufferCount > 0); #ifdef MODPLUG_TRACKER // Save pattern cue points for WAV rendering here (if we reached a new pattern, that is.) if(m_PatternCuePoints != nullptr && (m_PatternCuePoints->empty() || m_PlayState.m_nCurrentOrder != m_PatternCuePoints->back().order)) { PatternCuePoint cue; cue.offset = countRendered; cue.order = m_PlayState.m_nCurrentOrder; cue.processed = false; // We don't know the base offset in the file here. It has to be added in the main conversion loop. m_PatternCuePoints->push_back(cue); } #endif } else { // No new pattern data #ifdef MODPLUG_TRACKER if((m_nMaxOrderPosition) && (m_PlayState.m_nCurrentOrder >= m_nMaxOrderPosition)) { m_SongFlags.set(SONG_ENDREACHED); } #endif // MODPLUG_TRACKER if(IsRenderingToDisc()) { // Disable song fade when rendering or when requested in libopenmpt. m_SongFlags.set(SONG_ENDREACHED); } else { // end of song reached, fade it out if(FadeSong(FADESONGDELAY)) // sets m_nBufferCount xor returns false { // FadeSong sets m_nBufferCount here MPT_ASSERT(m_PlayState.m_nBufferCount > 0); m_SongFlags.set(SONG_FADINGSONG); } else { m_SongFlags.set(SONG_ENDREACHED); } } } } if(m_SongFlags[SONG_ENDREACHED]) { // Mix done. // If we decide to continue the mix (possible in libopenmpt), the tick count // is valid right now (0), meaning that no new row data will be processed. // This would effectively prolong the last played row. m_PlayState.m_nTickCount = m_PlayState.TicksOnRow(); break; } MPT_ASSERT(m_PlayState.m_nBufferCount > 0); // assert that we have actually something to do const samplecount_t countChunk = std::min({ static_cast(MIXBUFFERSIZE), static_cast(m_PlayState.m_nBufferCount), static_cast(countToRender) }); if(m_MixerSettings.NumInputChannels > 0) { ProcessInputChannels(source, countChunk); } if(inputMonitor) { mixsample_t *buffers[NUMMIXINPUTBUFFERS]; for(std::size_t channel = 0; channel < NUMMIXINPUTBUFFERS; ++channel) { buffers[channel] = MixInputBuffer[channel]; } inputMonitor->get().Process(mpt::audio_span_planar(buffers, m_MixerSettings.NumInputChannels, countChunk)); } CreateStereoMix(countChunk); if(m_opl) { m_opl->Mix(MixSoundBuffer, countChunk, m_OPLVolumeFactor * m_nVSTiVolume / 48); } #ifndef NO_REVERB m_Reverb.Process(MixSoundBuffer, ReverbSendBuffer, m_RvbROfsVol, m_RvbLOfsVol, countChunk); #endif // NO_REVERB #ifndef NO_PLUGINS if(m_loadedPlugins) { ProcessPlugins(countChunk); } #endif // NO_PLUGINS if(m_MixerSettings.gnChannels == 1) { MonoFromStereo(MixSoundBuffer, countChunk); } if(m_PlayConfig.getGlobalVolumeAppliesToMaster()) { ProcessGlobalVolume(countChunk); } if(m_MixerSettings.m_nStereoSeparation != MixerSettings::StereoSeparationScale) { ProcessStereoSeparation(countChunk); } if(m_MixerSettings.DSPMask) { ProcessDSP(countChunk); } if(m_MixerSettings.gnChannels == 4) { InterleaveFrontRear(MixSoundBuffer, MixRearBuffer, countChunk); } if(outputMonitor) { outputMonitor->get().Process(mpt::audio_span_interleaved(MixSoundBuffer, m_MixerSettings.gnChannels, countChunk)); } target.Process(mpt::audio_span_interleaved(MixSoundBuffer, m_MixerSettings.gnChannels, countChunk)); // Buffer ready countRendered += countChunk; countToRender -= countChunk; m_PlayState.m_nBufferCount -= countChunk; m_PlayState.m_lTotalSampleCount += countChunk; #ifdef MODPLUG_TRACKER if(IsRenderingToDisc()) { // Stop playback on F00 if no more voices are active. // F00 sets the tick count to 65536 in FT2, so it just generates a reaaaally long row. // Usually this command can be found at the end of a song to effectively stop playback. // Since we don't want to render hours of silence, we are going to check if there are // still any channels playing, and if that is no longer the case, we stop playback at // the end of the next tick. if(m_PlayState.m_nMusicSpeed == uint16_max && (m_nMixStat == 0 || m_PlayState.m_nGlobalVolume == 0) && GetType() == MOD_TYPE_XM && !m_PlayState.m_nBufferCount) { m_SongFlags.set(SONG_ENDREACHED); } } #endif // MODPLUG_TRACKER } // mix done return countRendered; } void CSoundFile::ProcessDSP(uint32 countChunk) { #ifndef NO_DSP if(m_MixerSettings.DSPMask & SNDDSP_SURROUND) { m_Surround.Process(MixSoundBuffer, MixRearBuffer, countChunk, m_MixerSettings.gnChannels); } #endif // NO_DSP #ifndef NO_DSP if(m_MixerSettings.DSPMask & SNDDSP_MEGABASS) { m_MegaBass.Process(MixSoundBuffer, MixRearBuffer, countChunk, m_MixerSettings.gnChannels); } #endif // NO_DSP #ifndef NO_EQ if(m_MixerSettings.DSPMask & SNDDSP_EQ) { m_EQ.Process(MixSoundBuffer, MixRearBuffer, countChunk, m_MixerSettings.gnChannels); } #endif // NO_EQ #ifndef NO_AGC if(m_MixerSettings.DSPMask & SNDDSP_AGC) { m_AGC.Process(MixSoundBuffer, MixRearBuffer, countChunk, m_MixerSettings.gnChannels); } #endif // NO_AGC #ifndef NO_DSP if(m_MixerSettings.DSPMask & SNDDSP_BITCRUSH) { m_BitCrush.Process(MixSoundBuffer, MixRearBuffer, countChunk, m_MixerSettings.gnChannels); } #endif // NO_DSP #if defined(NO_DSP) && defined(NO_EQ) && defined(NO_AGC) MPT_UNREFERENCED_PARAMETER(countChunk); #endif } ///////////////////////////////////////////////////////////////////////////// // Handles navigation/effects bool CSoundFile::ProcessRow() { while(++m_PlayState.m_nTickCount >= m_PlayState.TicksOnRow()) { const auto [ignoreRow, patternTransition] = NextRow(m_PlayState, m_SongFlags[SONG_BREAKTOROW]); #ifdef MODPLUG_TRACKER if(patternTransition) { HandlePatternTransitionEvents(); } // "Lock row" editing feature if(m_lockRowStart != ROWINDEX_INVALID && (m_PlayState.m_nRow < m_lockRowStart || m_PlayState.m_nRow > m_lockRowEnd) && !IsRenderingToDisc()) { m_PlayState.m_nRow = m_lockRowStart; } // "Lock order" editing feature if(Order().IsPositionLocked(m_PlayState.m_nCurrentOrder) && !IsRenderingToDisc()) { m_PlayState.m_nCurrentOrder = m_lockOrderStart; } #else MPT_UNUSED_VARIABLE(patternTransition); #endif // MODPLUG_TRACKER // Check if pattern is valid if(!m_SongFlags[SONG_PATTERNLOOP]) { m_PlayState.m_nPattern = (m_PlayState.m_nCurrentOrder < Order().size()) ? Order()[m_PlayState.m_nCurrentOrder] : Order.GetInvalidPatIndex(); if (m_PlayState.m_nPattern < Patterns.Size() && !Patterns[m_PlayState.m_nPattern].IsValid()) m_PlayState.m_nPattern = Order.GetIgnoreIndex(); while (m_PlayState.m_nPattern >= Patterns.Size()) { // End of song? if ((m_PlayState.m_nPattern == Order.GetInvalidPatIndex()) || (m_PlayState.m_nCurrentOrder >= Order().size())) { ORDERINDEX restartPosOverride = Order().GetRestartPos(); if(restartPosOverride == 0 && m_PlayState.m_nCurrentOrder <= Order().size() && m_PlayState.m_nCurrentOrder > 0) { // Subtune detection. Subtunes are separated by "---" order items, so if we're in a // subtune and there's no restart position, we go to the first order of the subtune // (i.e. the first order after the previous "---" item) for(ORDERINDEX ord = m_PlayState.m_nCurrentOrder - 1; ord > 0; ord--) { if(Order()[ord] == Order.GetInvalidPatIndex()) { // Jump back to first order of this subtune restartPosOverride = ord + 1; break; } } } // If channel resetting is disabled in MPT, we will emulate a pattern break (and we always do it if we're not in MPT) #ifdef MODPLUG_TRACKER if(!(TrackerSettings::Instance().m_dwPatternSetup & PATTERN_RESETCHANNELS)) #endif // MODPLUG_TRACKER { m_SongFlags.set(SONG_BREAKTOROW); } if (restartPosOverride == 0 && !m_SongFlags[SONG_BREAKTOROW]) { //rewbs.instroVSTi: stop all VSTi at end of song, if looping. StopAllVsti(); m_PlayState.m_nMusicSpeed = m_nDefaultSpeed; m_PlayState.m_nMusicTempo = m_nDefaultTempo; m_PlayState.m_nGlobalVolume = m_nDefaultGlobalVolume; for(CHANNELINDEX i = 0; i < MAX_CHANNELS; i++) { auto &chn = m_PlayState.Chn[i]; if(chn.dwFlags[CHN_ADLIB] && m_opl) { m_opl->NoteCut(i); } chn.dwFlags.set(CHN_NOTEFADE | CHN_KEYOFF); chn.nFadeOutVol = 0; if(i < m_nChannels) { chn.nGlobalVol = ChnSettings[i].nVolume; chn.nVolume = ChnSettings[i].nVolume; chn.nPan = ChnSettings[i].nPan; chn.nPanSwing = chn.nVolSwing = 0; chn.nCutSwing = chn.nResSwing = 0; chn.nOldVolParam = 0; chn.oldOffset = 0; chn.nOldHiOffset = 0; chn.nPortamentoDest = 0; if(!chn.nLength) { chn.dwFlags = ChnSettings[i].dwFlags; chn.nLoopStart = 0; chn.nLoopEnd = 0; chn.pModInstrument = nullptr; chn.pModSample = nullptr; } } } } //Handle Repeat position m_PlayState.m_nCurrentOrder = restartPosOverride; m_SongFlags.reset(SONG_BREAKTOROW); //If restart pos points to +++, move along while(m_PlayState.m_nCurrentOrder < Order().size() && Order()[m_PlayState.m_nCurrentOrder] == Order.GetIgnoreIndex()) { m_PlayState.m_nCurrentOrder++; } //Check for end of song or bad pattern if (m_PlayState.m_nCurrentOrder >= Order().size() || !Order().IsValidPat(m_PlayState.m_nCurrentOrder)) { m_visitedRows.Initialize(true); return false; } } else { m_PlayState.m_nCurrentOrder++; } if (m_PlayState.m_nCurrentOrder < Order().size()) m_PlayState.m_nPattern = Order()[m_PlayState.m_nCurrentOrder]; else m_PlayState.m_nPattern = Order.GetInvalidPatIndex(); if (m_PlayState.m_nPattern < Patterns.Size() && !Patterns[m_PlayState.m_nPattern].IsValid()) m_PlayState.m_nPattern = Order.GetIgnoreIndex(); } m_PlayState.m_nNextOrder = m_PlayState.m_nCurrentOrder; #ifdef MODPLUG_TRACKER if ((m_nMaxOrderPosition) && (m_PlayState.m_nCurrentOrder >= m_nMaxOrderPosition)) return false; #endif // MODPLUG_TRACKER } // Weird stuff? if (!Patterns.IsValidPat(m_PlayState.m_nPattern)) return false; // Did we jump to an invalid row? if (m_PlayState.m_nRow >= Patterns[m_PlayState.m_nPattern].GetNumRows()) m_PlayState.m_nRow = 0; // Has this row been visited before? We might want to stop playback now. // But: We will not mark the row as modified if the song is not in loop mode but // the pattern loop (editor flag, not to be confused with the pattern loop effect) // flag is set - because in that case, the module would stop after the first pattern loop... const bool overrideLoopCheck = (m_nRepeatCount != -1) && m_SongFlags[SONG_PATTERNLOOP]; if(!overrideLoopCheck && m_visitedRows.Visit(m_PlayState.m_nCurrentOrder, m_PlayState.m_nRow, m_PlayState.Chn, ignoreRow)) { if(m_nRepeatCount) { // repeat count == -1 means repeat infinitely. if(m_nRepeatCount > 0) { m_nRepeatCount--; } // Forget all but the current row. m_visitedRows.Initialize(true); m_visitedRows.Visit(m_PlayState.m_nCurrentOrder, m_PlayState.m_nRow, m_PlayState.Chn, ignoreRow); } else { #ifdef MODPLUG_TRACKER // Let's check again if this really is the end of the song. // The visited rows vector might have been screwed up while editing... // This is of course not possible during rendering to WAV, so we ignore that case. bool isReallyAtEnd = IsRenderingToDisc(); if(!isReallyAtEnd) { for(const auto &t : GetLength(eNoAdjust, GetLengthTarget(true))) { if(t.lastOrder == m_PlayState.m_nCurrentOrder && t.lastRow == m_PlayState.m_nRow) { isReallyAtEnd = true; break; } } } if(isReallyAtEnd) { // This is really the song's end! m_visitedRows.Initialize(true); return false; } else { // Ok, this is really dirty, but we have to update the visited rows vector... GetLength(eAdjustOnlyVisitedRows, GetLengthTarget(m_PlayState.m_nCurrentOrder, m_PlayState.m_nRow)); } #else if(m_SongFlags[SONG_PLAYALLSONGS]) { // When playing all subsongs consecutively, first search for any hidden subsongs... if(!m_visitedRows.GetFirstUnvisitedRow(m_PlayState.m_nCurrentOrder, m_PlayState.m_nRow, true)) { // ...and then try the next sequence. m_PlayState.m_nNextOrder = m_PlayState.m_nCurrentOrder = 0; m_PlayState.m_nNextRow = m_PlayState.m_nRow = 0; if(Order.GetCurrentSequenceIndex() >= Order.GetNumSequences() - 1) { Order.SetSequence(0); m_visitedRows.Initialize(true); return false; } Order.SetSequence(Order.GetCurrentSequenceIndex() + 1); m_visitedRows.Initialize(true); } // When jumping to the next subsong, stop all playing notes from the previous song... const auto muteFlag = CSoundFile::GetChannelMuteFlag(); for(CHANNELINDEX i = 0; i < MAX_CHANNELS; i++) m_PlayState.Chn[i].Reset(ModChannel::resetSetPosFull, *this, i, muteFlag); StopAllVsti(); // ...and the global playback information. m_PlayState.m_nMusicSpeed = m_nDefaultSpeed; m_PlayState.m_nMusicTempo = m_nDefaultTempo; m_PlayState.m_nGlobalVolume = m_nDefaultGlobalVolume; m_PlayState.m_nNextOrder = m_PlayState.m_nCurrentOrder; m_PlayState.m_nNextRow = m_PlayState.m_nRow; if(Order().size() > m_PlayState.m_nCurrentOrder) m_PlayState.m_nPattern = Order()[m_PlayState.m_nCurrentOrder]; m_visitedRows.Visit(m_PlayState.m_nCurrentOrder, m_PlayState.m_nRow, m_PlayState.Chn, ignoreRow); if (!Patterns.IsValidPat(m_PlayState.m_nPattern)) return false; } else { m_visitedRows.Initialize(true); return false; } #endif // MODPLUG_TRACKER } } SetupNextRow(m_PlayState, m_SongFlags[SONG_PATTERNLOOP]); // Reset channel values ModCommand *m = Patterns[m_PlayState.m_nPattern].GetpModCommand(m_PlayState.m_nRow, 0); for (ModChannel *pChn = m_PlayState.Chn, *pEnd = pChn + m_nChannels; pChn != pEnd; pChn++, m++) { // First, handle some quirks that happen after the last tick of the previous row... if(m_playBehaviour[KST3PortaAfterArpeggio] && pChn->nCommand == CMD_ARPEGGIO // Previous row state! && (m->command == CMD_PORTAMENTOUP || m->command == CMD_PORTAMENTODOWN)) { // In ST3, a portamento immediately following an arpeggio continues where the arpeggio left off. // Test case: PortaAfterArp.s3m pChn->nPeriod = GetPeriodFromNote(pChn->nArpeggioLastNote, pChn->nFineTune, pChn->nC5Speed); } if(m_playBehaviour[kMODOutOfRangeNoteDelay] && !m->IsNote() && pChn->rowCommand.IsNote() && pChn->rowCommand.command == CMD_MODCMDEX && (pChn->rowCommand.param & 0xF0) == 0xD0 && (pChn->rowCommand.param & 0x0Fu) >= m_PlayState.m_nMusicSpeed) { // In ProTracker, a note triggered by an out-of-range note delay can be heard on the next row // if there is no new note on that row. // Test case: NoteDelay-NextRow.mod pChn->nPeriod = GetPeriodFromNote(pChn->rowCommand.note, pChn->nFineTune, 0); } if(m_playBehaviour[kMODTempoOnSecondTick] && !m_playBehaviour[kMODVBlankTiming] && m_PlayState.m_nMusicSpeed == 1 && pChn->rowCommand.command == CMD_TEMPO) { // ProTracker sets the tempo after the first tick. This block handles the case of one tick per row. // Test case: TempoChange.mod m_PlayState.m_nMusicTempo = TEMPO(pChn->rowCommand.param, 0); } pChn->rowCommand = *m; pChn->rightVol = pChn->newRightVol; pChn->leftVol = pChn->newLeftVol; pChn->dwFlags.reset(CHN_VIBRATO | CHN_TREMOLO); if(!m_playBehaviour[kITVibratoTremoloPanbrello]) pChn->nPanbrelloOffset = 0; pChn->nCommand = CMD_NONE; pChn->m_plugParamValueStep = 0; } // Now that we know which pattern we're on, we can update time signatures (global or pattern-specific) UpdateTimeSignature(); if(ignoreRow) { m_PlayState.m_nTickCount = m_PlayState.m_nMusicSpeed; continue; } break; } // Should we process tick0 effects? if (!m_PlayState.m_nMusicSpeed) m_PlayState.m_nMusicSpeed = 1; //End of row? stop pattern step (aka "play row"). #ifdef MODPLUG_TRACKER if (m_PlayState.m_nTickCount >= m_PlayState.TicksOnRow() - 1) { if(m_SongFlags[SONG_STEP]) { m_SongFlags.reset(SONG_STEP); m_SongFlags.set(SONG_PAUSED); } } #endif // MODPLUG_TRACKER if (m_PlayState.m_nTickCount) { m_SongFlags.reset(SONG_FIRSTTICK); if(!(GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2)) && (GetType() != MOD_TYPE_MOD || m_SongFlags[SONG_PT_MODE]) // Fix infinite loop in "GamerMan " by MrGamer, which was made with FT2 && m_PlayState.m_nTickCount < m_PlayState.TicksOnRow()) { // Emulate first tick behaviour if Row Delay is set. // Test cases: PatternDelaysRetrig.it, PatternDelaysRetrig.s3m, PatternDelaysRetrig.xm, PatternDelaysRetrig.mod if(!(m_PlayState.m_nTickCount % (m_PlayState.m_nMusicSpeed + m_PlayState.m_nFrameDelay))) { m_SongFlags.set(SONG_FIRSTTICK); } } } else { m_SongFlags.set(SONG_FIRSTTICK); m_SongFlags.reset(SONG_BREAKTOROW); } // Update Effects return ProcessEffects(); } std::pair CSoundFile::NextRow(PlayState &playState, const bool breakRow) const { // When having an EEx effect on the same row as a Dxx jump, the target row is not played in ProTracker. // Test case: DelayBreak.mod (based on condom_corruption by Travolta) const bool ignoreRow = playState.m_nPatternDelay > 1 && breakRow && GetType() == MOD_TYPE_MOD; // Done with the last row of the pattern or jumping somewhere else (could also be a result of pattern loop to row 0, but that doesn't matter here) const bool patternTransition = playState.m_nNextRow == 0 || breakRow; if(patternTransition && GetType() == MOD_TYPE_S3M) { // Reset pattern loop start // Test case: LoopReset.s3m for(CHANNELINDEX i = 0; i < GetNumChannels(); i++) { playState.Chn[i].nPatternLoop = 0; } } playState.m_nPatternDelay = 0; playState.m_nFrameDelay = 0; playState.m_nTickCount = 0; playState.m_nRow = playState.m_nNextRow; playState.m_nCurrentOrder = playState.m_nNextOrder; return {ignoreRow, patternTransition}; } void CSoundFile::SetupNextRow(PlayState &playState, const bool patternLoop) const { playState.m_nNextRow = playState.m_nRow + 1; if(playState.m_nNextRow >= Patterns[playState.m_nPattern].GetNumRows()) { if(!patternLoop) playState.m_nNextOrder = playState.m_nCurrentOrder + 1; playState.m_nNextRow = 0; // FT2 idiosyncrasy: When E60 is used on a pattern row x, the following pattern also starts from row x // instead of the beginning of the pattern, unless there was a Bxx or Dxx effect. if(m_playBehaviour[kFT2LoopE60Restart]) { playState.m_nNextRow = playState.m_nextPatStartRow; playState.m_nextPatStartRow = 0; } } } //////////////////////////////////////////////////////////////////////////////////////////// // Channel effect processing // Calculate delta for Vibrato / Tremolo / Panbrello effect int CSoundFile::GetVibratoDelta(int type, int position) const { // IT compatibility: IT has its own, more precise tables if(m_playBehaviour[kITVibratoTremoloPanbrello]) { position &= 0xFF; switch(type & 0x03) { case 0: // Sine default: return ITSinusTable[position]; case 1: // Ramp down return 64 - (position + 1) / 2; case 2: // Square return position < 128 ? 64 : 0; case 3: // Random return mpt::random(AccessPRNG()) - 0x40; } } else if(GetType() & (MOD_TYPE_DIGI | MOD_TYPE_DBM)) { // Other waveforms are not supported. static constexpr int8 DBMSinus[] = { 33, 52, 69, 84, 96, 107, 116, 122, 125, 127, 125, 122, 116, 107, 96, 84, 69, 52, 33, 13, -8, -31, -54, -79, -104,-128, -104, -79, -54, -31, -8, 13, }; return DBMSinus[(position / 2u) & 0x1F]; } else { position &= 0x3F; switch(type & 0x03) { case 0: // Sine default: return ModSinusTable[position]; case 1: // Ramp down return (position < 32 ? 0 : 255) - position * 4; case 2: // Square return position < 32 ? 127 : -127; case 3: // Random return ModRandomTable[position]; } } } void CSoundFile::ProcessVolumeSwing(ModChannel &chn, int &vol) const { if(m_playBehaviour[kITSwingBehaviour]) { vol += chn.nVolSwing; Limit(vol, 0, 64); } else if(m_playBehaviour[kMPTOldSwingBehaviour]) { vol += chn.nVolSwing; Limit(vol, 0, 256); } else { chn.nVolume += chn.nVolSwing; Limit(chn.nVolume, 0, 256); vol = chn.nVolume; chn.nVolSwing = 0; } } void CSoundFile::ProcessPanningSwing(ModChannel &chn) const { if(m_playBehaviour[kITSwingBehaviour] || m_playBehaviour[kMPTOldSwingBehaviour]) { chn.nRealPan = chn.nPan + chn.nPanSwing; Limit(chn.nRealPan, 0, 256); } else { chn.nPan += chn.nPanSwing; Limit(chn.nPan, 0, 256); chn.nPanSwing = 0; chn.nRealPan = chn.nPan; } } void CSoundFile::ProcessTremolo(ModChannel &chn, int &vol) const { if (chn.dwFlags[CHN_TREMOLO]) { if(m_SongFlags.test_all(SONG_FIRSTTICK | SONG_PT_MODE)) { // ProTracker doesn't apply tremolo nor advance on the first tick. // Test case: VibratoReset.mod return; } // IT compatibility: Why would you not want to execute tremolo at volume 0? if(vol > 0 || m_playBehaviour[kITVibratoTremoloPanbrello]) { // IT compatibility: We don't need a different attenuation here because of the different tables we're going to use const uint8 attenuation = ((GetType() & (MOD_TYPE_XM | MOD_TYPE_MOD)) || m_playBehaviour[kITVibratoTremoloPanbrello]) ? 5 : 6; int delta = GetVibratoDelta(chn.nTremoloType, chn.nTremoloPos); if((chn.nTremoloType & 0x03) == 1 && m_playBehaviour[kFT2MODTremoloRampWaveform]) { // FT2 compatibility: Tremolo ramp down / triangle implementation is weird and affected by vibrato position (copypaste bug) // Test case: TremoloWaveforms.xm, TremoloVibrato.xm uint8 ramp = (chn.nTremoloPos * 4u) & 0x7F; // Volume-colum vibrato gets executed first in FT2, so we may need to advance the vibrato position first uint32 vibPos = chn.nVibratoPos; if(!m_SongFlags[SONG_FIRSTTICK] && chn.dwFlags[CHN_VIBRATO]) vibPos += chn.nVibratoSpeed; if((vibPos & 0x3F) >= 32) ramp ^= 0x7F; if((chn.nTremoloPos & 0x3F) >= 32) delta = -ramp; else delta = ramp; } if(GetType() != MOD_TYPE_DMF) { vol += (delta * chn.nTremoloDepth) / (1 << attenuation); } else { // Tremolo in DMF always attenuates by a percentage of the current note volume vol -= (vol * chn.nTremoloDepth * (64 - delta)) / (128 * 64); } } if(!m_SongFlags[SONG_FIRSTTICK] || ((GetType() & (MOD_TYPE_IT|MOD_TYPE_MPT)) && !m_SongFlags[SONG_ITOLDEFFECTS])) { // IT compatibility: IT has its own, more precise tables if(m_playBehaviour[kITVibratoTremoloPanbrello]) chn.nTremoloPos += 4 * chn.nTremoloSpeed; else chn.nTremoloPos += chn.nTremoloSpeed; } } } void CSoundFile::ProcessTremor(CHANNELINDEX nChn, int &vol) { ModChannel &chn = m_PlayState.Chn[nChn]; if(m_playBehaviour[kFT2Tremor]) { // FT2 Compatibility: Weird XM tremor. // Test case: Tremor.xm if(chn.nTremorCount & 0x80) { if(!m_SongFlags[SONG_FIRSTTICK] && chn.nCommand == CMD_TREMOR) { chn.nTremorCount &= ~0x20; if(chn.nTremorCount == 0x80) { // Reached end of off-time chn.nTremorCount = (chn.nTremorParam >> 4) | 0xC0; } else if(chn.nTremorCount == 0xC0) { // Reached end of on-time chn.nTremorCount = (chn.nTremorParam & 0x0F) | 0x80; } else { chn.nTremorCount--; } chn.dwFlags.set(CHN_FASTVOLRAMP); } if((chn.nTremorCount & 0xE0) == 0x80) { vol = 0; } } } else if(chn.nCommand == CMD_TREMOR) { // IT compatibility 12. / 13.: Tremor if(m_playBehaviour[kITTremor]) { if((chn.nTremorCount & 0x80) && chn.nLength) { if (chn.nTremorCount == 0x80) chn.nTremorCount = (chn.nTremorParam >> 4) | 0xC0; else if (chn.nTremorCount == 0xC0) chn.nTremorCount = (chn.nTremorParam & 0x0F) | 0x80; else chn.nTremorCount--; } if((chn.nTremorCount & 0xC0) == 0x80) vol = 0; } else { uint8 ontime = chn.nTremorParam >> 4; uint8 n = ontime + (chn.nTremorParam & 0x0F); // Total tremor cycle time (On + Off) if ((!(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT))) || m_SongFlags[SONG_ITOLDEFFECTS]) { n += 2; ontime++; } uint8 tremcount = chn.nTremorCount; if(!(GetType() & MOD_TYPE_XM)) { if (tremcount >= n) tremcount = 0; if (tremcount >= ontime) vol = 0; chn.nTremorCount = tremcount + 1; } else { if(m_SongFlags[SONG_FIRSTTICK]) { // tremcount is only 0 on the first tremor tick after triggering a note. if(tremcount > 0) { tremcount--; } } else { chn.nTremorCount = tremcount + 1; } if (tremcount % n >= ontime) vol = 0; } } chn.dwFlags.set(CHN_FASTVOLRAMP); } #ifndef NO_PLUGINS // Plugin tremor if(chn.nCommand == CMD_TREMOR && chn.pModInstrument && chn.pModInstrument->nMixPlug && !chn.pModInstrument->dwFlags[INS_MUTE] && !chn.dwFlags[CHN_MUTE | CHN_SYNCMUTE] && ModCommand::IsNote(chn.nLastNote)) { const ModInstrument *pIns = chn.pModInstrument; IMixPlugin *pPlugin = m_MixPlugins[pIns->nMixPlug - 1].pMixPlugin; if(pPlugin) { const bool isPlaying = pPlugin->IsNotePlaying(chn.nLastNote, nChn); if(vol == 0 && isPlaying) pPlugin->MidiCommand(*pIns, chn.nLastNote + NOTE_MAX_SPECIAL, 0, nChn); else if(vol != 0 && !isPlaying) pPlugin->MidiCommand(*pIns, chn.nLastNote, static_cast(chn.nVolume), nChn); } } #endif // NO_PLUGINS } bool CSoundFile::IsEnvelopeProcessed(const ModChannel &chn, EnvelopeType env) const { if(chn.pModInstrument == nullptr) { return false; } const InstrumentEnvelope &insEnv = chn.pModInstrument->GetEnvelope(env); // IT Compatibility: S77/S79/S7B do not disable the envelope, they just pause the counter // Test cases: s77.it, EnvLoops.xm, PanSustainRelease.xm bool playIfPaused = m_playBehaviour[kITEnvelopePositionHandling] || m_playBehaviour[kFT2PanSustainRelease]; return ((chn.GetEnvelope(env).flags[ENV_ENABLED] || (insEnv.dwFlags[ENV_ENABLED] && playIfPaused)) && !insEnv.empty()); } void CSoundFile::ProcessVolumeEnvelope(ModChannel &chn, int &vol) const { if(IsEnvelopeProcessed(chn, ENV_VOLUME)) { const ModInstrument *pIns = chn.pModInstrument; if(m_playBehaviour[kITEnvelopePositionHandling] && chn.VolEnv.nEnvPosition == 0) { // If the envelope is disabled at the very same moment as it is triggered, we do not process anything. return; } const int envpos = chn.VolEnv.nEnvPosition - (m_playBehaviour[kITEnvelopePositionHandling] ? 1 : 0); // Get values in [0, 256] int envval = pIns->VolEnv.GetValueFromPosition(envpos, 256); // if we are in the release portion of the envelope, // rescale envelope factor so that it is proportional to the release point // and release envelope beginning. if(pIns->VolEnv.nReleaseNode != ENV_RELEASE_NODE_UNSET && chn.VolEnv.nEnvValueAtReleaseJump != NOT_YET_RELEASED) { int envValueAtReleaseJump = chn.VolEnv.nEnvValueAtReleaseJump; int envValueAtReleaseNode = pIns->VolEnv[pIns->VolEnv.nReleaseNode].value * 4; //If we have just hit the release node, force the current env value //to be that of the release node. This works around the case where // we have another node at the same position as the release node. if(envpos == pIns->VolEnv[pIns->VolEnv.nReleaseNode].tick) envval = envValueAtReleaseNode; if(m_playBehaviour[kLegacyReleaseNode]) { // Old, hard to grasp release node behaviour (additive) int relativeVolumeChange = (envval - envValueAtReleaseNode) * 2; envval = envValueAtReleaseJump + relativeVolumeChange; } else { // New behaviour, truly relative to release node if(envValueAtReleaseNode > 0) envval = envValueAtReleaseJump * envval / envValueAtReleaseNode; else envval = 0; } } vol = (vol * Clamp(envval, 0, 512)) / 256; } } void CSoundFile::ProcessPanningEnvelope(ModChannel &chn) const { if(IsEnvelopeProcessed(chn, ENV_PANNING)) { const ModInstrument *pIns = chn.pModInstrument; if(m_playBehaviour[kITEnvelopePositionHandling] && chn.PanEnv.nEnvPosition == 0) { // If the envelope is disabled at the very same moment as it is triggered, we do not process anything. return; } const int envpos = chn.PanEnv.nEnvPosition - (m_playBehaviour[kITEnvelopePositionHandling] ? 1 : 0); // Get values in [-32, 32] const int envval = pIns->PanEnv.GetValueFromPosition(envpos, 64) - 32; int pan = chn.nRealPan; if(pan >= 128) { pan += (envval * (256 - pan)) / 32; } else { pan += (envval * (pan)) / 32; } chn.nRealPan = Clamp(pan, 0, 256); } } int CSoundFile::ProcessPitchFilterEnvelope(ModChannel &chn, int32 &period) const { if(IsEnvelopeProcessed(chn, ENV_PITCH)) { const ModInstrument *pIns = chn.pModInstrument; if(m_playBehaviour[kITEnvelopePositionHandling] && chn.PitchEnv.nEnvPosition == 0) { // If the envelope is disabled at the very same moment as it is triggered, we do not process anything. return -1; } const int envpos = chn.PitchEnv.nEnvPosition - (m_playBehaviour[kITEnvelopePositionHandling] ? 1 : 0); // Get values in [-256, 256] #ifdef MODPLUG_TRACKER const int32 range = ENVELOPE_MAX; const int32 amp = 512; #else // TODO: AMS2 envelopes behave differently when linear slides are off - emulate with 15 * (-128...127) >> 6 // Copy over vibrato behaviour for that? const int32 range = GetType() == MOD_TYPE_AMS ? uint8_max : ENVELOPE_MAX; int32 amp; switch(GetType()) { case MOD_TYPE_AMS: amp = 64; break; case MOD_TYPE_MDL: amp = 192; break; default: amp = 512; } #endif const int envval = pIns->PitchEnv.GetValueFromPosition(envpos, amp, range) - amp / 2; if(chn.PitchEnv.flags[ENV_FILTER]) { // Filter Envelope: controls cutoff frequency return SetupChannelFilter(chn, !chn.dwFlags[CHN_FILTER], envval); } else { // Pitch Envelope if(chn.HasCustomTuning()) { if(chn.nFineTune != envval) { chn.nFineTune = mpt::saturate_cast(envval); chn.m_CalculateFreq = true; //Preliminary tests indicated that this behavior //is very close to original(with 12TET) when finestep count //is 15. } } else //Original behavior { const bool useFreq = PeriodsAreFrequencies(); const uint32 (&upTable)[256] = useFreq ? LinearSlideUpTable : LinearSlideDownTable; const uint32 (&downTable)[256] = useFreq ? LinearSlideDownTable : LinearSlideUpTable; int l = envval; if(l < 0) { l = -l; LimitMax(l, 255); period = Util::muldiv(period, downTable[l], 65536); } else { LimitMax(l, 255); period = Util::muldiv(period, upTable[l], 65536); } } //End: Original behavior. } } return -1; } void CSoundFile::IncrementEnvelopePosition(ModChannel &chn, EnvelopeType envType) const { ModChannel::EnvInfo &chnEnv = chn.GetEnvelope(envType); if(chn.pModInstrument == nullptr || !chnEnv.flags[ENV_ENABLED]) { return; } // Increase position uint32 position = chnEnv.nEnvPosition + (m_playBehaviour[kITEnvelopePositionHandling] ? 0 : 1); const InstrumentEnvelope &insEnv = chn.pModInstrument->GetEnvelope(envType); if(insEnv.empty()) { return; } bool endReached = false; if(!m_playBehaviour[kITEnvelopePositionHandling]) { // FT2-style envelope processing. if(insEnv.dwFlags[ENV_LOOP]) { // Normal loop active uint32 end = insEnv[insEnv.nLoopEnd].tick; if(!(GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2))) end++; // FT2 compatibility: If the sustain point is at the loop end and the sustain loop has been released, don't loop anymore. // Test case: EnvLoops.xm const bool escapeLoop = (insEnv.nLoopEnd == insEnv.nSustainEnd && insEnv.dwFlags[ENV_SUSTAIN] && chn.dwFlags[CHN_KEYOFF] && m_playBehaviour[kFT2EnvelopeEscape]); if(position == end && !escapeLoop) { position = insEnv[insEnv.nLoopStart].tick; } } if(insEnv.dwFlags[ENV_SUSTAIN] && !chn.dwFlags[CHN_KEYOFF]) { // Envelope sustained if(position == insEnv[insEnv.nSustainEnd].tick + 1u) { position = insEnv[insEnv.nSustainStart].tick; // FT2 compatibility: If the panning envelope reaches its sustain point before key-off, it stays there forever. // Test case: PanSustainRelease.xm if(m_playBehaviour[kFT2PanSustainRelease] && envType == ENV_PANNING && !chn.dwFlags[CHN_KEYOFF]) { chnEnv.flags.reset(ENV_ENABLED); } } } else { // Limit to last envelope point if(position > insEnv.back().tick) { // Env of envelope position = insEnv.back().tick; endReached = true; } } } else { // IT envelope processing. // Test case: EnvLoops.it uint32 start, end; // IT compatiblity: OpenMPT processes the key-off flag earlier than IT. Grab the flag from the previous tick instead. // Test case: EnvOffLength.it if(insEnv.dwFlags[ENV_SUSTAIN] && !chn.dwOldFlags[CHN_KEYOFF] && (chnEnv.nEnvValueAtReleaseJump == NOT_YET_RELEASED || m_playBehaviour[kReleaseNodePastSustainBug])) { // Envelope sustained start = insEnv[insEnv.nSustainStart].tick; end = insEnv[insEnv.nSustainEnd].tick + 1; } else if(insEnv.dwFlags[ENV_LOOP]) { // Normal loop active start = insEnv[insEnv.nLoopStart].tick; end = insEnv[insEnv.nLoopEnd].tick + 1; } else { // Limit to last envelope point start = end = insEnv.back().tick; if(position > end) { // Env of envelope endReached = true; } } if(position >= end) { position = start; } } if(envType == ENV_VOLUME && endReached) { // Special handling for volume envelopes at end of envelope if((GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) || (chn.dwFlags[CHN_KEYOFF] && GetType() != MOD_TYPE_MDL)) { chn.dwFlags.set(CHN_NOTEFADE); } if(insEnv.back().value == 0 && (chn.nMasterChn > 0 || (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)))) { // Stop channel if the last envelope node is silent anyway. chn.dwFlags.set(CHN_NOTEFADE); chn.nFadeOutVol = 0; chn.nRealVolume = 0; chn.nCalcVolume = 0; } } chnEnv.nEnvPosition = position + (m_playBehaviour[kITEnvelopePositionHandling] ? 1 : 0); } void CSoundFile::IncrementEnvelopePositions(ModChannel &chn) const { if (chn.isFirstTick && GetType() == MOD_TYPE_MED) return; IncrementEnvelopePosition(chn, ENV_VOLUME); IncrementEnvelopePosition(chn, ENV_PANNING); IncrementEnvelopePosition(chn, ENV_PITCH); } void CSoundFile::ProcessInstrumentFade(ModChannel &chn, int &vol) const { // FadeOut volume if(chn.dwFlags[CHN_NOTEFADE] && chn.pModInstrument != nullptr) { const ModInstrument *pIns = chn.pModInstrument; uint32 fadeout = pIns->nFadeOut; if (fadeout) { chn.nFadeOutVol -= fadeout * 2; if (chn.nFadeOutVol <= 0) chn.nFadeOutVol = 0; vol = (vol * chn.nFadeOutVol) / 65536; } else if (!chn.nFadeOutVol) { vol = 0; } } } void CSoundFile::ProcessPitchPanSeparation(int32 &pan, int note, const ModInstrument &instr) { if(!instr.nPPS || note == NOTE_NONE) return; // with PPS = 16 / PPC = C-5, E-6 will pan hard right (and D#6 will not) int32 delta = (note - instr.nPPC - NOTE_MIN) * instr.nPPS / 2; pan = Clamp(pan + delta, 0, 256); } void CSoundFile::ProcessPanbrello(ModChannel &chn) const { int pdelta = chn.nPanbrelloOffset; if(chn.rowCommand.command == CMD_PANBRELLO) { uint32 panpos; // IT compatibility: IT has its own, more precise tables if(m_playBehaviour[kITVibratoTremoloPanbrello]) panpos = chn.nPanbrelloPos; else panpos = ((chn.nPanbrelloPos + 0x10) >> 2); pdelta = GetVibratoDelta(chn.nPanbrelloType, panpos); // IT compatibility: Sample-and-hold style random panbrello (tremolo and vibrato don't use this mechanism in IT) // Test case: RandomWaveform.it if(m_playBehaviour[kITSampleAndHoldPanbrello] && chn.nPanbrelloType == 3) { if(chn.nPanbrelloPos == 0 || chn.nPanbrelloPos >= chn.nPanbrelloSpeed) { chn.nPanbrelloPos = 0; chn.nPanbrelloRandomMemory = static_cast(pdelta); } chn.nPanbrelloPos++; pdelta = chn.nPanbrelloRandomMemory; } else { chn.nPanbrelloPos += chn.nPanbrelloSpeed; } // IT compatibility: Panbrello effect is active until next note or panning command. // Test case: PanbrelloHold.it if(m_playBehaviour[kITPanbrelloHold]) { chn.nPanbrelloOffset = static_cast(pdelta); } } if(pdelta) { pdelta = ((pdelta * (int)chn.nPanbrelloDepth) + 2) / 8; pdelta += chn.nRealPan; chn.nRealPan = Clamp(pdelta, 0, 256); } } void CSoundFile::ProcessArpeggio(CHANNELINDEX nChn, int32 &period, Tuning::NOTEINDEXTYPE &arpeggioSteps) { ModChannel &chn = m_PlayState.Chn[nChn]; #ifndef NO_PLUGINS // Plugin arpeggio if(chn.pModInstrument && chn.pModInstrument->nMixPlug && !chn.pModInstrument->dwFlags[INS_MUTE] && !chn.dwFlags[CHN_MUTE | CHN_SYNCMUTE]) { const ModInstrument *pIns = chn.pModInstrument; IMixPlugin *pPlugin = m_MixPlugins[pIns->nMixPlug - 1].pMixPlugin; if(pPlugin) { uint8 step = 0; const bool arpOnRow = (chn.rowCommand.command == CMD_ARPEGGIO); const ModCommand::NOTE lastNote = ModCommand::IsNote(chn.nLastNote) ? static_cast(pIns->NoteMap[chn.nLastNote - NOTE_MIN]) : static_cast(NOTE_NONE); if(arpOnRow) { switch(m_PlayState.m_nTickCount % 3) { case 1: step = chn.nArpeggio >> 4; break; case 2: step = chn.nArpeggio & 0x0F; break; } chn.nArpeggioBaseNote = lastNote; } // Trigger new note: // - If there's an arpeggio on this row and // - the note to trigger is not the same as the previous arpeggio note or // - a pattern note has just been triggered on this tick // - If there's no arpeggio // - but an arpeggio note is still active and // - there's no note stop or new note that would stop it anyway if((arpOnRow && chn.nArpeggioLastNote != chn.nArpeggioBaseNote + step && (!m_SongFlags[SONG_FIRSTTICK] || !chn.rowCommand.IsNote())) || (!arpOnRow && chn.rowCommand.note == NOTE_NONE && chn.nArpeggioLastNote != NOTE_NONE)) SendMIDINote(nChn, chn.nArpeggioBaseNote + step, static_cast(chn.nVolume)); // Stop note: // - If some arpeggio note is still registered or // - When starting an arpeggio on a row with no other note on it, stop some possibly still playing note. if(chn.nArpeggioLastNote != NOTE_NONE) SendMIDINote(nChn, chn.nArpeggioLastNote + NOTE_MAX_SPECIAL, 0); else if(arpOnRow && m_SongFlags[SONG_FIRSTTICK] && !chn.rowCommand.IsNote() && ModCommand::IsNote(lastNote)) SendMIDINote(nChn, lastNote + NOTE_MAX_SPECIAL, 0); if(chn.rowCommand.command == CMD_ARPEGGIO) chn.nArpeggioLastNote = chn.nArpeggioBaseNote + step; else chn.nArpeggioLastNote = NOTE_NONE; } } #endif // NO_PLUGINS if(chn.nCommand == CMD_ARPEGGIO) { if(chn.HasCustomTuning()) { switch(m_PlayState.m_nTickCount % 3) { case 0: arpeggioSteps = 0; break; case 1: arpeggioSteps = chn.nArpeggio >> 4; break; case 2: arpeggioSteps = chn.nArpeggio & 0x0F; break; } chn.m_CalculateFreq = true; chn.m_ReCalculateFreqOnFirstTick = true; } else { if(GetType() == MOD_TYPE_MT2 && m_SongFlags[SONG_FIRSTTICK]) { // MT2 resets any previous portamento when an arpeggio occurs. chn.nPeriod = period = GetPeriodFromNote(chn.nNote, chn.nFineTune, chn.nC5Speed); } if(m_playBehaviour[kITArpeggio]) { //IT playback compatibility 01 & 02 // Pattern delay restarts tick counting. Not quite correct yet! const uint32 tick = m_PlayState.m_nTickCount % (m_PlayState.m_nMusicSpeed + m_PlayState.m_nFrameDelay); if(chn.nArpeggio != 0) { uint32 arpRatio = 65536; switch(tick % 3) { case 1: arpRatio = LinearSlideUpTable[(chn.nArpeggio >> 4) * 16]; break; case 2: arpRatio = LinearSlideUpTable[(chn.nArpeggio & 0x0F) * 16]; break; } if(PeriodsAreFrequencies()) period = Util::muldivr(period, arpRatio, 65536); else period = Util::muldivr(period, 65536, arpRatio); } } else if(m_playBehaviour[kFT2Arpeggio]) { // FastTracker 2: Swedish tracker logic (TM) arpeggio if(!m_SongFlags[SONG_FIRSTTICK]) { // Arpeggio is added on top of current note, but cannot do it the IT way because of // the behaviour in ArpeggioClamp.xm. // Test case: ArpSlide.xm uint32 note = 0; // The fact that arpeggio behaves in a totally fucked up way at 16 ticks/row or more is that the arpeggio offset LUT only has 16 entries in FT2. // At more than 16 ticks/row, FT2 reads into the vibrato table, which is placed right after the arpeggio table. // Test case: Arpeggio.xm int arpPos = m_PlayState.m_nMusicSpeed - (m_PlayState.m_nTickCount % m_PlayState.m_nMusicSpeed); if(arpPos > 16) arpPos = 2; else if(arpPos == 16) arpPos = 0; else arpPos %= 3; switch(arpPos) { case 1: note = (chn.nArpeggio >> 4); break; case 2: note = (chn.nArpeggio & 0x0F); break; } if(arpPos != 0) { // Arpeggio is added on top of current note, but cannot do it the IT way because of // the behaviour in ArpeggioClamp.xm. // Test case: ArpSlide.xm note += GetNoteFromPeriod(period, chn.nFineTune, chn.nC5Speed); period = GetPeriodFromNote(note, chn.nFineTune, chn.nC5Speed); // FT2 compatibility: FT2 has a different note limit for Arpeggio. // Test case: ArpeggioClamp.xm if(note >= 108 + NOTE_MIN) { period = std::max(static_cast(period), GetPeriodFromNote(108 + NOTE_MIN, 0, chn.nC5Speed)); } } } } // Other trackers else { uint32 tick = m_PlayState.m_nTickCount; // TODO other likely formats for MOD case: MED, OKT, etc uint8 note = (GetType() != MOD_TYPE_MOD) ? chn.nNote : static_cast(GetNoteFromPeriod(period, chn.nFineTune, chn.nC5Speed)); if(GetType() & (MOD_TYPE_DBM | MOD_TYPE_DIGI)) tick += 2; switch(tick % 3) { case 1: note += (chn.nArpeggio >> 4); break; case 2: note += (chn.nArpeggio & 0x0F); break; } if(note != chn.nNote || (GetType() & (MOD_TYPE_DBM | MOD_TYPE_DIGI | MOD_TYPE_STM)) || m_playBehaviour[KST3PortaAfterArpeggio]) { if(m_SongFlags[SONG_PT_MODE]) { // Weird arpeggio wrap-around in ProTracker. // Test case: ArpWraparound.mod, and the snare sound in "Jim is dead" by doh. if(note == NOTE_MIDDLEC + 24) { period = int32_max; return; } else if(note > NOTE_MIDDLEC + 24) { note -= 37; } } period = GetPeriodFromNote(note, chn.nFineTune, chn.nC5Speed); if(GetType() & (MOD_TYPE_DBM | MOD_TYPE_DIGI | MOD_TYPE_PSM | MOD_TYPE_STM | MOD_TYPE_OKT)) { // The arpeggio note offset remains effective after the end of the current row in ScreamTracker 2. // This fixes the flute lead in MORPH.STM by Skaven, pattern 27. // Note that ScreamTracker 2.24 handles arpeggio slightly differently: It only considers the lower // nibble, and switches to that note halfway through the row. chn.nPeriod = period; } else if(m_playBehaviour[KST3PortaAfterArpeggio]) { chn.nArpeggioLastNote = note; } } } } } } void CSoundFile::ProcessVibrato(CHANNELINDEX nChn, int32 &period, Tuning::RATIOTYPE &vibratoFactor) { ModChannel &chn = m_PlayState.Chn[nChn]; if(chn.dwFlags[CHN_VIBRATO]) { const bool advancePosition = !m_SongFlags[SONG_FIRSTTICK] || ((GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) && !(m_SongFlags[SONG_ITOLDEFFECTS])); if(GetType() == MOD_TYPE_669) { if(chn.nVibratoPos % 2u) { period += chn.nVibratoDepth * 167; // Already multiplied by 4, and it seems like the real factor here is 669... how original =) } chn.nVibratoPos++; return; } // IT compatibility: IT has its own, more precise tables and pre-increments the vibrato position if(advancePosition && m_playBehaviour[kITVibratoTremoloPanbrello]) chn.nVibratoPos += 4 * chn.nVibratoSpeed; int vdelta = GetVibratoDelta(chn.nVibratoType, chn.nVibratoPos); if(chn.HasCustomTuning()) { //Hack implementation: Scaling vibratofactor to [0.95; 1.05] //using figure from above tables and vibratodepth parameter vibratoFactor += 0.05f * (vdelta * chn.nVibratoDepth) / (128.0f * 60.0f); chn.m_CalculateFreq = true; chn.m_ReCalculateFreqOnFirstTick = false; if(m_PlayState.m_nTickCount + 1 == m_PlayState.m_nMusicSpeed) chn.m_ReCalculateFreqOnFirstTick = true; } else { // Original behaviour if(m_SongFlags.test_all(SONG_FIRSTTICK | SONG_PT_MODE) || ((GetType() & (MOD_TYPE_DIGI | MOD_TYPE_DBM)) && m_SongFlags[SONG_FIRSTTICK])) { // ProTracker doesn't apply vibrato nor advance on the first tick. // Test case: VibratoReset.mod return; } else if((GetType() & (MOD_TYPE_XM | MOD_TYPE_MOD)) && (chn.nVibratoType & 0x03) == 1) { // FT2 compatibility: Vibrato ramp down table is upside down. // Test case: VibratoWaveforms.xm vdelta = -vdelta; } uint32 vdepth; // IT compatibility: correct vibrato depth if(m_playBehaviour[kITVibratoTremoloPanbrello]) { // Yes, vibrato goes backwards with old effects enabled! if(m_SongFlags[SONG_ITOLDEFFECTS]) { // Test case: vibrato-oldfx.it vdepth = 5; } else { // Test case: vibrato.it vdepth = 6; vdelta = -vdelta; } } else { if(m_SongFlags[SONG_S3MOLDVIBRATO]) vdepth = 5; else if(GetType() == MOD_TYPE_DTM) vdepth = 8; else if(GetType() & (MOD_TYPE_DBM | MOD_TYPE_MTM)) vdepth = 7; else if((GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) && !m_SongFlags[SONG_ITOLDEFFECTS]) vdepth = 7; else vdepth = 6; // ST3 compatibility: Do not distinguish between vibrato types in effect memory // Test case: VibratoTypeChange.s3m if(m_playBehaviour[kST3VibratoMemory] && chn.rowCommand.command == CMD_FINEVIBRATO) vdepth += 2; } vdelta = (-vdelta * static_cast(chn.nVibratoDepth)) / (1 << vdepth); DoFreqSlide(chn, period, vdelta); // Process MIDI vibrato for plugins: #ifndef NO_PLUGINS IMixPlugin *plugin = GetChannelInstrumentPlugin(m_PlayState.Chn[nChn]); if(plugin != nullptr) { // If the Pitch Wheel Depth is configured correctly (so it's the same as the plugin's PWD), // MIDI vibrato will sound identical to vibrato with linear slides enabled. int8 pwd = 2; if(chn.pModInstrument != nullptr) { pwd = chn.pModInstrument->midiPWD; } plugin->MidiVibrato(vdelta, pwd, nChn); } #endif // NO_PLUGINS } // Advance vibrato position - IT updates on every tick, unless "old effects" are enabled (in this case it only updates on non-first ticks like other trackers) // IT compatibility: IT has its own, more precise tables and pre-increments the vibrato position if(advancePosition && !m_playBehaviour[kITVibratoTremoloPanbrello]) chn.nVibratoPos += chn.nVibratoSpeed; } else if(chn.dwOldFlags[CHN_VIBRATO]) { // Stop MIDI vibrato for plugins: #ifndef NO_PLUGINS IMixPlugin *plugin = GetChannelInstrumentPlugin(m_PlayState.Chn[nChn]); if(plugin != nullptr) { plugin->MidiVibrato(0, 0, nChn); } #endif // NO_PLUGINS } } void CSoundFile::ProcessSampleAutoVibrato(ModChannel &chn, int32 &period, Tuning::RATIOTYPE &vibratoFactor, int &nPeriodFrac) const { // Sample Auto-Vibrato if(chn.pModSample != nullptr && chn.pModSample->nVibDepth) { const ModSample *pSmp = chn.pModSample; const bool hasTuning = chn.HasCustomTuning(); // In IT compatible mode, we use always frequencies, otherwise we use periods, which are upside down. // In this context, the "up" tables refer to the tables that increase frequency, and the down tables are the ones that decrease frequency. const bool useFreq = PeriodsAreFrequencies(); const uint32 (&upTable)[256] = useFreq ? LinearSlideUpTable : LinearSlideDownTable; const uint32 (&downTable)[256] = useFreq ? LinearSlideDownTable : LinearSlideUpTable; const uint32 (&fineUpTable)[16] = useFreq ? FineLinearSlideUpTable : FineLinearSlideDownTable; const uint32 (&fineDownTable)[16] = useFreq ? FineLinearSlideDownTable : FineLinearSlideUpTable; // IT compatibility: Autovibrato is so much different in IT that I just put this in a separate code block, to get rid of a dozen IsCompatibilityMode() calls. if(m_playBehaviour[kITVibratoTremoloPanbrello] && !hasTuning && GetType() != MOD_TYPE_MT2) { if(!pSmp->nVibRate) return; // Schism's autovibrato code /* X86 Assembler from ITTECH.TXT: 1) Mov AX, [SomeVariableNameRelatingToVibrato] 2) Add AL, Rate 3) AdC AH, 0 4) AH contains the depth of the vibrato as a fine-linear slide. 5) Mov [SomeVariableNameRelatingToVibrato], AX ; For the next cycle. */ const int vibpos = chn.nAutoVibPos & 0xFF; int adepth = chn.nAutoVibDepth; // (1) adepth += pSmp->nVibSweep; // (2 & 3) LimitMax(adepth, static_cast(pSmp->nVibDepth * 256u)); chn.nAutoVibDepth = adepth; // (5) adepth /= 256; // (4) chn.nAutoVibPos += pSmp->nVibRate; int vdelta; switch(pSmp->nVibType) { case VIB_RANDOM: vdelta = mpt::random(AccessPRNG()) - 0x40; break; case VIB_RAMP_DOWN: vdelta = 64 - (vibpos + 1) / 2; break; case VIB_RAMP_UP: vdelta = ((vibpos + 1) / 2) - 64; break; case VIB_SQUARE: vdelta = vibpos < 128 ? 64 : 0; break; case VIB_SINE: default: vdelta = ITSinusTable[vibpos]; break; } vdelta = (vdelta * adepth) / 64; uint32 l = std::abs(vdelta); LimitMax(period, Util::MaxValueOfType(period) / 256); period *= 256; if(vdelta < 0) { vdelta = Util::muldiv(period, downTable[l / 4u], 0x10000) - period; if (l & 0x03) { vdelta += Util::muldiv(period, fineDownTable[l & 0x03], 0x10000) - period; } } else { vdelta = Util::muldiv(period, upTable[l / 4u], 0x10000) - period; if (l & 0x03) { vdelta += Util::muldiv(period, fineUpTable[l & 0x03], 0x10000) - period; } } period = (period + vdelta) / 256; nPeriodFrac = vdelta & 0xFF; } else { // MPT's autovibrato code if (pSmp->nVibSweep == 0 && !(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT))) { chn.nAutoVibDepth = pSmp->nVibDepth * 256; } else { // Calculate current autovibrato depth using vibsweep if (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) { chn.nAutoVibDepth += pSmp->nVibSweep * 2u; } else { if(!chn.dwFlags[CHN_KEYOFF]) { chn.nAutoVibDepth += (pSmp->nVibDepth * 256u) / pSmp->nVibSweep; } } LimitMax(chn.nAutoVibDepth, static_cast(pSmp->nVibDepth * 256u)); } chn.nAutoVibPos += pSmp->nVibRate; int vdelta; switch(pSmp->nVibType) { case VIB_RANDOM: vdelta = ModRandomTable[chn.nAutoVibPos & 0x3F]; chn.nAutoVibPos++; break; case VIB_RAMP_DOWN: vdelta = ((0x40 - (chn.nAutoVibPos / 2u)) & 0x7F) - 0x40; break; case VIB_RAMP_UP: vdelta = ((0x40 + (chn.nAutoVibPos / 2u)) & 0x7F) - 0x40; break; case VIB_SQUARE: vdelta = (chn.nAutoVibPos & 128) ? +64 : -64; break; case VIB_SINE: default: if(GetType() != MOD_TYPE_MT2) { vdelta = -ITSinusTable[chn.nAutoVibPos & 0xFF]; } else { // Fix flat-sounding pads in "another worlds" by Eternal Engine. // Vibrato starts at the maximum amplitude of the sine wave // and the vibrato frequency never decreases below the original note's frequency. vdelta = (-ITSinusTable[(chn.nAutoVibPos + 192) & 0xFF] + 64) / 2; } } int n = (vdelta * chn.nAutoVibDepth) / 256; if(hasTuning) { //Vib sweep is not taken into account here. vibratoFactor += 0.05F * pSmp->nVibDepth * vdelta / 4096.0f; //4096 == 64^2 //See vibrato for explanation. chn.m_CalculateFreq = true; /* Finestep vibrato: const float autoVibDepth = pSmp->nVibDepth * val / 4096.0f; //4096 == 64^2 vibratoFineSteps += static_cast(chn.pModInstrument->pTuning->GetFineStepCount() * autoVibDepth); chn.m_CalculateFreq = true; */ } else //Original behavior { if (GetType() != MOD_TYPE_XM) { int df1, df2; if (n < 0) { n = -n; uint32 n1 = n / 256; df1 = downTable[n1]; df2 = downTable[n1+1]; } else { uint32 n1 = n / 256; df1 = upTable[n1]; df2 = upTable[n1+1]; } n /= 4; period = Util::muldiv(period, df1 + ((df2 - df1) * (n & 0x3F) / 64), 256); nPeriodFrac = period & 0xFF; period /= 256; } else { period += (n / 64); } } //Original MPT behavior } } } void CSoundFile::ProcessRamping(ModChannel &chn) const { chn.leftRamp = chn.rightRamp = 0; LimitMax(chn.newLeftVol, int32_max >> VOLUMERAMPPRECISION); LimitMax(chn.newRightVol, int32_max >> VOLUMERAMPPRECISION); if(chn.dwFlags[CHN_VOLUMERAMP] && (chn.leftVol != chn.newLeftVol || chn.rightVol != chn.newRightVol)) { const bool rampUp = (chn.newLeftVol > chn.leftVol) || (chn.newRightVol > chn.rightVol); int32 rampLength, globalRampLength, instrRampLength = 0; rampLength = globalRampLength = (rampUp ? m_MixerSettings.GetVolumeRampUpSamples() : m_MixerSettings.GetVolumeRampDownSamples()); //XXXih: add real support for bidi ramping here if(m_playBehaviour[kFT2VolumeRamping] && (GetType() & MOD_TYPE_XM)) { // apply FT2-style super-soft volume ramping (5ms), overriding openmpt settings rampLength = globalRampLength = Util::muldivr(5, m_MixerSettings.gdwMixingFreq, 1000); } if(chn.pModInstrument != nullptr && rampUp) { instrRampLength = chn.pModInstrument->nVolRampUp; rampLength = instrRampLength ? (m_MixerSettings.gdwMixingFreq * instrRampLength / 100000) : globalRampLength; } const bool enableCustomRamp = (instrRampLength > 0); if(!rampLength) { rampLength = 1; } int32 leftDelta = ((chn.newLeftVol - chn.leftVol) * (1 << VOLUMERAMPPRECISION)); int32 rightDelta = ((chn.newRightVol - chn.rightVol) * (1 << VOLUMERAMPPRECISION)); if(!enableCustomRamp) { // Extra-smooth ramping, unless we're forced to use the default values if((chn.leftVol | chn.rightVol) && (chn.newLeftVol | chn.newRightVol) && !chn.dwFlags[CHN_FASTVOLRAMP]) { rampLength = m_PlayState.m_nBufferCount; Limit(rampLength, globalRampLength, int32(1 << (VOLUMERAMPPRECISION - 1))); } } chn.leftRamp = leftDelta / rampLength; chn.rightRamp = rightDelta / rampLength; chn.leftVol = chn.newLeftVol - ((chn.leftRamp * rampLength) / (1 << VOLUMERAMPPRECISION)); chn.rightVol = chn.newRightVol - ((chn.rightRamp * rampLength) / (1 << VOLUMERAMPPRECISION)); if (chn.leftRamp|chn.rightRamp) { chn.nRampLength = rampLength; } else { chn.dwFlags.reset(CHN_VOLUMERAMP); chn.leftVol = chn.newLeftVol; chn.rightVol = chn.newRightVol; } } else { chn.dwFlags.reset(CHN_VOLUMERAMP); chn.leftVol = chn.newLeftVol; chn.rightVol = chn.newRightVol; } chn.rampLeftVol = chn.leftVol * (1 << VOLUMERAMPPRECISION); chn.rampRightVol = chn.rightVol * (1 << VOLUMERAMPPRECISION); chn.dwFlags.reset(CHN_FASTVOLRAMP); } // Returns channel increment and frequency with FREQ_FRACBITS fractional bits std::pair CSoundFile::GetChannelIncrement(const ModChannel &chn, uint32 period, int periodFrac) const { uint32 freq; if(!chn.HasCustomTuning()) freq = GetFreqFromPeriod(period, chn.nC5Speed, periodFrac); else freq = chn.nPeriod; const ModInstrument *ins = chn.pModInstrument; if(int32 finetune = chn.microTuning; finetune != 0) { if(ins) finetune *= ins->midiPWD; if(finetune) freq = mpt::saturate_round(freq * std::pow(2.0, finetune / (12.0 * 256.0 * 128.0))); } // Applying Pitch/Tempo lock if(ins && ins->pitchToTempoLock.GetRaw()) { freq = Util::muldivr(freq, m_PlayState.m_nMusicTempo.GetRaw(), ins->pitchToTempoLock.GetRaw()); } // Avoid increment to overflow and become negative with unrealisticly high frequencies. LimitMax(freq, uint32(int32_max)); return {SamplePosition::Ratio(freq, m_MixerSettings.gdwMixingFreq << FREQ_FRACBITS), freq}; } //////////////////////////////////////////////////////////////////////////////////////////// // Handles envelopes & mixer setup bool CSoundFile::ReadNote() { #ifdef MODPLUG_TRACKER // Checking end of row ? if(m_SongFlags[SONG_PAUSED]) { m_PlayState.m_nTickCount = 0; if (!m_PlayState.m_nMusicSpeed) m_PlayState.m_nMusicSpeed = 6; if (!m_PlayState.m_nMusicTempo.GetRaw()) m_PlayState.m_nMusicTempo.Set(125); } else #endif // MODPLUG_TRACKER { if(!ProcessRow()) return false; } //////////////////////////////////////////////////////////////////////////////////// if (m_PlayState.m_nMusicTempo.GetRaw() == 0) return false; m_PlayState.m_nSamplesPerTick = GetTickDuration(m_PlayState); m_PlayState.m_nBufferCount = m_PlayState.m_nSamplesPerTick; // Master Volume + Pre-Amplification / Attenuation setup uint32 nMasterVol; { CHANNELINDEX nchn32 = Clamp(m_nChannels, CHANNELINDEX(1), CHANNELINDEX(31)); uint32 mastervol; if (m_PlayConfig.getUseGlobalPreAmp()) { int realmastervol = m_MixerSettings.m_nPreAmp; if (realmastervol > 0x80) { //Attenuate global pre-amp depending on num channels realmastervol = 0x80 + ((realmastervol - 0x80) * (nchn32 + 4)) / 16; } mastervol = (realmastervol * (m_nSamplePreAmp)) / 64; } else { //Preferred option: don't use global pre-amp at all. mastervol = m_nSamplePreAmp; } if (m_PlayConfig.getUseGlobalPreAmp()) { uint32 attenuation = #ifndef NO_AGC (m_MixerSettings.DSPMask & SNDDSP_AGC) ? PreAmpAGCTable[nchn32 / 2u] : #endif PreAmpTable[nchn32 / 2u]; if(attenuation < 1) attenuation = 1; nMasterVol = (mastervol << 7) / attenuation; } else { nMasterVol = mastervol; } } //////////////////////////////////////////////////////////////////////////////////// // Update channels data m_nMixChannels = 0; for (CHANNELINDEX nChn = 0; nChn < MAX_CHANNELS; nChn++) { ModChannel &chn = m_PlayState.Chn[nChn]; // FT2 Compatibility: Prevent notes to be stopped after a fadeout. This way, a portamento effect can pick up a faded instrument which is long enough. // This occurs for example in the bassline (channel 11) of jt_burn.xm. I hope this won't break anything else... // I also suppose this could decrease mixing performance a bit, but hey, which CPU can't handle 32 muted channels these days... :-) if(chn.dwFlags[CHN_NOTEFADE] && (!(chn.nFadeOutVol|chn.leftVol|chn.rightVol)) && !m_playBehaviour[kFT2ProcessSilentChannels]) { chn.nLength = 0; chn.nROfs = chn.nLOfs = 0; } // Check for unused channel if(chn.dwFlags[CHN_MUTE] || (nChn >= m_nChannels && !chn.nLength)) { if(nChn < m_nChannels) { // Process MIDI macros on channels that are currently muted. ProcessMacroOnChannel(nChn); } chn.nLeftVU = chn.nRightVU = 0; continue; } // Reset channel data chn.increment = SamplePosition(0); chn.nRealVolume = 0; chn.nCalcVolume = 0; chn.nRampLength = 0; //Aux variables Tuning::RATIOTYPE vibratoFactor = 1; Tuning::NOTEINDEXTYPE arpeggioSteps = 0; const ModInstrument *pIns = chn.pModInstrument; // Calc Frequency int32 period = 0; // Also process envelopes etc. when there's a plugin on this channel, for possible fake automation using volume and pan data. // We only care about master channels, though, since automation only "happens" on them. const bool samplePlaying = (chn.nPeriod && chn.nLength); const bool plugAssigned = (nChn < m_nChannels) && (ChnSettings[nChn].nMixPlugin || (chn.pModInstrument != nullptr && chn.pModInstrument->nMixPlug)); if (samplePlaying || plugAssigned) { int vol = chn.nVolume; int insVol = chn.nInsVol; // This is the "SV * IV" value in ITTECH.TXT ProcessVolumeSwing(chn, m_playBehaviour[kITSwingBehaviour] ? insVol : vol); ProcessPanningSwing(chn); ProcessTremolo(chn, vol); ProcessTremor(nChn, vol); // Clip volume and multiply (extend to 14 bits) Limit(vol, 0, 256); vol <<= 6; // Process Envelopes if (pIns) { if(m_playBehaviour[kITEnvelopePositionHandling]) { // In IT compatible mode, envelope position indices are shifted by one for proper envelope pausing, // so we have to update the position before we actually process the envelopes. // When using MPT behaviour, we get the envelope position for the next tick while we are still calculating the current tick, // which then results in wrong position information when the envelope is paused on the next row. // Test cases: s77.it IncrementEnvelopePositions(chn); } ProcessVolumeEnvelope(chn, vol); ProcessInstrumentFade(chn, vol); ProcessPanningEnvelope(chn); if(!m_playBehaviour[kITPitchPanSeparation] && chn.nNote != NOTE_NONE && chn.pModInstrument && chn.pModInstrument->nPPS != 0) ProcessPitchPanSeparation(chn.nRealPan, chn.nNote, *chn.pModInstrument); } else { // No Envelope: key off => note cut if(chn.dwFlags[CHN_NOTEFADE]) // 1.41-: CHN_KEYOFF|CHN_NOTEFADE { chn.nFadeOutVol = 0; vol = 0; } } if(chn.isPaused) vol = 0; // vol is 14-bits if (vol) { // IMPORTANT: chn.nRealVolume is 14 bits !!! // -> Util::muldiv( 14+8, 6+6, 18); => RealVolume: 14-bit result (22+12-20) if(chn.dwFlags[CHN_SYNCMUTE]) { chn.nRealVolume = 0; } else if (m_PlayConfig.getGlobalVolumeAppliesToMaster()) { // Don't let global volume affect level of sample if // Global volume is going to be applied to master output anyway. chn.nRealVolume = Util::muldiv(vol * MAX_GLOBAL_VOLUME, chn.nGlobalVol * insVol, 1 << 20); } else { chn.nRealVolume = Util::muldiv(vol * m_PlayState.m_nGlobalVolume, chn.nGlobalVol * insVol, 1 << 20); } } chn.nCalcVolume = vol; // Update calculated volume for MIDI macros // ST3 only clamps the final output period, but never the channel's internal period. // Test case: PeriodLimit.s3m if (chn.nPeriod < m_nMinPeriod && GetType() != MOD_TYPE_S3M && !PeriodsAreFrequencies()) { chn.nPeriod = m_nMinPeriod; } else if(chn.nPeriod >= m_nMaxPeriod && m_playBehaviour[kApplyUpperPeriodLimit] && !PeriodsAreFrequencies()) { // ...but on the other hand, ST3's SoundBlaster driver clamps the maximum channel period. // Test case: PeriodLimitUpper.s3m chn.nPeriod = m_nMaxPeriod; } if(m_playBehaviour[kFT2Periods]) Clamp(chn.nPeriod, 1, 31999); period = chn.nPeriod; // When glissando mode is set to semitones, clamp to the next halftone. if((chn.dwFlags & (CHN_GLISSANDO | CHN_PORTAMENTO)) == (CHN_GLISSANDO | CHN_PORTAMENTO) && (!m_SongFlags[SONG_PT_MODE] || (chn.rowCommand.IsPortamento() && !m_SongFlags[SONG_FIRSTTICK]))) { if(period != chn.cachedPeriod) { // Only recompute this whole thing in case the base period has changed. chn.cachedPeriod = period; chn.glissandoPeriod = GetPeriodFromNote(GetNoteFromPeriod(period, chn.nFineTune, chn.nC5Speed), chn.nFineTune, chn.nC5Speed); } period = chn.glissandoPeriod; } ProcessArpeggio(nChn, period, arpeggioSteps); // Preserve Amiga freq limits. // In ST3, the frequency is always clamped to periods 113 to 856, while in ProTracker, // the limit is variable, depending on the finetune of the sample. // The int32_max test is for the arpeggio wrap-around in ProcessArpeggio(). // Test case: AmigaLimits.s3m, AmigaLimitsFinetune.mod if(m_SongFlags[SONG_AMIGALIMITS | SONG_PT_MODE] && period != int32_max) { int limitLow = 113 * 4, limitHigh = 856 * 4; if(GetType() != MOD_TYPE_S3M) { const int tableOffset = XM2MODFineTune(chn.nFineTune) * 12; limitLow = ProTrackerTunedPeriods[tableOffset + 11] / 2; limitHigh = ProTrackerTunedPeriods[tableOffset] * 2; // Amiga cannot actually keep up with lower periods if(limitLow < 113 * 4) limitLow = 113 * 4; } Limit(period, limitLow, limitHigh); Limit(chn.nPeriod, limitLow, limitHigh); } ProcessPanbrello(chn); } // IT Compatibility: Ensure that there is no pan swing, panbrello, panning envelopes, etc. applied on surround channels. // Test case: surround-pan.it if(chn.dwFlags[CHN_SURROUND] && !m_SongFlags[SONG_SURROUNDPAN] && m_playBehaviour[kITNoSurroundPan]) { chn.nRealPan = 128; } // Now that all relevant envelopes etc. have been processed, we can parse the MIDI macro data. ProcessMacroOnChannel(nChn); // After MIDI macros have been processed, we can also process the pitch / filter envelope and other pitch-related things. if(samplePlaying) { int cutoff = ProcessPitchFilterEnvelope(chn, period); if(cutoff >= 0 && chn.dwFlags[CHN_ADLIB] && m_opl) { // Cutoff doubles as modulator intensity for FM instruments m_opl->Volume(nChn, static_cast(cutoff / 4), true); } } if(chn.rowCommand.volcmd == VOLCMD_VIBRATODEPTH && (chn.rowCommand.command == CMD_VIBRATO || chn.rowCommand.command == CMD_VIBRATOVOL || chn.rowCommand.command == CMD_FINEVIBRATO)) { if(GetType() == MOD_TYPE_XM) { // XM Compatibility: Vibrato should be advanced twice (but not added up) if both volume-column and effect column vibrato is present. // Effect column vibrato parameter has precedence if non-zero. // Test case: VibratoDouble.xm if(!m_SongFlags[SONG_FIRSTTICK]) chn.nVibratoPos += chn.nVibratoSpeed; } else if(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) { // IT Compatibility: Vibrato should be applied twice if both volume-colum and effect column vibrato is present. // Volume column vibrato parameter has precedence if non-zero. // Test case: VibratoDouble.it Vibrato(chn, chn.rowCommand.vol); ProcessVibrato(nChn, period, vibratoFactor); } } // Plugins may also receive vibrato ProcessVibrato(nChn, period, vibratoFactor); if(samplePlaying) { int nPeriodFrac = 0; ProcessSampleAutoVibrato(chn, period, vibratoFactor, nPeriodFrac); // Final Period // ST3 only clamps the final output period, but never the channel's internal period. // Test case: PeriodLimit.s3m if (period <= m_nMinPeriod) { if(m_playBehaviour[kST3LimitPeriod]) chn.nLength = 0; // Pattern 15 in watcha.s3m period = m_nMinPeriod; } const bool hasTuning = chn.HasCustomTuning(); if(hasTuning) { if(chn.m_CalculateFreq || (chn.m_ReCalculateFreqOnFirstTick && m_PlayState.m_nTickCount == 0)) { chn.RecalcTuningFreq(vibratoFactor, arpeggioSteps, *this); if(!chn.m_CalculateFreq) chn.m_ReCalculateFreqOnFirstTick = false; else chn.m_CalculateFreq = false; } } auto [ninc, freq] = GetChannelIncrement(chn, period, nPeriodFrac); #ifndef MODPLUG_TRACKER ninc.MulDiv(m_nFreqFactor, 65536); #endif // !MODPLUG_TRACKER if(ninc.IsZero()) { ninc.Set(0, 1); } chn.increment = ninc; if((chn.dwFlags & (CHN_ADLIB | CHN_MUTE | CHN_SYNCMUTE)) == CHN_ADLIB && m_opl) { const bool doProcess = m_playBehaviour[kOPLFlexibleNoteOff] || !chn.dwFlags[CHN_NOTEFADE] || GetType() == MOD_TYPE_S3M; if(doProcess && !(GetType() == MOD_TYPE_S3M && chn.dwFlags[CHN_KEYOFF])) { // In ST3, a sample rate of 8363 Hz is mapped to middle-C, which is 261.625 Hz in a tempered scale at A4 = 440. // Hence, we have to translate our "sample rate" into pitch. auto milliHertz = Util::muldivr_unsigned(freq, 261625, 8363 << FREQ_FRACBITS); const bool keyOff = chn.dwFlags[CHN_KEYOFF] || (chn.dwFlags[CHN_NOTEFADE] && chn.nFadeOutVol == 0); if(!m_playBehaviour[kOPLNoteStopWith0Hz] || !keyOff) m_opl->Frequency(nChn, milliHertz, keyOff, m_playBehaviour[kOPLBeatingOscillators]); } if(doProcess) { // Scale volume to OPL range (0...63). m_opl->Volume(nChn, static_cast(Util::muldivr_unsigned(chn.nCalcVolume * chn.nGlobalVol * chn.nInsVol, 63, 1 << 26)), false); chn.nRealPan = m_opl->Pan(nChn, chn.nRealPan) * 128 + 128; } // Deallocate OPL channels for notes that are most definitely never going to play again. if(const auto *ins = chn.pModInstrument; ins != nullptr && (ins->VolEnv.dwFlags & (ENV_ENABLED | ENV_LOOP | ENV_SUSTAIN)) == ENV_ENABLED && !ins->VolEnv.empty() && chn.GetEnvelope(ENV_VOLUME).nEnvPosition >= ins->VolEnv.back().tick && ins->VolEnv.back().value == 0) { m_opl->NoteCut(nChn); if(!m_playBehaviour[kOPLNoResetAtEnvelopeEnd]) chn.dwFlags.reset(CHN_ADLIB); chn.dwFlags.set(CHN_NOTEFADE); chn.nFadeOutVol = 0; } else if(m_playBehaviour[kOPLFlexibleNoteOff] && chn.dwFlags[CHN_NOTEFADE] && chn.nFadeOutVol == 0) { m_opl->NoteCut(nChn); chn.dwFlags.reset(CHN_ADLIB); } } } // Increment envelope positions if(pIns != nullptr && !m_playBehaviour[kITEnvelopePositionHandling]) { // In IT and FT2 compatible mode, envelope positions are updated above. // Test cases: s77.it, EnvLoops.xm IncrementEnvelopePositions(chn); } // Volume ramping chn.dwFlags.set(CHN_VOLUMERAMP, (chn.nRealVolume | chn.rightVol | chn.leftVol) != 0 && !chn.dwFlags[CHN_ADLIB]); constexpr uint8 VUMETER_DECAY = 4; chn.nLeftVU = (chn.nLeftVU > VUMETER_DECAY) ? (chn.nLeftVU - VUMETER_DECAY) : 0; chn.nRightVU = (chn.nRightVU > VUMETER_DECAY) ? (chn.nRightVU - VUMETER_DECAY) : 0; chn.newLeftVol = chn.newRightVol = 0; chn.pCurrentSample = (chn.pModSample && chn.pModSample->HasSampleData() && chn.nLength && chn.IsSamplePlaying()) ? chn.pModSample->samplev() : nullptr; if(chn.pCurrentSample || (chn.HasMIDIOutput() && !chn.dwFlags[CHN_KEYOFF | CHN_NOTEFADE])) { // Update VU-Meter (nRealVolume is 14-bit) uint32 vul = (chn.nRealVolume * (256-chn.nRealPan)) / (1 << 14); if (vul > 127) vul = 127; if (chn.nLeftVU > 127) chn.nLeftVU = (uint8)vul; vul /= 2; if (chn.nLeftVU < vul) chn.nLeftVU = (uint8)vul; uint32 vur = (chn.nRealVolume * chn.nRealPan) / (1 << 14); if (vur > 127) vur = 127; if (chn.nRightVU > 127) chn.nRightVU = (uint8)vur; vur /= 2; if (chn.nRightVU < vur) chn.nRightVU = (uint8)vur; } else { // Note change but no sample if (chn.nLeftVU > 128) chn.nLeftVU = 0; if (chn.nRightVU > 128) chn.nRightVU = 0; } if (chn.pCurrentSample) { #ifdef MODPLUG_TRACKER const uint32 kChnMasterVol = chn.dwFlags[CHN_EXTRALOUD] ? (uint32)m_PlayConfig.getNormalSamplePreAmp() : nMasterVol; #else const uint32 kChnMasterVol = nMasterVol; #endif // MODPLUG_TRACKER // Adjusting volumes { int32 pan = (m_MixerSettings.gnChannels >= 2) ? Clamp(chn.nRealPan, 0, 256) : 128; int32 realvol; if(m_PlayConfig.getUseGlobalPreAmp()) { realvol = (chn.nRealVolume * kChnMasterVol) / 128; } else { // Extra attenuation required here if we're bypassing pre-amp. realvol = (chn.nRealVolume * kChnMasterVol) / 256; } const PanningMode panningMode = m_PlayConfig.getPanningMode(); if(panningMode == PanningMode::SoftPanning || (panningMode == PanningMode::Undetermined && (m_MixerSettings.MixerFlags & SNDMIX_SOFTPANNING))) { if(pan < 128) { chn.newLeftVol = (realvol * 128) / 256; chn.newRightVol = (realvol * pan) / 256; } else { chn.newLeftVol = (realvol * (256 - pan)) / 256; chn.newRightVol = (realvol * 128) / 256; } } else if(panningMode == PanningMode::FT2Panning) { // FT2 uses square root panning. There is a 257-entry LUT for this, // but FT2's internal panning ranges from 0 to 255 only, meaning that // you can never truly achieve 100% right panning in FT2, only 100% left. // Test case: FT2PanLaw.xm LimitMax(pan, 255); const int panL = pan > 0 ? XMPanningTable[256 - pan] : 65536; const int panR = XMPanningTable[pan]; chn.newLeftVol = (realvol * panL) / 65536; chn.newRightVol = (realvol * panR) / 65536; } else { chn.newLeftVol = (realvol * (256 - pan)) / 256; chn.newRightVol = (realvol * pan) / 256; } } // Clipping volumes //if (chn.nNewRightVol > 0xFFFF) chn.nNewRightVol = 0xFFFF; //if (chn.nNewLeftVol > 0xFFFF) chn.nNewLeftVol = 0xFFFF; if(chn.pModInstrument && Resampling::IsKnownMode(chn.pModInstrument->resampling)) { // For defined resampling modes, use per-instrument resampling mode if set chn.resamplingMode = chn.pModInstrument->resampling; } else if(Resampling::IsKnownMode(m_nResampling)) { chn.resamplingMode = m_nResampling; } else if(m_SongFlags[SONG_ISAMIGA] && m_Resampler.m_Settings.emulateAmiga != Resampling::AmigaFilter::Off) { // Enforce Amiga resampler for Amiga modules chn.resamplingMode = SRCMODE_AMIGA; } else { // Default to global mixer settings chn.resamplingMode = m_Resampler.m_Settings.SrcMode; } if(chn.increment.IsUnity() && !(chn.dwFlags[CHN_VIBRATO] || chn.nAutoVibDepth || chn.resamplingMode == SRCMODE_AMIGA)) { // Exact sample rate match, do not interpolate at all // - unless vibrato is applied, because in this case the constant enabling and disabling // of resampling can introduce clicks (this is easily observable with a sine sample // played at the mix rate). chn.resamplingMode = SRCMODE_NEAREST; } const int extraAttenuation = m_PlayConfig.getExtraSampleAttenuation(); chn.newLeftVol /= (1 << extraAttenuation); chn.newRightVol /= (1 << extraAttenuation); // Dolby Pro-Logic Surround if(chn.dwFlags[CHN_SURROUND] && m_MixerSettings.gnChannels == 2) chn.newRightVol = -chn.newRightVol; // Checking Ping-Pong Loops if(chn.dwFlags[CHN_PINGPONGFLAG]) chn.increment.Negate(); // Setting up volume ramp ProcessRamping(chn); // Adding the channel in the channel list if(!chn.dwFlags[CHN_ADLIB]) { m_PlayState.ChnMix[m_nMixChannels++] = nChn; } } else { chn.rightVol = chn.leftVol = 0; chn.nLength = 0; // Put the channel back into the mixer for end-of-sample pop reduction if(chn.nLOfs || chn.nROfs) m_PlayState.ChnMix[m_nMixChannels++] = nChn; } chn.dwOldFlags = chn.dwFlags; } // If there are more channels being mixed than allowed, order them by volume and discard the most quiet ones if(m_nMixChannels >= m_MixerSettings.m_nMaxMixChannels) { std::partial_sort(std::begin(m_PlayState.ChnMix), std::begin(m_PlayState.ChnMix) + m_MixerSettings.m_nMaxMixChannels, std::begin(m_PlayState.ChnMix) + m_nMixChannels, [this](CHANNELINDEX i, CHANNELINDEX j) { return (m_PlayState.Chn[i].nRealVolume > m_PlayState.Chn[j].nRealVolume); }); } return true; } void CSoundFile::ProcessMacroOnChannel(CHANNELINDEX nChn) { ModChannel &chn = m_PlayState.Chn[nChn]; if(nChn < GetNumChannels()) { // TODO evaluate per-plugin macros here //ProcessMIDIMacro(m_PlayState, nChn, false, m_MidiCfg.Global[MIDIOUT_PAN]); //ProcessMIDIMacro(m_PlayState, nChn, false, m_MidiCfg.Global[MIDIOUT_VOLUME]); if((chn.rowCommand.command == CMD_MIDI && m_SongFlags[SONG_FIRSTTICK]) || chn.rowCommand.command == CMD_SMOOTHMIDI) { if(chn.rowCommand.param < 0x80) ProcessMIDIMacro(m_PlayState, nChn, (chn.rowCommand.command == CMD_SMOOTHMIDI), m_MidiCfg.SFx[chn.nActiveMacro], chn.rowCommand.param); else ProcessMIDIMacro(m_PlayState, nChn, (chn.rowCommand.command == CMD_SMOOTHMIDI), m_MidiCfg.Zxx[chn.rowCommand.param & 0x7F], chn.rowCommand.param); } } } #ifndef NO_PLUGINS void CSoundFile::ProcessMidiOut(CHANNELINDEX nChn) { ModChannel &chn = m_PlayState.Chn[nChn]; // Do we need to process MIDI? // For now there is no difference between mute and sync mute with VSTis. if(chn.dwFlags[CHN_MUTE | CHN_SYNCMUTE] || !chn.HasMIDIOutput()) return; // Get instrument info and plugin reference const ModInstrument *pIns = chn.pModInstrument; // Can't be nullptr at this point, as we have valid MIDI output. // No instrument or muted instrument? if(pIns->dwFlags[INS_MUTE]) { return; } // Check instrument plugins const PLUGINDEX nPlugin = GetBestPlugin(m_PlayState, nChn, PrioritiseInstrument, RespectMutes); IMixPlugin *pPlugin = nullptr; if(nPlugin > 0 && nPlugin <= MAX_MIXPLUGINS) { pPlugin = m_MixPlugins[nPlugin - 1].pMixPlugin; } // Couldn't find a valid plugin if(pPlugin == nullptr) return; const ModCommand::NOTE note = chn.rowCommand.note; // Check for volume commands uint8 vol = 0xFF; if(chn.rowCommand.volcmd == VOLCMD_VOLUME) { vol = std::min(chn.rowCommand.vol, uint8(64)); } else if(chn.rowCommand.command == CMD_VOLUME) { vol = std::min(chn.rowCommand.param, uint8(64)); } const bool hasVolCommand = (vol != 0xFF); if(m_playBehaviour[kMIDICCBugEmulation]) { if(note != NOTE_NONE) { ModCommand::NOTE realNote = note; if(ModCommand::IsNote(note)) realNote = pIns->NoteMap[note - NOTE_MIN]; SendMIDINote(nChn, realNote, static_cast(chn.nVolume)); } else if(hasVolCommand) { pPlugin->MidiCC(MIDIEvents::MIDICC_Volume_Fine, vol, nChn); } return; } const uint32 defaultVolume = pIns->nGlobalVol; //If new note, determine notevelocity to use. if(note != NOTE_NONE) { int32 velocity = static_cast(4 * defaultVolume); switch(pIns->pluginVelocityHandling) { case PLUGIN_VELOCITYHANDLING_CHANNEL: velocity = chn.nVolume; break; default: break; } int32 swing = chn.nVolSwing; if(m_playBehaviour[kITSwingBehaviour]) swing *= 4; velocity += swing; Limit(velocity, 0, 256); ModCommand::NOTE realNote = note; if(ModCommand::IsNote(note)) realNote = pIns->NoteMap[note - NOTE_MIN]; // Experimental VST panning //ProcessMIDIMacro(nChn, false, m_MidiCfg.Global[MIDIOUT_PAN], 0, nPlugin); SendMIDINote(nChn, realNote, static_cast(velocity)); } const bool processVolumeAlsoOnNote = (pIns->pluginVelocityHandling == PLUGIN_VELOCITYHANDLING_VOLUME); const bool hasNote = m_playBehaviour[kMIDIVolumeOnNoteOffBug] ? (note != NOTE_NONE) : ModCommand::IsNote(note); if((hasVolCommand && !hasNote) || (hasNote && processVolumeAlsoOnNote)) { switch(pIns->pluginVolumeHandling) { case PLUGIN_VOLUMEHANDLING_DRYWET: if(hasVolCommand) pPlugin->SetDryRatio(2 * vol); else pPlugin->SetDryRatio(2 * defaultVolume); break; case PLUGIN_VOLUMEHANDLING_MIDI: if(hasVolCommand) pPlugin->MidiCC(MIDIEvents::MIDICC_Volume_Coarse, std::min(uint8(127), static_cast(2 * vol)), nChn); else pPlugin->MidiCC(MIDIEvents::MIDICC_Volume_Coarse, static_cast(std::min(uint32(127), static_cast(2 * defaultVolume))), nChn); break; default: break; } } } #endif // NO_PLUGINS template MPT_FORCEINLINE void ApplyGlobalVolumeWithRamping(int32 *SoundBuffer, int32 *RearBuffer, int32 lCount, int32 m_nGlobalVolume, int32 step, int32 &m_nSamplesToGlobalVolRampDest, int32 &m_lHighResRampingGlobalVolume) { const bool isStereo = (channels >= 2); const bool hasRear = (channels >= 4); for(int pos = 0; pos < lCount; ++pos) { if(m_nSamplesToGlobalVolRampDest > 0) { // Ramping required m_lHighResRampingGlobalVolume += step; SoundBuffer[0] = Util::muldiv(SoundBuffer[0], m_lHighResRampingGlobalVolume, MAX_GLOBAL_VOLUME << VOLUMERAMPPRECISION); if constexpr(isStereo) SoundBuffer[1] = Util::muldiv(SoundBuffer[1], m_lHighResRampingGlobalVolume, MAX_GLOBAL_VOLUME << VOLUMERAMPPRECISION); if constexpr(hasRear) RearBuffer[0] = Util::muldiv(RearBuffer[0] , m_lHighResRampingGlobalVolume, MAX_GLOBAL_VOLUME << VOLUMERAMPPRECISION); else MPT_UNUSED_VARIABLE(RearBuffer); if constexpr(hasRear) RearBuffer[1] = Util::muldiv(RearBuffer[1] , m_lHighResRampingGlobalVolume, MAX_GLOBAL_VOLUME << VOLUMERAMPPRECISION); else MPT_UNUSED_VARIABLE(RearBuffer); m_nSamplesToGlobalVolRampDest--; } else { SoundBuffer[0] = Util::muldiv(SoundBuffer[0], m_nGlobalVolume, MAX_GLOBAL_VOLUME); if constexpr(isStereo) SoundBuffer[1] = Util::muldiv(SoundBuffer[1], m_nGlobalVolume, MAX_GLOBAL_VOLUME); if constexpr(hasRear) RearBuffer[0] = Util::muldiv(RearBuffer[0] , m_nGlobalVolume, MAX_GLOBAL_VOLUME); else MPT_UNUSED_VARIABLE(RearBuffer); if constexpr(hasRear) RearBuffer[1] = Util::muldiv(RearBuffer[1] , m_nGlobalVolume, MAX_GLOBAL_VOLUME); else MPT_UNUSED_VARIABLE(RearBuffer); m_lHighResRampingGlobalVolume = m_nGlobalVolume << VOLUMERAMPPRECISION; } SoundBuffer += isStereo ? 2 : 1; if constexpr(hasRear) RearBuffer += 2; } } void CSoundFile::ProcessGlobalVolume(long lCount) { // should we ramp? if(IsGlobalVolumeUnset()) { // do not ramp if no global volume was set before (which is the case at song start), to prevent audible glitches when default volume is > 0 and it is set to 0 in the first row m_PlayState.m_nGlobalVolumeDestination = m_PlayState.m_nGlobalVolume; m_PlayState.m_nSamplesToGlobalVolRampDest = 0; m_PlayState.m_nGlobalVolumeRampAmount = 0; } else if(m_PlayState.m_nGlobalVolumeDestination != m_PlayState.m_nGlobalVolume) { // User has provided new global volume // m_nGlobalVolume: the last global volume which got set e.g. by a pattern command // m_nGlobalVolumeDestination: the current target of the ramping algorithm const bool rampUp = m_PlayState.m_nGlobalVolume > m_PlayState.m_nGlobalVolumeDestination; m_PlayState.m_nGlobalVolumeDestination = m_PlayState.m_nGlobalVolume; m_PlayState.m_nSamplesToGlobalVolRampDest = m_PlayState.m_nGlobalVolumeRampAmount = rampUp ? m_MixerSettings.GetVolumeRampUpSamples() : m_MixerSettings.GetVolumeRampDownSamples(); } // calculate ramping step int32 step = 0; if (m_PlayState.m_nSamplesToGlobalVolRampDest > 0) { // Still some ramping left to do. int32 highResGlobalVolumeDestination = static_cast(m_PlayState.m_nGlobalVolumeDestination) << VOLUMERAMPPRECISION; const long delta = highResGlobalVolumeDestination - m_PlayState.m_lHighResRampingGlobalVolume; step = delta / static_cast(m_PlayState.m_nSamplesToGlobalVolRampDest); if(m_nMixLevels == MixLevels::v1_17RC2) { // Define max step size as some factor of user defined ramping value: the lower the value, the more likely the click. // If step is too big (might cause click), extend ramp length. // Warning: This increases the volume ramp length by EXTREME amounts (factors of 100 are easily reachable) // compared to the user-defined setting, so this really should not be used! int32 maxStep = std::max(int32(50), static_cast((10000 / (m_PlayState.m_nGlobalVolumeRampAmount + 1)))); while(std::abs(step) > maxStep) { m_PlayState.m_nSamplesToGlobalVolRampDest += m_PlayState.m_nGlobalVolumeRampAmount; step = delta / static_cast(m_PlayState.m_nSamplesToGlobalVolRampDest); } } } // apply volume and ramping if(m_MixerSettings.gnChannels == 1) { ApplyGlobalVolumeWithRamping<1>(MixSoundBuffer, MixRearBuffer, lCount, m_PlayState.m_nGlobalVolume, step, m_PlayState.m_nSamplesToGlobalVolRampDest, m_PlayState.m_lHighResRampingGlobalVolume); } else if(m_MixerSettings.gnChannels == 2) { ApplyGlobalVolumeWithRamping<2>(MixSoundBuffer, MixRearBuffer, lCount, m_PlayState.m_nGlobalVolume, step, m_PlayState.m_nSamplesToGlobalVolRampDest, m_PlayState.m_lHighResRampingGlobalVolume); } else if(m_MixerSettings.gnChannels == 4) { ApplyGlobalVolumeWithRamping<4>(MixSoundBuffer, MixRearBuffer, lCount, m_PlayState.m_nGlobalVolume, step, m_PlayState.m_nSamplesToGlobalVolRampDest, m_PlayState.m_lHighResRampingGlobalVolume); } } void CSoundFile::ProcessStereoSeparation(long countChunk) { ApplyStereoSeparation(MixSoundBuffer, MixRearBuffer, m_MixerSettings.gnChannels, countChunk, m_MixerSettings.m_nStereoSeparation); } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/SoundFilePlayConfig.cpp0000644000175000017500000000756013710077365022464 00000000000000/* * SoundFilePlayConfig.cpp * ----------------------- * Purpose: Configuration of sound levels, pan laws, etc... for various mix configurations. * Notes : (currently none) * Authors: Olivier Lapicque * OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Mixer.h" #include "SoundFilePlayConfig.h" OPENMPT_NAMESPACE_BEGIN CSoundFilePlayConfig::CSoundFilePlayConfig() { setVSTiVolume(1.0f); SetMixLevels(MixLevels::Compatible); } void CSoundFilePlayConfig::SetMixLevels(MixLevels mixLevelType) { switch (mixLevelType) { // Olivier's version gives us floats in [-0.5; 0.5] and slightly saturates VSTis. case MixLevels::Original: setVSTiAttenuation(1.0f); // no attenuation setIntToFloat(1.0f/static_cast(1<<28)); setFloatToInt(static_cast(1<<28)); setGlobalVolumeAppliesToMaster(false); setUseGlobalPreAmp(true); setPanningMode(PanningMode::Undetermined); setDisplayDBValues(false); setNormalSamplePreAmp(256.0f); setNormalVSTiVol(100.0f); setNormalGlobalVol(128.0f); setExtraSampleAttenuation(MIXING_ATTENUATION); break; // Ericus' version gives us floats in [-0.06;0.06] and requires attenuation to // avoid massive VSTi saturation. case MixLevels::v1_17RC1: setVSTiAttenuation(32.0f); setIntToFloat(1.0f/static_cast(0x07FFFFFFF)); setFloatToInt(static_cast(0x07FFFFFFF)); setGlobalVolumeAppliesToMaster(false); setUseGlobalPreAmp(true); setPanningMode(PanningMode::Undetermined); setDisplayDBValues(false); setNormalSamplePreAmp(256.0f); setNormalVSTiVol(100.0f); setNormalGlobalVol(128.0f); setExtraSampleAttenuation(MIXING_ATTENUATION); break; // 1.17RC2 gives us floats in [-1.0; 1.0] and hopefully plays VSTis at // the right volume... but we attenuate by 2x to approx. match sample volume. case MixLevels::v1_17RC2: setVSTiAttenuation(2.0f); setIntToFloat(1.0f/MIXING_SCALEF); setFloatToInt(MIXING_SCALEF); setGlobalVolumeAppliesToMaster(true); setUseGlobalPreAmp(true); setPanningMode(PanningMode::Undetermined); setDisplayDBValues(false); setNormalSamplePreAmp(256.0f); setNormalVSTiVol(100.0f); setNormalGlobalVol(128.0f); setExtraSampleAttenuation(MIXING_ATTENUATION); break; // 1.17RC3 ignores the horrible global, system-specific pre-amp, // treats panning as balance to avoid saturation on loud sample (and because I think it's better :), // and allows display of attenuation in decibels. default: case MixLevels::v1_17RC3: setVSTiAttenuation(1.0f); setIntToFloat(1.0f/MIXING_SCALEF); setFloatToInt(MIXING_SCALEF); setGlobalVolumeAppliesToMaster(true); setUseGlobalPreAmp(false); setPanningMode(PanningMode::SoftPanning); setDisplayDBValues(true); setNormalSamplePreAmp(128.0f); setNormalVSTiVol(128.0f); setNormalGlobalVol(256.0f); setExtraSampleAttenuation(0); break; // A mixmode that is intended to be compatible to legacy trackers (IT/FT2/etc). // This is basically derived from mixmode 1.17 RC3, with panning mode and volume levels changed. // Sample attenuation is the same as in Schism Tracker (more attenuation than with RC3, thus VSTi attenuation is also higher). case MixLevels::Compatible: case MixLevels::CompatibleFT2: setVSTiAttenuation(0.75f); setIntToFloat(1.0f/MIXING_SCALEF); setFloatToInt(MIXING_SCALEF); setGlobalVolumeAppliesToMaster(true); setUseGlobalPreAmp(false); setPanningMode(mixLevelType == MixLevels::Compatible ? PanningMode::NoSoftPanning : PanningMode::FT2Panning); setDisplayDBValues(true); setNormalSamplePreAmp(mixLevelType == MixLevels::Compatible ? 256.0f : 192.0f); setNormalVSTiVol(mixLevelType == MixLevels::Compatible ? 256.0f : 192.0f); setNormalGlobalVol(256.0f); setExtraSampleAttenuation(1); break; } } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/SoundFilePlayConfig.h0000644000175000017500000000622114052666041022115 00000000000000/* * SoundFilePlayConfig.h * --------------------- * Purpose: Configuration of sound levels, pan laws, etc... for various mix configurations. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" OPENMPT_NAMESPACE_BEGIN enum class TempoMode : uint8 { Classic = 0, Alternative = 1, Modern = 2, NumModes }; enum class MixLevels : uint8 { Original = 0, v1_17RC1 = 1, v1_17RC2 = 2, v1_17RC3 = 3, Compatible = 4, CompatibleFT2 = 5, NumMixLevels }; enum class PanningMode : uint8 { Undetermined, SoftPanning, NoSoftPanning, FT2Panning, }; // Class used to store settings for a song file. class CSoundFilePlayConfig { public: CSoundFilePlayConfig(); void SetMixLevels(MixLevels mixLevelType); //getters/setters bool getGlobalVolumeAppliesToMaster() const { return m_globalVolumeAppliesToMaster; } void setGlobalVolumeAppliesToMaster(bool inGlobalVolumeAppliesToMaster) { m_globalVolumeAppliesToMaster=inGlobalVolumeAppliesToMaster; } // user-controllable VSTi gain factor. float getVSTiVolume() const { return m_VSTiVolume; } void setVSTiVolume(float inVSTiVolume) { m_VSTiVolume = inVSTiVolume; } // default VSTi gain factor, different depending on the MPT version we're "emulating" float getVSTiAttenuation() const { return m_VSTiAttenuation; } void setVSTiAttenuation(float inVSTiAttenuation) { m_VSTiAttenuation = inVSTiAttenuation; } float getIntToFloat() const { return m_IntToFloat; } void setIntToFloat(float inIntToFloat) { m_IntToFloat = inIntToFloat; } float getFloatToInt() const { return m_FloatToInt; } void setFloatToInt(float inFloatToInt) { m_FloatToInt = inFloatToInt; } bool getUseGlobalPreAmp() const { return m_ignorePreAmp; } void setUseGlobalPreAmp(bool inUseGlobalPreAmp) { m_ignorePreAmp = inUseGlobalPreAmp; } PanningMode getPanningMode() const { return m_forceSoftPanning; } void setPanningMode(PanningMode inForceSoftPanning) { m_forceSoftPanning = inForceSoftPanning; } bool getDisplayDBValues() const { return m_displayDBValues; } void setDisplayDBValues(bool in) { m_displayDBValues = in; } // Values at which volumes are unchanged float getNormalSamplePreAmp() const { return m_normalSamplePreAmp; } void setNormalSamplePreAmp(float in) { m_normalSamplePreAmp = in; } float getNormalVSTiVol() const { return m_normalVSTiVol; } void setNormalVSTiVol(float in) { m_normalVSTiVol = in; } float getNormalGlobalVol() const { return m_normalGlobalVol; } void setNormalGlobalVol(float in) { m_normalGlobalVol = in; } // Extra sample attenuation in bits int getExtraSampleAttenuation() const { return m_extraAttenuation; } void setExtraSampleAttenuation(int attn) { m_extraAttenuation = attn; } protected: float m_IntToFloat; float m_FloatToInt; float m_VSTiAttenuation; float m_VSTiVolume; float m_normalSamplePreAmp; float m_normalVSTiVol; float m_normalGlobalVol; int m_extraAttenuation; PanningMode m_forceSoftPanning; bool m_globalVolumeAppliesToMaster; bool m_ignorePreAmp; bool m_displayDBValues; }; OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Tables.cpp0000644000175000017500000011136014147260247020022 00000000000000/* * Tables.cpp * ---------- * Purpose: Effect, interpolation, data and other pre-calculated tables. * Notes : (currently none) * Authors: Olivier Lapicque * OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Tables.h" #include "Sndfile.h" #include "Resampler.h" #include "WindowedFIR.h" #include OPENMPT_NAMESPACE_BEGIN ///////////////////////////////////////////////////////////////////////////// // Note Name Tables const mpt::uchar NoteNamesSharp[12][4] = { UL_("C-"), UL_("C#"), UL_("D-"), UL_("D#"), UL_("E-"), UL_("F-"), UL_("F#"), UL_("G-"), UL_("G#"), UL_("A-"), UL_("A#"), UL_("B-") }; const mpt::uchar NoteNamesFlat[12][4] = { UL_("C-"), UL_("Db"), UL_("D-"), UL_("Eb"), UL_("E-"), UL_("F-"), UL_("Gb"), UL_("G-"), UL_("Ab"), UL_("A-"), UL_("Bb"), UL_("B-") }; /////////////////////////////////////////////////////////// // File Formats Information (name, extension, etc) struct ModFormatInfo { MODTYPE format; // MOD_TYPE_XXXX const mpt::uchar *name; // "ProTracker" const char *extension; // "mod" }; // Note: Formats with identical extensions must be grouped together. static constexpr ModFormatInfo modFormatInfo[] = { { MOD_TYPE_MPT, UL_("OpenMPT"), "mptm" }, { MOD_TYPE_MOD, UL_("ProTracker"), "mod" }, { MOD_TYPE_S3M, UL_("Scream Tracker 3"), "s3m" }, { MOD_TYPE_XM, UL_("FastTracker 2"), "xm" }, { MOD_TYPE_IT, UL_("Impulse Tracker"), "it" }, { MOD_TYPE_669, UL_("Composer 669 / UNIS 669"), "669" }, { MOD_TYPE_AMF0, UL_("ASYLUM Music Format"), "amf" }, { MOD_TYPE_AMF, UL_("DSMI Advanced Music Format"), "amf" }, { MOD_TYPE_AMS, UL_("Extreme's Tracker"), "ams" }, { MOD_TYPE_AMS, UL_("Velvet Studio"), "ams" }, { MOD_TYPE_S3M, UL_("CDFM / Composer 670"), "c67" }, { MOD_TYPE_DBM, UL_("DigiBooster Pro"), "dbm" }, { MOD_TYPE_DIGI, UL_("DigiBooster"), "digi" }, { MOD_TYPE_DMF, UL_("X-Tracker"), "dmf" }, { MOD_TYPE_DSM, UL_("DSIK Format"), "dsm" }, { MOD_TYPE_MOD, UL_("Digital Symphony"), "dsym" }, { MOD_TYPE_DTM, UL_("Digital Tracker"), "dtm" }, { MOD_TYPE_FAR, UL_("Farandole Composer"), "far" }, { MOD_TYPE_S3M, UL_("FM Tracker"), "fmt" }, { MOD_TYPE_IMF, UL_("Imago Orpheus"), "imf" }, { MOD_TYPE_MOD, UL_("Ice Tracker"), "ice" }, #ifdef MPT_EXTERNAL_SAMPLES { MOD_TYPE_IT, UL_("Impulse Tracker Project"), "itp" }, #endif { MOD_TYPE_J2B, UL_("Galaxy Sound System"), "j2b" }, { MOD_TYPE_MOD, UL_("Soundtracker"), "m15" }, { MOD_TYPE_MDL, UL_("Digitrakker"), "mdl" }, { MOD_TYPE_MED, UL_("OctaMED"), "med" }, { MOD_TYPE_SFX, UL_("MultiMedia Sound"), "mms" }, { MOD_TYPE_MT2, UL_("MadTracker 2"), "mt2" }, { MOD_TYPE_MTM, UL_("MultiTracker"), "mtm" }, { MOD_TYPE_MOD, UL_("Karl Morton Music Format"), "mus" }, { MOD_TYPE_MOD, UL_("NoiseTracker"), "nst" }, { MOD_TYPE_OKT, UL_("Oktalyzer"), "okt" }, { MOD_TYPE_PLM, UL_("Disorder Tracker 2"), "plm" }, { MOD_TYPE_PSM, UL_("Epic Megagames MASI"), "psm" }, { MOD_TYPE_MOD, UL_("ProTracker"), "pt36" }, { MOD_TYPE_PTM, UL_("PolyTracker"), "ptm" }, { MOD_TYPE_SFX, UL_("SoundFX"), "sfx" }, { MOD_TYPE_SFX, UL_("SoundFX"), "sfx2" }, { MOD_TYPE_MOD, UL_("SoundTracker 2.6"), "st26" }, { MOD_TYPE_MOD, UL_("Soundtracker"), "stk" }, { MOD_TYPE_STM, UL_("Scream Tracker 2"), "stm" }, { MOD_TYPE_STM, UL_("Scream Tracker Music Interface Kit"), "stx" }, { MOD_TYPE_STP, UL_("Soundtracker Pro II"), "stp" }, { MOD_TYPE_MPT, UL_("Symphonie"), "symmod"}, { MOD_TYPE_ULT, UL_("UltraTracker"), "ult" }, { MOD_TYPE_MOD, UL_("Mod's Grave"), "wow" }, // converted formats (no MODTYPE) { MOD_TYPE_NONE, UL_("General Digital Music"), "gdm" }, { MOD_TYPE_NONE, UL_("Un4seen MO3"), "mo3" }, { MOD_TYPE_NONE, UL_("OggMod FastTracker 2"), "oxm" }, #ifndef NO_ARCHIVE_SUPPORT // Compressed modules { MOD_TYPE_MOD, UL_("Compressed ProTracker"), "mdz" }, { MOD_TYPE_MOD, UL_("Compressed Module"), "mdr" }, { MOD_TYPE_S3M, UL_("Compressed Scream Tracker 3"), "s3z" }, { MOD_TYPE_XM, UL_("Compressed FastTracker 2"), "xmz" }, { MOD_TYPE_IT, UL_("Compressed Impulse Tracker"), "itz" }, { MOD_TYPE_MPT, UL_("Compressed OpenMPT"), "mptmz" }, #endif }; struct ModContainerInfo { MODCONTAINERTYPE format; // MOD_CONTAINERTYPE_XXXX const mpt::uchar *name; // "Unreal Music" const char *extension; // "umx" }; static constexpr ModContainerInfo modContainerInfo[] = { // Container formats { MOD_CONTAINERTYPE_UMX, UL_("Unreal Music"), "umx" }, { MOD_CONTAINERTYPE_XPK, UL_("XPK packed"), "xpk" }, { MOD_CONTAINERTYPE_PP20, UL_("PowerPack PP20"), "ppm" }, { MOD_CONTAINERTYPE_MMCMP, UL_("Music Module Compressor"), "mmcmp" }, #ifdef MODPLUG_TRACKER { MOD_CONTAINERTYPE_WAV, UL_("Wave"), "wav" }, { MOD_CONTAINERTYPE_UAX, UL_("Unreal Sounds"), "uax" }, #endif }; #ifdef MODPLUG_TRACKER static constexpr ModFormatInfo otherFormatInfo[] = { { MOD_TYPE_MID, UL_("MIDI"), "mid" }, { MOD_TYPE_MID, UL_("MIDI"), "rmi" }, { MOD_TYPE_MID, UL_("MIDI"), "smf" } }; #endif std::vector CSoundFile::GetSupportedExtensions(bool otherFormats) { std::vector exts; for(const auto &formatInfo : modFormatInfo) { // Avoid dupes in list if(exts.empty() || strcmp(formatInfo.extension, exts.back())) { exts.push_back(formatInfo.extension); } } for(const auto &containerInfo : modContainerInfo) { // Avoid dupes in list if(exts.empty() || strcmp(containerInfo.extension, exts.back())) { exts.push_back(containerInfo.extension); } } #ifdef MODPLUG_TRACKER if(otherFormats) { for(const auto &formatInfo : otherFormatInfo) { exts.push_back(formatInfo.extension); } } #else MPT_UNREFERENCED_PARAMETER(otherFormats); #endif return exts; } static bool IsEqualExtension(std::string_view a, std::string_view b) { if(a.length() != b.length()) { return false; } return mpt::CompareNoCaseAscii(a, b) == 0; } bool CSoundFile::IsExtensionSupported(std::string_view ext) { if(ext.length() == 0) { return false; } for(const auto &formatInfo : modFormatInfo) { if(IsEqualExtension(ext, formatInfo.extension)) { return true; } } for(const auto &containerInfo : modContainerInfo) { if(IsEqualExtension(ext, containerInfo.extension)) { return true; } } return false; } mpt::ustring CSoundFile::ModContainerTypeToString(MODCONTAINERTYPE containertype) { for(const auto &containerInfo : modContainerInfo) { if(containerInfo.format == containertype) { return mpt::ToUnicode(mpt::Charset::UTF8, containerInfo.extension); } } return mpt::ustring(); } mpt::ustring CSoundFile::ModContainerTypeToTracker(MODCONTAINERTYPE containertype) { std::set retvals; mpt::ustring retval; for(const auto &containerInfo : modContainerInfo) { if(containerInfo.format == containertype) { mpt::ustring name = containerInfo.name; if(retvals.insert(name).second) { if(!retval.empty()) { retval += U_(" / "); } retval += name; } } } return retval; } /////////////////////////////////////////////////////////////////////// const uint8 ImpulseTrackerPortaVolCmd[16] = { 0x00, 0x01, 0x04, 0x08, 0x10, 0x20, 0x40, 0x60, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; // Period table for ProTracker octaves (1-7 in FastTracker 2, also used for file I/O): const uint16 ProTrackerPeriodTable[7*12] = { 2*1712,2*1616,2*1524,2*1440,2*1356,2*1280,2*1208,2*1140,2*1076,2*1016,2*960,2*906, 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 }; const uint16 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 }; // Table for Invert Loop and Funk Repeat effects (EFx, .MOD only) const uint8 ModEFxTable[16] = { 0, 5, 6, 7, 8, 10, 11, 13, 16, 19, 22, 26, 32, 43, 64, 128 }; // S3M C-4 periods const uint16 FreqS3MTable[12] = { 1712,1616,1524,1440,1356,1280,1208,1140,1076,1016,960,907 }; // S3M FineTune frequencies const uint16 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 const int8 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 }; // Random wave table const int8 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 }; // Impulse Tracker sinus table (ITTECH.TXT) const int8 ITSinusTable[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, }; // volume fade tables for Retrig Note: const int8 retrigTable1[16] = { 0, 0, 0, 0, 0, 0, 10, 8, 0, 0, 0, 0, 0, 0, 24, 32 }; const int8 retrigTable2[16] = { 0, -1, -2, -4, -8, -16, 0, 0, 0, 1, 2, 4, 8, 16, 0, 0 }; const uint16 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 }; // floor(8363 * 64 * 2**(-n/768)) // 768 = 64 period steps for 12 notes // Table is for highest possible octave const uint32 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 }; // round(65536 * 2**(n/768)) // 768 = 64 extra-fine finetune steps for 12 notes // Table content is in 16.16 format const uint32 FineLinearSlideUpTable[16] = { 65536, 65595, 65654, 65714, 65773, 65832, 65892, 65951, 66011, 66071, 66130, 66190, 66250, 66309, 66369, 66429 }; // round(65536 * 2**(-n/768)) // 768 = 64 extra-fine finetune steps for 12 notes // Table content is in 16.16 format // Note that there are a few errors in this table (typos?), but well, this table comes straight from Impulse Tracker's source... // Entry 0 (65535) should be 65536 (this value is unused and most likely stored this way so that it fits in a 16-bit integer) // Entry 11 (64888) should be 64889 - rounding error? // Entry 15 (64645) should be 64655 - typo? const uint32 FineLinearSlideDownTable[16] = { 65535, 65477, 65418, 65359, 65300, 65241, 65182, 65123, 65065, 65006, 64947, 64888, 64830, 64772, 64713, 64645 }; // round(65536 * 2**(n/192)) // 192 = 16 finetune steps for 12 notes // Table content is in 16.16 format const uint32 LinearSlideUpTable[256] = { 65536, 65773, 66011, 66250, 66489, 66730, 66971, 67213, 67456, 67700, 67945, 68191, 68438, 68685, 68933, 69183, 69433, 69684, 69936, 70189, 70443, 70698, 70953, 71210, 71468, 71726, 71985, 72246, 72507, 72769, 73032, 73297, 73562, 73828, 74095, 74363, 74632, 74902, 75172, 75444, 75717, 75991, 76266, 76542, 76819, 77096, 77375, 77655, 77936, 78218, 78501, 78785, 79069, 79355, 79642, 79930, 80220, 80510, 80801, 81093, 81386, 81681, 81976, 82273, 82570, 82869, 83169, 83469, 83771, 84074, 84378, 84683, 84990, 85297, 85606, 85915, 86226, 86538, 86851, 87165, 87480, 87796, 88114, 88433, 88752, 89073, 89396, 89719, 90043, 90369, 90696, 91024, 91353, 91684, 92015, 92348, 92682, 93017, 93354, 93691, 94030, 94370, 94711, 95054, 95398, 95743, 96089, 96436, 96785, 97135, 97487, 97839, 98193, 98548, 98905, 99262, 99621, 99982, 100343, 100706, 101070, 101436, 101803, 102171, 102540, 102911, 103283, 103657, 104032, 104408, 104786, 105165, 105545, 105927, 106310, 106694, 107080, 107468, 107856, 108246, 108638, 109031, 109425, 109821, 110218, 110617, 111017, 111418, 111821, 112226, 112631, 113039, 113448, 113858, 114270, 114683, 115098, 115514, 115932, 116351, 116772, 117194, 117618, 118043, 118470, 118899, 119329, 119760, 120194, 120628, 121065, 121502, 121942, 122383, 122825, 123270, 123715, 124163, 124612, 125063, 125515, 125969, 126425, 126882, 127341, 127801, 128263, 128727, 129193, 129660, 130129, 130600, 131072, 131546, 132022, 132499, 132978, 133459, 133942, 134427, 134913, 135401, 135890, 136382, 136875, 137370, 137867, 138366, 138866, 139368, 139872, 140378, 140886, 141395, 141907, 142420, 142935, 143452, 143971, 144491, 145014, 145539, 146065, 146593, 147123, 147655, 148189, 148725, 149263, 149803, 150345, 150889, 151434, 151982, 152532, 153083, 153637, 154193, 154750, 155310, 155872, 156435, 157001, 157569, 158139, 158711, 159285, 159861, 160439, 161019, 161602, 162186, 162773, 163361, 163952, 164545 }; // round(65536 * 2**(-n/192)) // 192 = 16 finetune steps for 12 notes // Table content is in 16.16 format const uint32 LinearSlideDownTable[256] = { 65536, 65300, 65065, 64830, 64596, 64364, 64132, 63901, 63670, 63441, 63212, 62984, 62757, 62531, 62306, 62081, 61858, 61635, 61413, 61191, 60971, 60751, 60532, 60314, 60097, 59880, 59664, 59449, 59235, 59022, 58809, 58597, 58386, 58176, 57966, 57757, 57549, 57341, 57135, 56929, 56724, 56519, 56316, 56113, 55911, 55709, 55508, 55308, 55109, 54910, 54713, 54515, 54319, 54123, 53928, 53734, 53540, 53347, 53155, 52963, 52773, 52582, 52393, 52204, 52016, 51829, 51642, 51456, 51270, 51085, 50901, 50718, 50535, 50353, 50172, 49991, 49811, 49631, 49452, 49274, 49097, 48920, 48743, 48568, 48393, 48218, 48044, 47871, 47699, 47527, 47356, 47185, 47015, 46846, 46677, 46509, 46341, 46174, 46008, 45842, 45677, 45512, 45348, 45185, 45022, 44859, 44698, 44537, 44376, 44216, 44057, 43898, 43740, 43582, 43425, 43269, 43113, 42958, 42803, 42649, 42495, 42342, 42189, 42037, 41886, 41735, 41584, 41434, 41285, 41136, 40988, 40840, 40693, 40547, 40400, 40255, 40110, 39965, 39821, 39678, 39535, 39392, 39250, 39109, 38968, 38828, 38688, 38548, 38409, 38271, 38133, 37996, 37859, 37722, 37586, 37451, 37316, 37181, 37047, 36914, 36781, 36648, 36516, 36385, 36254, 36123, 35993, 35863, 35734, 35605, 35477, 35349, 35221, 35095, 34968, 34842, 34716, 34591, 34467, 34343, 34219, 34095, 33973, 33850, 33728, 33607, 33486, 33365, 33245, 33125, 33005, 32887, 32768, 32650, 32532, 32415, 32298, 32182, 32066, 31950, 31835, 31720, 31606, 31492, 31379, 31266, 31153, 31041, 30929, 30817, 30706, 30596, 30485, 30376, 30266, 30157, 30048, 29940, 29832, 29725, 29618, 29511, 29405, 29299, 29193, 29088, 28983, 28879, 28774, 28671, 28567, 28464, 28362, 28260, 28158, 28056, 27955, 27855, 27754, 27654, 27554, 27455, 27356, 27258, 27159, 27062, 26964, 26867, 26770, 26674, 26577, 26482, 26386, 26291, 26196, 26102 }; // FT2's square root panning law LUT. // Formula to generate this table: round(65536 * sqrt(n / 256)) const uint16 XMPanningTable[256] = { 0, 4096, 5793, 7094, 8192, 9159, 10033, 10837, 11585, 12288, 12953, 13585, 14189, 14768, 15326, 15864, 16384, 16888, 17378, 17854, 18318, 18770, 19212, 19644, 20066, 20480, 20886, 21283, 21674, 22058, 22435, 22806, 23170, 23530, 23884, 24232, 24576, 24915, 25249, 25580, 25905, 26227, 26545, 26859, 27170, 27477, 27780, 28081, 28378, 28672, 28963, 29251, 29537, 29819, 30099, 30377, 30652, 30924, 31194, 31462, 31727, 31991, 32252, 32511, 32768, 33023, 33276, 33527, 33776, 34024, 34270, 34514, 34756, 34996, 35235, 35472, 35708, 35942, 36175, 36406, 36636, 36864, 37091, 37316, 37540, 37763, 37985, 38205, 38424, 38642, 38858, 39073, 39287, 39500, 39712, 39923, 40132, 40341, 40548, 40755, 40960, 41164, 41368, 41570, 41771, 41972, 42171, 42369, 42567, 42763, 42959, 43154, 43348, 43541, 43733, 43925, 44115, 44305, 44494, 44682, 44869, 45056, 45242, 45427, 45611, 45795, 45977, 46160, 46341, 46522, 46702, 46881, 47059, 47237, 47415, 47591, 47767, 47942, 48117, 48291, 48465, 48637, 48809, 48981, 49152, 49322, 49492, 49661, 49830, 49998, 50166, 50332, 50499, 50665, 50830, 50995, 51159, 51323, 51486, 51649, 51811, 51972, 52134, 52294, 52454, 52614, 52773, 52932, 53090, 53248, 53405, 53562, 53719, 53874, 54030, 54185, 54340, 54494, 54647, 54801, 54954, 55106, 55258, 55410, 55561, 55712, 55862, 56012, 56162, 56311, 56459, 56608, 56756, 56903, 57051, 57198, 57344, 57490, 57636, 57781, 57926, 58071, 58215, 58359, 58503, 58646, 58789, 58931, 59073, 59215, 59357, 59498, 59639, 59779, 59919, 60059, 60199, 60338, 60477, 60615, 60753, 60891, 61029, 61166, 61303, 61440, 61576, 61712, 61848, 61984, 62119, 62254, 62388, 62523, 62657, 62790, 62924, 63057, 63190, 63323, 63455, 63587, 63719, 63850, 63982, 64113, 64243, 64374, 64504, 64634, 64763, 64893, 65022, 65151, 65279, 65408, }; // IT Vibrato -> OpenMPT/XM VibratoType const uint8 AutoVibratoIT2XM[8] = { VIB_SINE, VIB_RAMP_DOWN, VIB_SQUARE, VIB_RANDOM, VIB_RAMP_UP, 0, 0, 0 }; // OpenMPT/XM VibratoType -> IT Vibrato const uint8 AutoVibratoXM2IT[8] = { 0, 2, 4, 1, 3, 0, 0, 0 }; // Reversed sinc coefficients for 4x256 taps polyphase FIR resampling filter (SchismTracker's lutgen.c should generate a very similar table, but it's more precise) const int16 CResampler::FastSincTable[256*4] = { // Cubic Spline 0, 16384, 0, 0, -31, 16383, 32, 0, -63, 16381, 65, 0, -93, 16378, 100, -1, -124, 16374, 135, -1, -153, 16368, 172, -3, -183, 16361, 209, -4, -211, 16353, 247, -5, -240, 16344, 287, -7, -268, 16334, 327, -9, -295, 16322, 368, -12, -322, 16310, 410, -14, -348, 16296, 453, -17, -374, 16281, 497, -20, -400, 16265, 541, -23, -425, 16248, 587, -26, -450, 16230, 634, -30, -474, 16210, 681, -33, -497, 16190, 729, -37, -521, 16168, 778, -41, -543, 16145, 828, -46, -566, 16121, 878, -50, -588, 16097, 930, -55, -609, 16071, 982, -60, -630, 16044, 1035, -65, -651, 16016, 1089, -70, -671, 15987, 1144, -75, -691, 15957, 1199, -81, -710, 15926, 1255, -87, -729, 15894, 1312, -93, -748, 15861, 1370, -99, -766, 15827, 1428, -105, -784, 15792, 1488, -112, -801, 15756, 1547, -118, -818, 15719, 1608, -125, -834, 15681, 1669, -132, -850, 15642, 1731, -139, -866, 15602, 1794, -146, -881, 15561, 1857, -153, -896, 15520, 1921, -161, -911, 15477, 1986, -168, -925, 15434, 2051, -176, -939, 15390, 2117, -184, -952, 15344, 2184, -192, -965, 15298, 2251, -200, -978, 15251, 2319, -208, -990, 15204, 2387, -216, -1002, 15155, 2456, -225, -1014, 15106, 2526, -234, -1025, 15055, 2596, -242, -1036, 15004, 2666, -251, -1046, 14952, 2738, -260, -1056, 14899, 2810, -269, -1066, 14846, 2882, -278, -1075, 14792, 2955, -287, -1084, 14737, 3028, -296, -1093, 14681, 3102, -306, -1102, 14624, 3177, -315, -1110, 14567, 3252, -325, -1118, 14509, 3327, -334, -1125, 14450, 3403, -344, -1132, 14390, 3480, -354, -1139, 14330, 3556, -364, -1145, 14269, 3634, -374, -1152, 14208, 3712, -384, -1157, 14145, 3790, -394, -1163, 14082, 3868, -404, -1168, 14018, 3947, -414, -1173, 13954, 4027, -424, -1178, 13889, 4107, -434, -1182, 13823, 4187, -445, -1186, 13757, 4268, -455, -1190, 13690, 4349, -465, -1193, 13623, 4430, -476, -1196, 13555, 4512, -486, -1199, 13486, 4594, -497, -1202, 13417, 4676, -507, -1204, 13347, 4759, -518, -1206, 13276, 4842, -528, -1208, 13205, 4926, -539, -1210, 13134, 5010, -550, -1211, 13061, 5094, -560, -1212, 12989, 5178, -571, -1212, 12915, 5262, -581, -1213, 12842, 5347, -592, -1213, 12767, 5432, -603, -1213, 12693, 5518, -613, -1213, 12617, 5603, -624, -1212, 12542, 5689, -635, -1211, 12466, 5775, -645, -1210, 12389, 5862, -656, -1209, 12312, 5948, -667, -1208, 12234, 6035, -677, -1206, 12156, 6122, -688, -1204, 12078, 6209, -698, -1202, 11999, 6296, -709, -1200, 11920, 6384, -720, -1197, 11840, 6471, -730, -1194, 11760, 6559, -740, -1191, 11679, 6647, -751, -1188, 11598, 6735, -761, -1184, 11517, 6823, -772, -1181, 11436, 6911, -782, -1177, 11354, 6999, -792, -1173, 11271, 7088, -802, -1168, 11189, 7176, -812, -1164, 11106, 7265, -822, -1159, 11022, 7354, -832, -1155, 10939, 7442, -842, -1150, 10855, 7531, -852, -1144, 10771, 7620, -862, -1139, 10686, 7709, -872, -1134, 10602, 7798, -882, -1128, 10516, 7886, -891, -1122, 10431, 7975, -901, -1116, 10346, 8064, -910, -1110, 10260, 8153, -919, -1103, 10174, 8242, -929, -1097, 10088, 8331, -938, -1090, 10001, 8420, -947, -1083, 9915, 8508, -956, -1076, 9828, 8597, -965, -1069, 9741, 8686, -973, -1062, 9654, 8774, -982, -1054, 9566, 8863, -991, -1047, 9479, 8951, -999, -1039, 9391, 9039, -1007, -1031, 9303, 9127, -1015, -1024, 9216, 9216, -1024, -1015, 9127, 9303, -1031, -1007, 9039, 9391, -1039, -999, 8951, 9479, -1047, -991, 8863, 9566, -1054, -982, 8774, 9654, -1062, -973, 8686, 9741, -1069, -965, 8597, 9828, -1076, -956, 8508, 9915, -1083, -947, 8420, 10001, -1090, -938, 8331, 10088, -1097, -929, 8242, 10174, -1103, -919, 8153, 10260, -1110, -910, 8064, 10346, -1116, -901, 7975, 10431, -1122, -891, 7886, 10516, -1128, -882, 7798, 10602, -1134, -872, 7709, 10686, -1139, -862, 7620, 10771, -1144, -852, 7531, 10855, -1150, -842, 7442, 10939, -1155, -832, 7354, 11022, -1159, -822, 7265, 11106, -1164, -812, 7176, 11189, -1168, -802, 7088, 11271, -1173, -792, 6999, 11354, -1177, -782, 6911, 11436, -1181, -772, 6823, 11517, -1184, -761, 6735, 11598, -1188, -751, 6647, 11679, -1191, -740, 6559, 11760, -1194, -730, 6471, 11840, -1197, -720, 6384, 11920, -1200, -709, 6296, 11999, -1202, -698, 6209, 12078, -1204, -688, 6122, 12156, -1206, -677, 6035, 12234, -1208, -667, 5948, 12312, -1209, -656, 5862, 12389, -1210, -645, 5775, 12466, -1211, -635, 5689, 12542, -1212, -624, 5603, 12617, -1213, -613, 5518, 12693, -1213, -603, 5432, 12767, -1213, -592, 5347, 12842, -1213, -581, 5262, 12915, -1212, -571, 5178, 12989, -1212, -560, 5094, 13061, -1211, -550, 5010, 13134, -1210, -539, 4926, 13205, -1208, -528, 4842, 13276, -1206, -518, 4759, 13347, -1204, -507, 4676, 13417, -1202, -497, 4594, 13486, -1199, -486, 4512, 13555, -1196, -476, 4430, 13623, -1193, -465, 4349, 13690, -1190, -455, 4268, 13757, -1186, -445, 4187, 13823, -1182, -434, 4107, 13889, -1178, -424, 4027, 13954, -1173, -414, 3947, 14018, -1168, -404, 3868, 14082, -1163, -394, 3790, 14145, -1157, -384, 3712, 14208, -1152, -374, 3634, 14269, -1145, -364, 3556, 14330, -1139, -354, 3480, 14390, -1132, -344, 3403, 14450, -1125, -334, 3327, 14509, -1118, -325, 3252, 14567, -1110, -315, 3177, 14624, -1102, -306, 3102, 14681, -1093, -296, 3028, 14737, -1084, -287, 2955, 14792, -1075, -278, 2882, 14846, -1066, -269, 2810, 14899, -1056, -260, 2738, 14952, -1046, -251, 2666, 15004, -1036, -242, 2596, 15055, -1025, -234, 2526, 15106, -1014, -225, 2456, 15155, -1002, -216, 2387, 15204, -990, -208, 2319, 15251, -978, -200, 2251, 15298, -965, -192, 2184, 15344, -952, -184, 2117, 15390, -939, -176, 2051, 15434, -925, -168, 1986, 15477, -911, -161, 1921, 15520, -896, -153, 1857, 15561, -881, -146, 1794, 15602, -866, -139, 1731, 15642, -850, -132, 1669, 15681, -834, -125, 1608, 15719, -818, -118, 1547, 15756, -801, -112, 1488, 15792, -784, -105, 1428, 15827, -766, -99, 1370, 15861, -748, -93, 1312, 15894, -729, -87, 1255, 15926, -710, -81, 1199, 15957, -691, -75, 1144, 15987, -671, -70, 1089, 16016, -651, -65, 1035, 16044, -630, -60, 982, 16071, -609, -55, 930, 16097, -588, -50, 878, 16121, -566, -46, 828, 16145, -543, -41, 778, 16168, -521, -37, 729, 16190, -497, -33, 681, 16210, -474, -30, 634, 16230, -450, -26, 587, 16248, -425, -23, 541, 16265, -400, -20, 497, 16281, -374, -17, 453, 16296, -348, -14, 410, 16310, -322, -12, 368, 16322, -295, -9, 327, 16334, -268, -7, 287, 16344, -240, -5, 247, 16353, -211, -4, 209, 16361, -183, -3, 172, 16368, -153, -1, 135, 16374, -124, -1, 100, 16378, -93, 0, 65, 16381, -63, 0, 32, 16383, -31, }; ///////////////////////////////////////////////////////////////////////////////////////////// // Compute Bessel function Izero(y) using a series approximation double Izero(double y) { double s = 1, ds = 1, d = 0; do { d = d + 2; ds = ds * (y * y) / (d * d); s = s + ds; } while(ds > 1E-7 * s); return s; } static void getsinc(SINC_TYPE *psinc, double beta, double cutoff) { if(cutoff >= 0.999) { // Avoid mixer overflows. // 1.0 itself does not make much sense. cutoff = 0.999; } const double izeroBeta = Izero(beta); const double kPi = 4.0 * std::atan(1.0) * cutoff; for(int isrc = 0; isrc < 8 * SINC_PHASES; isrc++) { double fsinc; int ix = 7 - (isrc & 7); ix = (ix * SINC_PHASES) + (isrc >> 3); if(ix == (4 * SINC_PHASES)) { fsinc = 1.0; } else { const double x = (double)(ix - (4 * SINC_PHASES)) * (double)(1.0 / SINC_PHASES); const double xPi = x * kPi; fsinc = std::sin(xPi) * Izero(beta * std::sqrt(1 - x * x * (1.0 / 16.0))) / (izeroBeta * xPi); // Kaiser window } double coeff = fsinc * cutoff; #ifdef MPT_INTMIXER *psinc++ = mpt::saturate_round(coeff * (1 << SINC_QUANTSHIFT)); #else *psinc++ = static_cast(coeff); #endif } } #ifdef MODPLUG_TRACKER bool CResampler::StaticTablesInitialized = false; SINC_TYPE CResampler::gKaiserSinc[SINC_PHASES * 8]; // Upsampling SINC_TYPE CResampler::gDownsample13x[SINC_PHASES * 8]; // Downsample 1.333x SINC_TYPE CResampler::gDownsample2x[SINC_PHASES * 8]; // Downsample 2x Paula::BlepTables CResampler::blepTables; // Amiga BLEP resampler #ifndef MPT_INTMIXER mixsample_t CResampler::FastSincTablef[256 * 4]; // Cubic spline LUT #endif // !defined(MPT_INTMIXER) #endif // MODPLUG_TRACKER void CResampler::InitFloatmixerTables() { #ifdef MPT_BUILD_FUZZER // Creating resampling tables can take a little while which we really should not spend // when fuzzing OpenMPT for crashes and hangs. This content of the tables is not really // relevant for any kind of possible crashes or hangs. return; #endif // MPT_BUILD_FUZZER #ifndef MPT_INTMIXER // Prepare fast sinc coefficients for floating point mixer for(std::size_t i = 0; i < std::size(FastSincTable); i++) { FastSincTablef[i] = static_cast(FastSincTable[i] * mixsample_t(1.0f / 16384.0f)); } #endif // !defined(MPT_INTMIXER) } void CResampler::InitializeTablesFromScratch(bool force) { bool initParameterIndependentTables = false; if(force) { initParameterIndependentTables = true; } #ifdef MODPLUG_TRACKER initParameterIndependentTables = !StaticTablesInitialized; #endif // MODPLUG_TRACKER MPT_MAYBE_CONSTANT_IF(initParameterIndependentTables) { InitFloatmixerTables(); blepTables.InitTables(); getsinc(gKaiserSinc, 9.6377, 0.97); getsinc(gDownsample13x, 8.5, 0.5); getsinc(gDownsample2x, 2.7625, 0.425); #ifdef MODPLUG_TRACKER StaticTablesInitialized = true; #endif // MODPLUG_TRACKER } if((m_OldSettings == m_Settings) && !force) { return; } m_WindowedFIR.InitTable(m_Settings.gdWFIRCutoff, m_Settings.gbWFIRType); m_OldSettings = m_Settings; } #ifdef MPT_RESAMPLER_TABLES_CACHED static const CResampler & GetCachedResampler() { static CResampler s_CachedResampler(true); return s_CachedResampler; } void CResampler::InitializeTablesFromCache() { const CResampler & s_CachedResampler = GetCachedResampler(); InitFloatmixerTables(); std::copy(s_CachedResampler.gKaiserSinc, s_CachedResampler.gKaiserSinc + SINC_PHASES*8, gKaiserSinc); std::copy(s_CachedResampler.gDownsample13x, s_CachedResampler.gDownsample13x + SINC_PHASES*8, gDownsample13x); std::copy(s_CachedResampler.gDownsample2x, s_CachedResampler.gDownsample2x + SINC_PHASES*8, gDownsample2x); std::copy(s_CachedResampler.m_WindowedFIR.lut, s_CachedResampler.m_WindowedFIR.lut + WFIR_LUTLEN*WFIR_WIDTH, m_WindowedFIR.lut); blepTables = s_CachedResampler.blepTables; } #endif // MPT_RESAMPLER_TABLES_CACHED #ifdef MPT_RESAMPLER_TABLES_CACHED_ONSTARTUP struct ResampleCacheInitializer { ResampleCacheInitializer() { GetCachedResampler(); } }; #if MPT_COMPILER_CLANG #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wglobal-constructors" #endif // MPT_COMPILER_CLANG static ResampleCacheInitializer g_ResamplerCachePrimer; #if MPT_COMPILER_CLANG #pragma clang diagnostic pop #endif // MPT_COMPILER_CLANG #endif // MPT_RESAMPLER_TABLES_CACHED_ONSTARTUP OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Tables.h0000644000175000017500000000260414052666041017464 00000000000000/* * Tables.h * -------- * Purpose: Effect, interpolation, data and other pre-calculated tables. * Notes : (currently none) * Authors: Olivier Lapicque * OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" OPENMPT_NAMESPACE_BEGIN // Compute Bessel function Izero(y) using a series approximation double Izero(double y); extern const mpt::uchar NoteNamesSharp[12][4]; extern const mpt::uchar NoteNamesFlat[12][4]; extern const uint8 ImpulseTrackerPortaVolCmd[16]; extern const uint16 ProTrackerPeriodTable[7*12]; extern const uint16 ProTrackerTunedPeriods[16*12]; extern const uint8 ModEFxTable[16]; extern const uint16 FreqS3MTable[12]; extern const uint16 S3MFineTuneTable[16]; extern const int8 ModSinusTable[64]; extern const int8 ModRandomTable[64]; extern const int8 ITSinusTable[256]; extern const int8 retrigTable1[16]; extern const int8 retrigTable2[16]; extern const uint16 XMPeriodTable[104]; extern const uint32 XMLinearTable[768]; extern const uint32 FineLinearSlideUpTable[16]; extern const uint32 FineLinearSlideDownTable[16]; extern const uint32 LinearSlideUpTable[256]; extern const uint32 LinearSlideDownTable[256]; extern const uint16 XMPanningTable[256]; extern const uint8 AutoVibratoIT2XM[8]; extern const uint8 AutoVibratoXM2IT[8]; OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Tagging.cpp0000644000175000017500000000133613707346470020175 00000000000000/* * tagging.cpp * ----------- * Purpose: Structure holding a superset of tags for all supported output sample or stream files or types. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Tagging.h" #include "../common/version.h" OPENMPT_NAMESPACE_BEGIN void FileTags::SetEncoder() { encoder = Version::Current().GetOpenMPTVersionString(); } mpt::ustring GetSampleNameFromTags(const FileTags &tags) { mpt::ustring result; if(tags.artist.empty()) { result = tags.title; } else { result = MPT_UFORMAT("{} (by {})")(tags.title, tags.artist); } return result; } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/Tagging.h0000644000175000017500000000133714052666041017634 00000000000000/* * Tagging.h * --------- * Purpose: Structure holding a superset of tags for all supported output sample or stream files or types. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include OPENMPT_NAMESPACE_BEGIN struct FileTags { mpt::ustring encoder; mpt::ustring title; mpt::ustring comments; mpt::ustring bpm; mpt::ustring artist; mpt::ustring album; mpt::ustring trackno; mpt::ustring year; mpt::ustring url; mpt::ustring genre; void SetEncoder(); }; mpt::ustring GetSampleNameFromTags(const FileTags &tags); OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/TinyFFT.cpp0000644000175000017500000000640013575445376020105 00000000000000/* * TinyFFT.cpp * ----------- * Purpose: A simple FFT implementation for power-of-two FFTs * Notes : This is a C++ adaption of Ryuhei Mori's BSD 2-clause licensed TinyFFT * available from https://github.com/ryuhei-mori/tinyfft * Authors: Ryuhei Mori * OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "TinyFFT.h" OPENMPT_NAMESPACE_BEGIN void TinyFFT::GenerateTwiddleFactors(uint32 i, uint32 b, std::complex z) { if(b == 0) w[i] = z; else { GenerateTwiddleFactors(i, b >> 1, z); GenerateTwiddleFactors(i | b, b >> 1, z * w[b]); } } TinyFFT::TinyFFT(const uint32 fftSize) : w(std::size_t(1) << (fftSize - 1)) , k(fftSize) { const uint32 m = 1 << k; constexpr double PI2_ = 6.28318530717958647692; const double arg = -PI2_ / m; for(uint32 i = 1, j = m / 4; j; i <<= 1, j >>= 1) { w[i] = std::exp(I * (arg * j)); } GenerateTwiddleFactors(0, m / 4, 1); } uint32 TinyFFT::Size() const noexcept { return 1 << k; } // Computes in-place FFT of size 2^k of A, result is in bit-reversed order. void TinyFFT::FFT(std::vector> &A) const { MPT_ASSERT(A.size() == (std::size_t(1) << k)); const uint32 m = 1 << k; uint32 u = 1; uint32 v = m / 4; if(k & 1) { for(uint32 j = 0; j < m / 2; j++) { auto Ajv = A[j + (m / 2)]; A[j + (m / 2)] = A[j] - Ajv; A[j] += Ajv; } u <<= 1; v >>= 1; } for(uint32 i = k & ~1; i > 0; i -= 2) { for(uint32 jh = 0; jh < u; jh++) { auto wj = w[jh << 1]; auto wj2 = w[jh]; auto wj3 = wj2 * wj; for(uint32 j = jh << i, je = j + v; j < je; j++) { auto tmp0 = A[j]; auto tmp1 = wj * A[j + v]; auto tmp2 = wj2 * A[j + 2 * v]; auto tmp3 = wj3 * A[j + 3 * v]; auto ttmp0 = tmp0 + tmp2; auto ttmp2 = tmp0 - tmp2; auto ttmp1 = tmp1 + tmp3; auto ttmp3 = -I * (tmp1 - tmp3); A[j] = ttmp0 + ttmp1; A[j + v] = ttmp0 - ttmp1; A[j + 2 * v] = ttmp2 + ttmp3; A[j + 3 * v] = ttmp2 - ttmp3; } } u <<= 2; v >>= 2; } } // Computes in-place IFFT of size 2^k of A, input is expected to be in bit-reversed order. void TinyFFT::IFFT(std::vector> &A) const { MPT_ASSERT(A.size() == (std::size_t(1) << k)); const uint32 m = 1 << k; uint32 u = m / 4; uint32 v = 1; for(uint32 i = 2; i <= k; i += 2) { for(uint32 jh = 0; jh < u; jh++) { auto wj = std::conj(w[jh << 1]); auto wj2 = std::conj(w[jh]); auto wj3 = wj2 * wj; for(uint32 j = jh << i, je = j + v; j < je; j++) { auto tmp0 = A[j]; auto tmp1 = A[j + v]; auto tmp2 = A[j + 2 * v]; auto tmp3 = A[j + 3 * v]; auto ttmp0 = tmp0 + tmp1; auto ttmp1 = tmp0 - tmp1; auto ttmp2 = tmp2 + tmp3; auto ttmp3 = I * (tmp2 - tmp3); A[j] = ttmp0 + ttmp2; A[j + v] = wj * (ttmp1 + ttmp3); A[j + 2 * v] = wj2 * (ttmp0 - ttmp2); A[j + 3 * v] = wj3 * (ttmp1 - ttmp3); } } u >>= 2; v <<= 2; } if(k & 1) { for(uint32 j = 0; j < m / 2; j++) { auto Ajv = A[j + (m / 2)]; A[j + (m / 2)] = A[j] - Ajv; A[j] += Ajv; } } } void TinyFFT::Normalize(std::vector> &data) { const double s = static_cast(data.size()); for(auto &v : data) v /= s; } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/TinyFFT.h0000644000175000017500000000224114052666041017532 00000000000000/* * TinyFFT.h * --------- * Purpose: A simple FFT implementation for power-of-two FFTs * Notes : This is a C++ adaption of Ryuhei Mori's BSD 2-clause licensed TinyFFT * available from https://github.com/ryuhei-mori/tinyfft * Authors: Ryuhei Mori * OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include OPENMPT_NAMESPACE_BEGIN class TinyFFT { static constexpr std::complex I{0.0, 1.0}; std::vector> w; // Pre-computed twiddle factors const uint32 k; // log2 of FFT size void GenerateTwiddleFactors(uint32 i, uint32 b, std::complex z); public: TinyFFT(const uint32 fftSize); uint32 Size() const noexcept; // Computes in-place FFT of size 2^k of A, result is in bit-reversed order. void FFT(std::vector> &A) const; // Computes in-place IFFT of size 2^k of A, input is expected to be in bit-reversed order. void IFFT(std::vector> &A) const; static void Normalize(std::vector> &data); }; OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/tuningbase.h0000644000175000017500000000373114052666041020413 00000000000000/* * tuningbase.h * ------------ * Purpose: Alternative sample tuning. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include OPENMPT_NAMESPACE_BEGIN namespace Tuning { enum class SerializationResult : int { Success = 1, NoMagic = 0, Failure = -1 }; using NOTEINDEXTYPE = int16; // Some signed integer-type. using UNOTEINDEXTYPE = uint16; // Unsigned NOTEINDEXTYPE. using RATIOTYPE = float32; // Some 'real figure' type able to present ratios. If changing RATIOTYPE, serialization methods may need modifications. // Counter of steps between notes. If there is no 'finetune'(finestepcount == 0), // then 'step difference' between notes is the // same as differences in NOTEINDEXTYPE. In a way similar to ticks and rows in pattern - // ticks <-> STEPINDEX, rows <-> NOTEINDEX using STEPINDEXTYPE = int32; using USTEPINDEXTYPE = uint32; struct NoteRange { NOTEINDEXTYPE first; NOTEINDEXTYPE last; }; // Derived from old IsStepCountRangeSufficient(), this is actually a more // sensible value than what was calculated in earlier versions. inline constexpr STEPINDEXTYPE FINESTEPCOUNT_MAX = 0xffff; inline constexpr auto NOTEINDEXTYPE_MIN = std::numeric_limits::min(); inline constexpr auto NOTEINDEXTYPE_MAX = std::numeric_limits::max(); inline constexpr auto UNOTEINDEXTYPE_MAX = std::numeric_limits::max(); inline constexpr auto STEPINDEXTYPE_MIN = std::numeric_limits::min(); inline constexpr auto STEPINDEXTYPE_MAX = std::numeric_limits::max(); inline constexpr auto USTEPINDEXTYPE_MAX = std::numeric_limits::max(); enum class Type : uint16 { GENERAL = 0, GROUPGEOMETRIC = 1, GEOMETRIC = 3, }; class CTuning; } // namespace Tuning typedef Tuning::CTuning CTuning; OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/tuningCollection.cpp0000644000175000017500000001737714056354456022152 00000000000000/* * tuningCollection.cpp * -------------------- * Purpose: Alternative sample tuning collection class. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "tuningcollection.h" #include "mpt/io/io.hpp" #include "mpt/io/io_stdstream.hpp" #include "../common/serialization_utils.h" #include #include "../common/mptFileIO.h" #include "Loaders.h" #ifdef MODPLUG_TRACKER #include "../mptrack/TrackerSettings.h" #endif //MODPLUG_TRACKER OPENMPT_NAMESPACE_BEGIN namespace Tuning { /* Version history: 2->3: Serialization revamp(August 2007) 1->2: Sizetypes of string serialisation from size_t(uint32) to uint8. (March 2007) */ namespace CTuningS11n { void ReadStr(std::istream &iStrm, mpt::ustring &ustr, const std::size_t dummy, mpt::Charset charset); void WriteStr(std::ostream &oStrm, const mpt::ustring &ustr); } // namespace CTuningS11n using namespace CTuningS11n; static void ReadTuning(std::istream &iStrm, CTuningCollection &Tc, const std::size_t dummy, mpt::Charset defaultCharset) { MPT_UNREFERENCED_PARAMETER(dummy); Tc.AddTuning(iStrm, defaultCharset); } static void WriteTuning(std::ostream& oStrm, const CTuning& t) { t.Serialize(oStrm); } CTuning* CTuningCollection::GetTuning(const mpt::ustring &name) { for(std::size_t i = 0; iGetName() == name) { return m_Tunings[i].get(); } } return nullptr; } const CTuning* CTuningCollection::GetTuning(const mpt::ustring &name) const { for(std::size_t i = 0; iGetName() == name) { return m_Tunings[i].get(); } } return nullptr; } Tuning::SerializationResult CTuningCollection::Serialize(std::ostream& oStrm, const mpt::ustring &name) const { srlztn::SsbWrite ssb(oStrm); ssb.BeginWrite("TC", 3); // version ssb.WriteItem(int8(1), "UTF8"); ssb.WriteItem(name, "0", &WriteStr); uint16 dummyEditMask = 0xffff; ssb.WriteItem(dummyEditMask, "1"); const size_t tcount = m_Tunings.size(); for(size_t i = 0; i(inStrm, beginMarker); if(beginMarker != MagicBE("TCSH")) // Magic is reversed in file, hence BE return Tuning::SerializationResult::NoMagic; //2. version int32 version = 0; mpt::IO::ReadIntLE(inStrm, version); if(version > 2 || version < 1) return Tuning::SerializationResult::Failure; //3. Name if(version < 2) { std::string name; if(!mpt::IO::ReadSizedStringLE(inStrm, name, 256)) return Tuning::SerializationResult::Failure; uname = mpt::ToUnicode(defaultCharset, name); } else { std::string name; if(!mpt::IO::ReadSizedStringLE(inStrm, name)) return Tuning::SerializationResult::Failure; uname = mpt::ToUnicode(defaultCharset, name); } //4. Editmask int16 em = 0; mpt::IO::ReadIntLE(inStrm, em); //Not assigning the value yet, for if it sets some property const, //further loading might fail. //5. Tunings { uint32 s = 0; mpt::IO::ReadIntLE(inStrm, s); if(s > 50) return Tuning::SerializationResult::Failure; for(size_t i = 0; i(inStrm, endMarker); if(endMarker != MagicBE("TCSF")) // Magic is reversed in file, hence BE return Tuning::SerializationResult::Failure; return Tuning::SerializationResult::Success; } bool CTuningCollection::Remove(const CTuning *pT) { const auto it = std::find_if(m_Tunings.begin(), m_Tunings.end(), [&] (const std::unique_ptr & upT) -> bool { return upT.get() == pT; } ); if(it == m_Tunings.end()) { return false; } m_Tunings.erase(it); return true; } bool CTuningCollection::Remove(const std::size_t i) { if(i >= m_Tunings.size()) { return false; } m_Tunings.erase(m_Tunings.begin() + i); return true; } CTuning* CTuningCollection::AddTuning(std::unique_ptr pT) { if(m_Tunings.size() >= s_nMaxTuningCount) { return nullptr; } if(!pT) { return nullptr; } CTuning *result = pT.get(); m_Tunings.push_back(std::move(pT)); return result; } CTuning* CTuningCollection::AddTuning(std::istream &inStrm, mpt::Charset defaultCharset) { if(m_Tunings.size() >= s_nMaxTuningCount) { return nullptr; } if(!inStrm.good()) { return nullptr; } std::unique_ptr pT = CTuning::CreateDeserializeOLD(inStrm, defaultCharset); if(!pT) { pT = CTuning::CreateDeserialize(inStrm, defaultCharset); } if(!pT) { return nullptr; } CTuning *result = pT.get(); m_Tunings.push_back(std::move(pT)); return result; } #ifdef MODPLUG_TRACKER bool UnpackTuningCollection(const CTuningCollection &tc, const mpt::PathString &prefix) { bool error = false; auto numberFmt = mpt::FormatSpec().Dec().FillNul().Width(1 + static_cast(std::log10(tc.GetNumTunings()))); for(std::size_t i = 0; i < tc.GetNumTunings(); ++i) { const CTuning & tuning = *(tc.GetTuning(i)); mpt::PathString fn; fn += prefix; mpt::ustring tuningName = tuning.GetName(); if(tuningName.empty()) { tuningName = U_("untitled"); } SanitizeFilename(tuningName); fn += mpt::PathString::FromUnicode(MPT_UFORMAT("{} - {}")(mpt::ufmt::fmt(i + 1, numberFmt), tuningName)); fn += mpt::PathString::FromUTF8(CTuning::s_FileExtension); if(fn.FileOrDirectoryExists()) { error = true; } else { mpt::SafeOutputFile sfout(fn, std::ios::binary, mpt::FlushModeFromBool(TrackerSettings::Instance().MiscFlushFileBuffersOnSave)); if(tuning.Serialize(sfout) != Tuning::SerializationResult::Success) { error = true; } } } return !error; } #endif } // namespace Tuning OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/tuningcollection.h0000644000175000017500000000516614052666041021640 00000000000000/* * tuningCollection.h * ------------------ * Purpose: Alternative sample tuning collection class. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "tuning.h" #include #include OPENMPT_NAMESPACE_BEGIN namespace Tuning { class CTuningCollection { public: static constexpr char s_FileExtension[4] = ".tc"; // OpenMPT <= 1.26 had to following limits: // * 255 built-in tunings (only 2 were ever actually provided) // * 255 local tunings // * 255 tune-specific tunings // As 1.27 copies all used tunings into the module, the limit of 255 is no // longer sufficient. In the worst case scenario, the module contains 255 // unused tunings and uses 255 local ones. In addition to that, allow the // user to additionally import both built-in tunings. // Older OpenMPT versions will silently skip loading tunings beyond index // 255. static constexpr size_t s_nMaxTuningCount = 255 + 255 + 2 ; public: // returns observer ptr if successful CTuning* AddTuning(std::unique_ptr pT); CTuning* AddTuning(std::istream &inStrm, mpt::Charset defaultCharset); bool Remove(const std::size_t i); bool Remove(const CTuning *pT); std::size_t GetNumTunings() const { return m_Tunings.size(); } CTuning* GetTuning(std::size_t i) { if(i >= m_Tunings.size()) { return nullptr; } return m_Tunings[i].get(); } const CTuning* GetTuning(std::size_t i) const { if (i >= m_Tunings.size()) { return nullptr; } return m_Tunings[i].get(); } CTuning* GetTuning(const mpt::ustring &name); const CTuning* GetTuning(const mpt::ustring &name) const; Tuning::SerializationResult Serialize(std::ostream &oStrm, const mpt::ustring &name) const; Tuning::SerializationResult Deserialize(std::istream &iStrm, mpt::ustring &name, mpt::Charset defaultCharset); auto begin() { return m_Tunings.begin(); } auto begin() const { return m_Tunings.begin(); } auto cbegin() { return m_Tunings.cbegin(); } auto end() { return m_Tunings.end(); } auto end() const { return m_Tunings.end(); } auto cend() { return m_Tunings.cend(); } private: std::vector > m_Tunings; private: Tuning::SerializationResult DeserializeOLD(std::istream &inStrm, mpt::ustring &uname, mpt::Charset defaultCharset); }; #ifdef MODPLUG_TRACKER bool UnpackTuningCollection(const CTuningCollection &tc, const mpt::PathString &prefix); #endif } // namespace Tuning typedef Tuning::CTuningCollection CTuningCollection; OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/tuning.cpp0000644000175000017500000006612214170113560020110 00000000000000/* * tuning.cpp * ---------- * Purpose: Alternative sample tuning. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "tuning.h" #include "mpt/io/io.hpp" #include "mpt/io/io_stdstream.hpp" #include "../common/serialization_utils.h" #include "../common/misc_util.h" #include #include OPENMPT_NAMESPACE_BEGIN namespace Tuning { static RATIOTYPE SanitizeGroupRatio(RATIOTYPE ratio) { return std::clamp(std::abs(ratio), 1e-15f, 1e+07f); } namespace CTuningS11n { void ReadStr(std::istream &iStrm, mpt::ustring &ustr, const std::size_t dummy, mpt::Charset charset); void ReadNoteMap(std::istream &iStrm, std::map &m, const std::size_t dummy, mpt::Charset charset); void ReadRatioTable(std::istream& iStrm, std::vector& v, const size_t); void WriteNoteMap(std::ostream &oStrm, const std::map &m); void WriteStr(std::ostream &oStrm, const mpt::ustring &ustr); struct RatioWriter { RatioWriter(uint16 nWriteCount = s_nDefaultWriteCount) : m_nWriteCount(nWriteCount) {} void operator()(std::ostream& oStrm, const std::vector& v); uint16 m_nWriteCount; enum : uint16 { s_nDefaultWriteCount = (uint16_max >> 2) }; }; } using namespace CTuningS11n; /* Version history: 4->5: Lots of changes, finestep interpretation revamp, fileformat revamp. 3->4: Changed sizetypes in serialisation from size_t(uint32) to smaller types (uint8, USTEPTYPE) (March 2007) */ /* Version changes: 3->4: Finetune related internal structure and serialization revamp. 2->3: The type for the size_type in the serialisation changed from default(size_t, uint32) to unsigned STEPTYPE. (March 2007) */ static_assert(CTuning::s_RatioTableFineSizeMaxDefault < static_cast(FINESTEPCOUNT_MAX)); CTuning::CTuning() : m_TuningType(Type::GENERAL) , m_FineStepCount(0) { m_RatioTable.clear(); m_NoteMin = s_NoteMinDefault; m_RatioTable.resize(s_RatioTableSizeDefault, 1); m_GroupSize = 0; m_GroupRatio = 0; m_RatioTableFine.clear(); } bool CTuning::CreateGroupGeometric(const NOTEINDEXTYPE &s, const RATIOTYPE &r, const NOTEINDEXTYPE &startindex) { if(s < 1 || !IsValidRatio(r) || startindex < GetNoteRange().first) { return false; } std::vector v; v.reserve(s); for(NOTEINDEXTYPE i = startindex; i < startindex + s; i++) { v.push_back(GetRatio(i)); } return CreateGroupGeometric(v, r, GetNoteRange(), startindex); } bool CTuning::CreateGroupGeometric(const std::vector &v, const RATIOTYPE &r, const NoteRange &range, const NOTEINDEXTYPE &ratiostartpos) { if(range.first > range.last || v.size() == 0) { return false; } if(ratiostartpos < range.first || range.last < ratiostartpos || static_cast(range.last - ratiostartpos) < static_cast(v.size() - 1)) { return false; } if(GetFineStepCount() > FINESTEPCOUNT_MAX) { return false; } for(size_t i = 0; i < v.size(); i++) { if(v[i] < 0) { return false; } } if(r <= 0) { return false; } m_TuningType = Type::GROUPGEOMETRIC; m_NoteMin = range.first; m_GroupSize = mpt::saturate_cast(v.size()); m_GroupRatio = std::fabs(r); m_RatioTable.resize(range.last - range.first + 1); std::copy(v.begin(), v.end(), m_RatioTable.begin() + (ratiostartpos - range.first)); for(int32 i = ratiostartpos - 1; i >= m_NoteMin && ratiostartpos > NOTEINDEXTYPE_MIN; i--) { m_RatioTable[i - m_NoteMin] = m_RatioTable[i - m_NoteMin + m_GroupSize] / m_GroupRatio; } for(int32 i = ratiostartpos + m_GroupSize; i <= range.last && ratiostartpos <= (NOTEINDEXTYPE_MAX - m_GroupSize); i++) { m_RatioTable[i - m_NoteMin] = m_GroupRatio * m_RatioTable[i - m_NoteMin - m_GroupSize]; } UpdateFineStepTable(); return true; } bool CTuning::CreateGeometric(const UNOTEINDEXTYPE &p, const RATIOTYPE &r) { return CreateGeometric(p, r, GetNoteRange()); } bool CTuning::CreateGeometric(const UNOTEINDEXTYPE &s, const RATIOTYPE &r, const NoteRange &range) { if(range.first > range.last) { return false; } if(s < 1 || !IsValidRatio(r)) { return false; } if(range.last - range.first + 1 > NOTEINDEXTYPE_MAX) { return false; } m_TuningType = Type::GEOMETRIC; m_RatioTable.clear(); m_NoteMin = s_NoteMinDefault; m_RatioTable.resize(s_RatioTableSizeDefault, static_cast(1.0)); m_GroupSize = 0; m_GroupRatio = 0; m_RatioTableFine.clear(); m_NoteMin = range.first; m_GroupSize = mpt::saturate_cast(s); m_GroupRatio = std::fabs(r); const RATIOTYPE stepRatio = std::pow(m_GroupRatio, static_cast(1.0) / static_cast(m_GroupSize)); m_RatioTable.resize(range.last - range.first + 1); for(int32 i = range.first; i <= range.last; i++) { m_RatioTable[i - m_NoteMin] = std::pow(stepRatio, static_cast(i)); } UpdateFineStepTable(); return true; } mpt::ustring CTuning::GetNoteName(const NOTEINDEXTYPE &x, bool addOctave) const { if(!IsValidNote(x)) { return mpt::ustring(); } if(GetGroupSize() < 1) { const auto i = m_NoteNameMap.find(x); if(i != m_NoteNameMap.end()) return i->second; else return mpt::ufmt::val(x); } else { const NOTEINDEXTYPE pos = static_cast(mpt::wrapping_modulo(x, m_GroupSize)); const NOTEINDEXTYPE middlePeriodNumber = 5; mpt::ustring rValue; const auto nmi = m_NoteNameMap.find(pos); if(nmi != m_NoteNameMap.end()) { rValue = nmi->second; if(addOctave) { rValue += mpt::ufmt::val(middlePeriodNumber + mpt::wrapping_divide(x, m_GroupSize)); } } else { //By default, using notation nnP for notes; nn <-> note character starting //from 'A' with char ':' as fill char, and P is period integer. For example: //C:5, D:3, R:7 if(m_GroupSize <= 26) { rValue = mpt::ToUnicode(mpt::Charset::UTF8, std::string(1, static_cast(pos + 'A'))); rValue += UL_(":"); } else { rValue = mpt::ufmt::HEX0<1>(pos % 16) + mpt::ufmt::HEX0<1>((pos / 16) % 16); if(pos > 0xff) { rValue = mpt::ToUnicode(mpt::Charset::UTF8, mpt::ToLowerCaseAscii(mpt::ToCharset(mpt::Charset::UTF8, rValue))); } } if(addOctave) { rValue += mpt::ufmt::val(middlePeriodNumber + mpt::wrapping_divide(x, m_GroupSize)); } } return rValue; } } void CTuning::SetNoteName(const NOTEINDEXTYPE &n, const mpt::ustring &str) { const NOTEINDEXTYPE pos = (GetGroupSize() < 1) ? n : static_cast(mpt::wrapping_modulo(n, m_GroupSize)); if(!str.empty()) { m_NoteNameMap[pos] = str; } else { const auto iter = m_NoteNameMap.find(pos); if(iter != m_NoteNameMap.end()) { m_NoteNameMap.erase(iter); } } } // Without finetune RATIOTYPE CTuning::GetRatio(const NOTEINDEXTYPE note) const { if(!IsValidNote(note)) { return s_DefaultFallbackRatio; } const auto ratio = m_RatioTable[note - m_NoteMin]; if(ratio <= 1e-15f) { return s_DefaultFallbackRatio; } return ratio; } // With finetune RATIOTYPE CTuning::GetRatio(const NOTEINDEXTYPE baseNote, const STEPINDEXTYPE baseFineSteps) const { const STEPINDEXTYPE fineStepCount = static_cast(GetFineStepCount()); if(fineStepCount == 0 || baseFineSteps == 0) { return GetRatio(static_cast(baseNote + baseFineSteps)); } // If baseFineSteps is more than the number of finesteps between notes, note is increased. // So first figuring out what note and fineStep values to actually use. // Interpreting finestep==-1 on note x so that it is the same as finestep==fineStepCount on note x-1. // Note: If fineStepCount is n, n+1 steps are needed to get to next note. const NOTEINDEXTYPE note = static_cast(baseNote + mpt::wrapping_divide(baseFineSteps, (fineStepCount + 1))); const STEPINDEXTYPE fineStep = mpt::wrapping_modulo(baseFineSteps, (fineStepCount + 1)); if(!IsValidNote(note)) { return s_DefaultFallbackRatio; } if(fineStep == 0) { return m_RatioTable[note - m_NoteMin]; } RATIOTYPE fineRatio = static_cast(1.0); if(GetType() == Type::GEOMETRIC && m_RatioTableFine.size() > 0) { fineRatio = m_RatioTableFine[fineStep - 1]; } else if(GetType() == Type::GROUPGEOMETRIC && m_RatioTableFine.size() > 0) { fineRatio = m_RatioTableFine[GetRefNote(note) * fineStepCount + fineStep - 1]; } else { // Geometric finestepping fineRatio = std::pow(GetRatio(note + 1) / GetRatio(note), static_cast(fineStep) / (fineStepCount + 1)); } return m_RatioTable[note - m_NoteMin] * fineRatio; } bool CTuning::SetRatio(const NOTEINDEXTYPE& s, const RATIOTYPE& r) { if(GetType() != Type::GROUPGEOMETRIC && GetType() != Type::GENERAL) { return false; } //Creating ratio table if doesn't exist. if(m_RatioTable.empty()) { m_RatioTable.assign(s_RatioTableSizeDefault, 1); m_NoteMin = s_NoteMinDefault; } if(!IsValidNote(s)) { return false; } m_RatioTable[s - m_NoteMin] = std::fabs(r); if(GetType() == Type::GROUPGEOMETRIC) { // update other groups for(NOTEINDEXTYPE n = m_NoteMin; n < m_NoteMin + static_cast(m_RatioTable.size()); ++n) { if(n == s) { // nothing } else if(std::abs(n - s) % m_GroupSize == 0) { m_RatioTable[n - m_NoteMin] = std::pow(m_GroupRatio, static_cast(n - s) / static_cast(m_GroupSize)) * m_RatioTable[s - m_NoteMin]; } } UpdateFineStepTable(); } return true; } void CTuning::SetFineStepCount(const USTEPINDEXTYPE& fs) { m_FineStepCount = std::clamp(mpt::saturate_cast(fs), STEPINDEXTYPE(0), FINESTEPCOUNT_MAX); UpdateFineStepTable(); } void CTuning::UpdateFineStepTable() { if(m_FineStepCount <= 0) { m_RatioTableFine.clear(); return; } if(GetType() == Type::GEOMETRIC) { if(m_FineStepCount > s_RatioTableFineSizeMaxDefault) { m_RatioTableFine.clear(); return; } m_RatioTableFine.resize(m_FineStepCount); const RATIOTYPE q = GetRatio(GetNoteRange().first + 1) / GetRatio(GetNoteRange().first); const RATIOTYPE rFineStep = std::pow(q, static_cast(1)/(m_FineStepCount+1)); for(USTEPINDEXTYPE i = 1; i<=m_FineStepCount; i++) m_RatioTableFine[i-1] = std::pow(rFineStep, static_cast(i)); return; } if(GetType() == Type::GROUPGEOMETRIC) { const UNOTEINDEXTYPE p = GetGroupSize(); if(p > s_RatioTableFineSizeMaxDefault / m_FineStepCount) { //In case fineratiotable would become too large, not using //table for it. m_RatioTableFine.clear(); return; } else { //Creating 'geometric' finestepping between notes. m_RatioTableFine.resize(p * m_FineStepCount); const NOTEINDEXTYPE startnote = GetRefNote(GetNoteRange().first); for(UNOTEINDEXTYPE i = 0; i(1)/(m_FineStepCount+1)); for(UNOTEINDEXTYPE j = 1; j<=m_FineStepCount; j++) { m_RatioTableFine[m_FineStepCount * refnote + (j-1)] = std::pow(rFineStep, static_cast(j)); } } return; } } if(GetType() == Type::GENERAL) { //Not using table with tuning of type general. m_RatioTableFine.clear(); return; } //Should not reach here. m_RatioTableFine.clear(); m_FineStepCount = 0; } bool CTuning::Multiply(const RATIOTYPE r) { if(!IsValidRatio(r)) { return false; } for(auto & ratio : m_RatioTable) { ratio *= r; } return true; } bool CTuning::ChangeGroupsize(const NOTEINDEXTYPE& s) { if(s < 1) return false; if(m_TuningType == Type::GROUPGEOMETRIC) return CreateGroupGeometric(s, GetGroupRatio(), 0); if(m_TuningType == Type::GEOMETRIC) return CreateGeometric(s, GetGroupRatio()); return false; } bool CTuning::ChangeGroupRatio(const RATIOTYPE& r) { if(!IsValidRatio(r)) return false; if(m_TuningType == Type::GROUPGEOMETRIC) return CreateGroupGeometric(GetGroupSize(), r, 0); if(m_TuningType == Type::GEOMETRIC) return CreateGeometric(GetGroupSize(), r); return false; } SerializationResult CTuning::InitDeserialize(std::istream &iStrm, mpt::Charset defaultCharset) { // Note: OpenMPT since at least r323 writes version number (4<<24)+4 while it // reads version number (5<<24)+4 or earlier. // We keep this behaviour. if(iStrm.fail()) return SerializationResult::Failure; srlztn::SsbRead ssb(iStrm); ssb.BeginRead("CTB244RTI", (5 << 24) + 4); // version int8 use_utf8 = 0; ssb.ReadItem(use_utf8, "UTF8"); const mpt::Charset charset = use_utf8 ? mpt::Charset::UTF8 : defaultCharset; ssb.ReadItem(m_TuningName, "0", [charset](std::istream &iStrm, mpt::ustring &ustr, const std::size_t dummy){ return ReadStr(iStrm, ustr, dummy, charset); }); uint16 dummyEditMask = 0xffff; ssb.ReadItem(dummyEditMask, "1"); std::underlying_type::type type = 0; ssb.ReadItem(type, "2"); m_TuningType = static_cast(type); ssb.ReadItem(m_NoteNameMap, "3", [charset](std::istream &iStrm, std::map &m, const std::size_t dummy){ return ReadNoteMap(iStrm, m, dummy, charset); }); ssb.ReadItem(m_FineStepCount, "4"); // RTI entries. ssb.ReadItem(m_RatioTable, "RTI0", ReadRatioTable); ssb.ReadItem(m_NoteMin, "RTI1"); ssb.ReadItem(m_GroupSize, "RTI2"); ssb.ReadItem(m_GroupRatio, "RTI3"); UNOTEINDEXTYPE ratiotableSize = 0; ssb.ReadItem(ratiotableSize, "RTI4"); m_GroupRatio = SanitizeGroupRatio(m_GroupRatio); if(!std::isfinite(m_GroupRatio)) { return SerializationResult::Failure; } for(auto ratio : m_RatioTable) { if(!std::isfinite(ratio)) return SerializationResult::Failure; } // If reader status is ok and m_NoteMin is somewhat reasonable, process data. if(!((ssb.GetStatus() & srlztn::SNT_FAILURE) == 0 && m_NoteMin >= -300 && m_NoteMin <= 300)) { return SerializationResult::Failure; } // reject unknown types if(m_TuningType != Type::GENERAL && m_TuningType != Type::GROUPGEOMETRIC && m_TuningType != Type::GEOMETRIC) { return SerializationResult::Failure; } if(m_GroupSize < 0) { return SerializationResult::Failure; } m_FineStepCount = std::clamp(mpt::saturate_cast(m_FineStepCount), STEPINDEXTYPE(0), FINESTEPCOUNT_MAX); if(m_RatioTable.size() > static_cast(NOTEINDEXTYPE_MAX)) { return SerializationResult::Failure; } if((GetType() == Type::GROUPGEOMETRIC) || (GetType() == Type::GEOMETRIC)) { if(ratiotableSize < 1 || ratiotableSize > NOTEINDEXTYPE_MAX) { return SerializationResult::Failure; } if(GetType() == Type::GEOMETRIC) { if(!CreateGeometric(GetGroupSize(), GetGroupRatio(), NoteRange{m_NoteMin, static_cast(m_NoteMin + ratiotableSize - 1)})) { return SerializationResult::Failure; } } else { if(!CreateGroupGeometric(m_RatioTable, GetGroupRatio(), NoteRange{m_NoteMin, static_cast(m_NoteMin + ratiotableSize - 1)}, m_NoteMin)) { return SerializationResult::Failure; } } } else { UpdateFineStepTable(); } return SerializationResult::Success; } template static bool VectorFromBinaryStream(std::istream& inStrm, std::vector& v, const SIZETYPE maxSize = (std::numeric_limits::max)()) { if(!inStrm.good()) return false; SIZETYPE size = 0; mpt::IO::ReadIntLE(inStrm, size); if(size > maxSize) return false; v.resize(size); for(std::size_t i = 0; i(inStrm, version); if(version != 2 && version != 3) return SerializationResult::Failure; char begin2[8]; MemsetZero(begin2); inStrm.read(begin2, sizeof(begin2)); if(std::memcmp(begin2, "CTB", 8)) { return SerializationResult::Failure; } int16 version2 = 0; mpt::IO::ReadIntLE(inStrm, version2); if(version2 != 3 && version2 != 4) { return SerializationResult::Failure; } //Tuning name if(version2 <= 3) { std::string tmpName; if(!mpt::IO::ReadSizedStringLE(inStrm, tmpName, 0xffff)) { return SerializationResult::Failure; } m_TuningName = mpt::ToUnicode(defaultCharset, tmpName); } else { std::string tmpName; if(!mpt::IO::ReadSizedStringLE(inStrm, tmpName)) { return SerializationResult::Failure; } m_TuningName = mpt::ToUnicode(defaultCharset, tmpName); } //Const mask int16 em = 0; mpt::IO::ReadIntLE(inStrm, em); //Tuning type int16 tt = 0; mpt::IO::ReadIntLE(inStrm, tt); m_TuningType = static_cast(tt); //Notemap uint16 size = 0; if(version2 <= 3) { uint32 tempsize = 0; mpt::IO::ReadIntLE(inStrm, tempsize); if(tempsize > 0xffff) { return SerializationResult::Failure; } size = mpt::saturate_cast(tempsize); } else { mpt::IO::ReadIntLE(inStrm, size); } for(UNOTEINDEXTYPE i = 0; i(inStrm, n); if(version2 <= 3) { if(!mpt::IO::ReadSizedStringLE(inStrm, str, 0xffff)) { return SerializationResult::Failure; } } else { if(!mpt::IO::ReadSizedStringLE(inStrm, str)) { return SerializationResult::Failure; } } m_NoteNameMap[n] = mpt::ToUnicode(defaultCharset, str); } //End marker char end2[8]; MemsetZero(end2); inStrm.read(end2, sizeof(end2)); if(std::memcmp(end2, "CTE", 8)) { return SerializationResult::Failure; } // reject unknown types if(m_TuningType != Type::GENERAL && m_TuningType != Type::GROUPGEOMETRIC && m_TuningType != Type::GEOMETRIC) { return SerializationResult::Failure; } //Ratiotable if(version <= 2) { if(!VectorFromBinaryStream(inStrm, m_RatioTable, 0xffff)) { return SerializationResult::Failure; } } else { if(!VectorFromBinaryStream(inStrm, m_RatioTable)) { return SerializationResult::Failure; } } for(auto ratio : m_RatioTable) { if(!std::isfinite(ratio)) return SerializationResult::Failure; } //Fineratios if(version <= 2) { if(!VectorFromBinaryStream(inStrm, m_RatioTableFine, 0xffff)) { return SerializationResult::Failure; } } else { if(!VectorFromBinaryStream(inStrm, m_RatioTableFine)) { return SerializationResult::Failure; } } for(auto ratio : m_RatioTableFine) { if(!std::isfinite(ratio)) return SerializationResult::Failure; } m_FineStepCount = mpt::saturate_cast(m_RatioTableFine.size()); // m_NoteMin int16 notemin = 0; mpt::IO::ReadIntLE(inStrm, notemin); m_NoteMin = notemin; if(m_NoteMin < -200 || m_NoteMin > 200) { return SerializationResult::Failure; } //m_GroupSize int16 groupsize = 0; mpt::IO::ReadIntLE(inStrm, groupsize); m_GroupSize = groupsize; if(m_GroupSize < 0) { return SerializationResult::Failure; } //m_GroupRatio IEEE754binary32LE groupratio = IEEE754binary32LE(0.0f); mpt::IO::Read(inStrm, groupratio); m_GroupRatio = SanitizeGroupRatio(groupratio); if(!std::isfinite(m_GroupRatio)) { return SerializationResult::Failure; } char end[8]; MemsetZero(end); inStrm.read(reinterpret_cast(&end), sizeof(end)); if(std::memcmp(end, "CTRTI_E.", 8)) { return SerializationResult::Failure; } // reject corrupt tunings if(m_RatioTable.size() > static_cast(NOTEINDEXTYPE_MAX)) { return SerializationResult::Failure; } if((m_GroupSize <= 0 || m_GroupRatio <= 0) && m_TuningType != Type::GENERAL) { return SerializationResult::Failure; } if(m_TuningType == Type::GROUPGEOMETRIC || m_TuningType == Type::GEOMETRIC) { if(m_RatioTable.size() < static_cast(m_GroupSize)) { return SerializationResult::Failure; } } // convert old finestepcount if(m_FineStepCount > 0) { m_FineStepCount -= 1; } m_FineStepCount = std::clamp(mpt::saturate_cast(m_FineStepCount), STEPINDEXTYPE(0), FINESTEPCOUNT_MAX); UpdateFineStepTable(); if(m_TuningType == Type::GEOMETRIC) { // Convert old geometric to new groupgeometric because old geometric tunings // can have ratio(0) != 1.0, which would get lost when saving nowadays. if(mpt::saturate_cast(m_RatioTable.size()) >= m_GroupSize - m_NoteMin) { std::vector ratios; for(NOTEINDEXTYPE n = 0; n < m_GroupSize; ++n) { ratios.push_back(m_RatioTable[n - m_NoteMin]); } CreateGroupGeometric(ratios, m_GroupRatio, GetNoteRange(), 0); } } return SerializationResult::Success; } Tuning::SerializationResult CTuning::Serialize(std::ostream& outStrm) const { // Note: OpenMPT since at least r323 writes version number (4<<24)+4 while it // reads version number (5<<24)+4. // We keep this behaviour. srlztn::SsbWrite ssb(outStrm); ssb.BeginWrite("CTB244RTI", (4 << 24) + 4); // version ssb.WriteItem(int8(1), "UTF8"); if (m_TuningName.length() > 0) ssb.WriteItem(m_TuningName, "0", WriteStr); uint16 dummyEditMask = 0xffff; ssb.WriteItem(dummyEditMask, "1"); ssb.WriteItem(mpt::to_underlying(m_TuningType), "2"); if (m_NoteNameMap.size() > 0) ssb.WriteItem(m_NoteNameMap, "3", WriteNoteMap); if (GetFineStepCount() > 0) ssb.WriteItem(m_FineStepCount, "4"); const Tuning::Type tt = GetType(); if (GetGroupRatio() > 0) ssb.WriteItem(m_GroupRatio, "RTI3"); if (tt == Type::GROUPGEOMETRIC) ssb.WriteItem(m_RatioTable, "RTI0", RatioWriter(GetGroupSize())); if (tt == Type::GENERAL) ssb.WriteItem(m_RatioTable, "RTI0", RatioWriter()); if (tt == Type::GEOMETRIC) ssb.WriteItem(m_GroupSize, "RTI2"); if(tt == Type::GEOMETRIC || tt == Type::GROUPGEOMETRIC) { //For Groupgeometric this data is the number of ratios in ratiotable. UNOTEINDEXTYPE ratiotableSize = static_cast(m_RatioTable.size()); ssb.WriteItem(ratiotableSize, "RTI4"); } // m_NoteMin ssb.WriteItem(m_NoteMin, "RTI1"); ssb.FinishWrite(); return ((ssb.GetStatus() & srlztn::SNT_FAILURE) != 0) ? Tuning::SerializationResult::Failure : Tuning::SerializationResult::Success; } #ifdef MODPLUG_TRACKER bool CTuning::WriteSCL(std::ostream &f, const mpt::PathString &filename) const { mpt::IO::WriteTextCRLF(f, MPT_AFORMAT("! {}")(mpt::ToCharset(mpt::Charset::ISO8859_1, (filename.GetFileName() + filename.GetFileExt()).ToUnicode()))); mpt::IO::WriteTextCRLF(f, "!"); std::string name = mpt::ToCharset(mpt::Charset::ISO8859_1, GetName()); for(auto & c : name) { if(static_cast(c) < 32) c = ' '; } // remove control characters if(name.length() >= 1 && name[0] == '!') name[0] = '?'; // do not confuse description with comment mpt::IO::WriteTextCRLF(f, name); if(GetType() == Type::GEOMETRIC) { mpt::IO::WriteTextCRLF(f, MPT_AFORMAT(" {}")(m_GroupSize)); mpt::IO::WriteTextCRLF(f, "!"); for(NOTEINDEXTYPE n = 0; n < m_GroupSize; ++n) { double ratio = std::pow(static_cast(m_GroupRatio), static_cast(n + 1) / static_cast(m_GroupSize)); double cents = std::log2(ratio) * 1200.0; mpt::IO::WriteTextCRLF(f, MPT_AFORMAT(" {} ! {}")( mpt::afmt::fix(cents), mpt::ToCharset(mpt::Charset::ISO8859_1, GetNoteName((n + 1) % m_GroupSize, false)) )); } } else if(GetType() == Type::GROUPGEOMETRIC) { mpt::IO::WriteTextCRLF(f, MPT_AFORMAT(" {}")(m_GroupSize)); mpt::IO::WriteTextCRLF(f, "!"); for(NOTEINDEXTYPE n = 0; n < m_GroupSize; ++n) { bool last = (n == (m_GroupSize - 1)); double baseratio = static_cast(GetRatio(0)); double ratio = static_cast(last ? m_GroupRatio : GetRatio(n + 1)) / baseratio; double cents = std::log2(ratio) * 1200.0; mpt::IO::WriteTextCRLF(f, MPT_AFORMAT(" {} ! {}")( mpt::afmt::fix(cents), mpt::ToCharset(mpt::Charset::ISO8859_1, GetNoteName((n + 1) % m_GroupSize, false)) )); } } else if(GetType() == Type::GENERAL) { mpt::IO::WriteTextCRLF(f, MPT_AFORMAT(" {}")(m_RatioTable.size() + 1)); mpt::IO::WriteTextCRLF(f, "!"); double baseratio = 1.0; for(NOTEINDEXTYPE n = 0; n < mpt::saturate_cast(m_RatioTable.size()); ++n) { baseratio = std::min(baseratio, static_cast(m_RatioTable[n])); } for(NOTEINDEXTYPE n = 0; n < mpt::saturate_cast(m_RatioTable.size()); ++n) { double ratio = static_cast(m_RatioTable[n]) / baseratio; double cents = std::log2(ratio) * 1200.0; mpt::IO::WriteTextCRLF(f, MPT_AFORMAT(" {} ! {}")( mpt::afmt::fix(cents), mpt::ToCharset(mpt::Charset::ISO8859_1, GetNoteName(n + m_NoteMin, false)) )); } mpt::IO::WriteTextCRLF(f, MPT_AFORMAT(" {} ! {}")( mpt::afmt::val(1), std::string() )); } else { return false; } return true; } #endif namespace CTuningS11n { void RatioWriter::operator()(std::ostream& oStrm, const std::vector& v) { const std::size_t nWriteCount = std::min(v.size(), static_cast(m_nWriteCount)); mpt::IO::WriteAdaptiveInt64LE(oStrm, nWriteCount); for(size_t i = 0; i < nWriteCount; i++) mpt::IO::Write(oStrm, IEEE754binary32LE(v[i])); } void ReadNoteMap(std::istream &iStrm, std::map &m, const std::size_t dummy, mpt::Charset charset) { MPT_UNREFERENCED_PARAMETER(dummy); uint64 val; mpt::IO::ReadAdaptiveInt64LE(iStrm, val); LimitMax(val, 256u); // Read 256 at max. for(size_t i = 0; i < val; i++) { int16 key; mpt::IO::ReadIntLE(iStrm, key); std::string str; mpt::IO::ReadSizedStringLE(iStrm, str); m[key] = mpt::ToUnicode(charset, str); } } void ReadRatioTable(std::istream& iStrm, std::vector& v, const size_t) { uint64 val; mpt::IO::ReadAdaptiveInt64LE(iStrm, val); v.resize(std::min(mpt::saturate_cast(val), std::size_t(256))); // Read 256 vals at max. for(size_t i = 0; i < v.size(); i++) { IEEE754binary32LE tmp(0.0f); mpt::IO::Read(iStrm, tmp); v[i] = tmp; } } void ReadStr(std::istream &iStrm, mpt::ustring &ustr, const std::size_t dummy, mpt::Charset charset) { MPT_UNREFERENCED_PARAMETER(dummy); std::string str; uint64 val; mpt::IO::ReadAdaptiveInt64LE(iStrm, val); size_t nSize = (val > 255) ? 255 : static_cast(val); // Read 255 characters at max. str.clear(); str.resize(nSize); for(size_t i = 0; i < nSize; i++) mpt::IO::ReadIntLE(iStrm, str[i]); if(str.find_first_of('\0') != std::string::npos) { // trim \0 at the end str.resize(str.find_first_of('\0')); } ustr = mpt::ToUnicode(charset, str); } void WriteNoteMap(std::ostream &oStrm, const std::map &m) { mpt::IO::WriteAdaptiveInt64LE(oStrm, m.size()); for(auto &mi : m) { mpt::IO::WriteIntLE(oStrm, mi.first); mpt::IO::WriteSizedStringLE(oStrm, mpt::ToCharset(mpt::Charset::UTF8, mi.second)); } } void WriteStr(std::ostream &oStrm, const mpt::ustring &ustr) { std::string str = mpt::ToCharset(mpt::Charset::UTF8, ustr); mpt::IO::WriteAdaptiveInt64LE(oStrm, str.size()); oStrm.write(str.c_str(), str.size()); } } // namespace CTuningS11n. } // namespace Tuning OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/tuning.h0000644000175000017500000001672214052666041017564 00000000000000/* * tuning.h * -------- * Purpose: Alternative sample tuning. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include #include "tuningbase.h" OPENMPT_NAMESPACE_BEGIN namespace Tuning { class CTuning { public: static constexpr char s_FileExtension[5] = ".tun"; static constexpr RATIOTYPE s_DefaultFallbackRatio = 1.0f; static constexpr NOTEINDEXTYPE s_NoteMinDefault = -64; static constexpr UNOTEINDEXTYPE s_RatioTableSizeDefault = 128; static constexpr USTEPINDEXTYPE s_RatioTableFineSizeMaxDefault = 1000; public: // To return ratio of certain note. RATIOTYPE GetRatio(const NOTEINDEXTYPE note) const; // To return ratio from a 'step'(noteindex + stepindex) RATIOTYPE GetRatio(const NOTEINDEXTYPE baseNote, const STEPINDEXTYPE baseFineSteps) const; //Tuning might not be valid for arbitrarily large range, //so this can be used to ask where it is valid. Tells the lowest and highest //note that are valid. MPT_FORCEINLINE NoteRange GetNoteRange() const { return NoteRange{m_NoteMin, static_cast(m_NoteMin + static_cast(m_RatioTable.size()) - 1)}; } // Return true if note is within note range MPT_FORCEINLINE bool IsValidNote(const NOTEINDEXTYPE n) const { return (GetNoteRange().first <= n && n <= GetNoteRange().last); } MPT_FORCEINLINE UNOTEINDEXTYPE GetGroupSize() const { return m_GroupSize; } RATIOTYPE GetGroupRatio() const {return m_GroupRatio;} // To return (fine)stepcount between two consecutive mainsteps. MPT_FORCEINLINE USTEPINDEXTYPE GetFineStepCount() const { return m_FineStepCount; } //To return 'directed distance' between given notes. STEPINDEXTYPE GetStepDistance(const NOTEINDEXTYPE& from, const NOTEINDEXTYPE& to) const {return (to - from)*(static_cast(GetFineStepCount())+1);} //To return 'directed distance' between given steps. STEPINDEXTYPE GetStepDistance(const NOTEINDEXTYPE& noteFrom, const STEPINDEXTYPE& stepDistFrom, const NOTEINDEXTYPE& noteTo, const STEPINDEXTYPE& stepDistTo) const {return GetStepDistance(noteFrom, noteTo) + stepDistTo - stepDistFrom;} //To set finestepcount between two consecutive mainsteps. //Finestep count == 0 means that //stepdistances become the same as note distances. void SetFineStepCount(const USTEPINDEXTYPE& fs); // Multiply all ratios by given number. bool Multiply(const RATIOTYPE r); bool SetRatio(const NOTEINDEXTYPE& s, const RATIOTYPE& r); MPT_FORCEINLINE Tuning::Type GetType() const { return m_TuningType; } mpt::ustring GetNoteName(const NOTEINDEXTYPE &x, bool addOctave = true) const; void SetNoteName(const NOTEINDEXTYPE &, const mpt::ustring &); static std::unique_ptr CreateDeserialize(std::istream &f, mpt::Charset defaultCharset) { std::unique_ptr pT = std::unique_ptr(new CTuning()); if(pT->InitDeserialize(f, defaultCharset) != SerializationResult::Success) { return nullptr; } return pT; } //Try to read old version (v.3) and return pointer to new instance if succesfull, else nullptr. static std::unique_ptr CreateDeserializeOLD(std::istream &f, mpt::Charset defaultCharset) { std::unique_ptr pT = std::unique_ptr(new CTuning()); if(pT->InitDeserializeOLD(f, defaultCharset) != SerializationResult::Success) { return nullptr; } return pT; } static std::unique_ptr CreateGeneral(const mpt::ustring &name) { std::unique_ptr pT = std::unique_ptr(new CTuning()); pT->SetName(name); return pT; } static std::unique_ptr CreateGroupGeometric(const mpt::ustring &name, UNOTEINDEXTYPE groupsize, RATIOTYPE groupratio, USTEPINDEXTYPE finestepcount) { std::unique_ptr pT = std::unique_ptr(new CTuning()); pT->SetName(name); if(!pT->CreateGroupGeometric(groupsize, groupratio, 0)) { return nullptr; } pT->SetFineStepCount(finestepcount); return pT; } static std::unique_ptr CreateGroupGeometric(const mpt::ustring &name, const std::vector &ratios, RATIOTYPE groupratio, USTEPINDEXTYPE finestepcount) { std::unique_ptr pT = std::unique_ptr(new CTuning()); pT->SetName(name); NoteRange range = NoteRange{s_NoteMinDefault, static_cast(s_NoteMinDefault + s_RatioTableSizeDefault - 1)}; range.last = std::max(range.last, mpt::saturate_cast(ratios.size() - 1)); range.first = 0 - range.last - 1; if(!pT->CreateGroupGeometric(ratios, groupratio, range, 0)) { return nullptr; } pT->SetFineStepCount(finestepcount); return pT; } static std::unique_ptr CreateGeometric(const mpt::ustring &name, UNOTEINDEXTYPE groupsize, RATIOTYPE groupratio, USTEPINDEXTYPE finestepcount) { std::unique_ptr pT = std::unique_ptr(new CTuning()); pT->SetName(name); if(!pT->CreateGeometric(groupsize, groupratio)) { return nullptr; } pT->SetFineStepCount(finestepcount); return pT; } Tuning::SerializationResult Serialize(std::ostream& out) const; #ifdef MODPLUG_TRACKER bool WriteSCL(std::ostream &f, const mpt::PathString &filename) const; #endif bool ChangeGroupsize(const NOTEINDEXTYPE&); bool ChangeGroupRatio(const RATIOTYPE&); void SetName(const mpt::ustring &s) { m_TuningName = s; } mpt::ustring GetName() const { return m_TuningName; } private: CTuning(); SerializationResult InitDeserialize(std::istream &inStrm, mpt::Charset defaultCharset); //Try to read old version (v.3) and return pointer to new instance if succesfull, else nullptr. SerializationResult InitDeserializeOLD(std::istream &inStrm, mpt::Charset defaultCharset); //Create GroupGeometric tuning of *this using virtual ProCreateGroupGeometric. bool CreateGroupGeometric(const std::vector &v, const RATIOTYPE &r, const NoteRange &range, const NOTEINDEXTYPE &ratiostartpos); //Create GroupGeometric of *this using ratios from 'itself' and ratios starting from //position given as third argument. bool CreateGroupGeometric(const NOTEINDEXTYPE &s, const RATIOTYPE &r, const NOTEINDEXTYPE &startindex); //Create geometric tuning of *this using ratio(0) = 1. bool CreateGeometric(const UNOTEINDEXTYPE &p, const RATIOTYPE &r); bool CreateGeometric(const UNOTEINDEXTYPE &s, const RATIOTYPE &r, const NoteRange &range); void UpdateFineStepTable(); // GroupPeriodic-specific. // Get the corresponding note in [0, period-1]. // For example GetRefNote(-1) is to return note :'groupsize-1'. MPT_FORCEINLINE NOTEINDEXTYPE GetRefNote(NOTEINDEXTYPE note) const { MPT_ASSERT(GetType() == Type::GROUPGEOMETRIC || GetType() == Type::GEOMETRIC); return static_cast(mpt::wrapping_modulo(note, GetGroupSize())); } static bool IsValidRatio(RATIOTYPE ratio) { return (ratio > static_cast(0.0)); } private: Tuning::Type m_TuningType; //Noteratios std::vector m_RatioTable; //'Fineratios' std::vector m_RatioTableFine; // The lowest index of note in the table NOTEINDEXTYPE m_NoteMin; //For groupgeometric tunings, tells the 'group size' and 'group ratio' //m_GroupSize should always be >= 0. NOTEINDEXTYPE m_GroupSize; RATIOTYPE m_GroupRatio; USTEPINDEXTYPE m_FineStepCount; // invariant: 0 <= m_FineStepCount <= FINESTEPCOUNT_MAX mpt::ustring m_TuningName; std::map m_NoteNameMap; }; // class CTuning } // namespace Tuning OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/UMXTools.cpp0000644000175000017500000002106614154732664020312 00000000000000/* * UMXTools.h * ------------ * Purpose: UMX/UAX (Unreal package) helper functions * Notes : (currently none) * Authors: OpenMPT Devs (inspired by code from https://wiki.beyondunreal.com/Legacy:Package_File_Format) * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Loaders.h" #include "UMXTools.h" OPENMPT_NAMESPACE_BEGIN namespace UMX { bool FileHeader::IsValid() const { return !std::memcmp(magic, "\xC1\x83\x2A\x9E", 4) && nameOffset >= sizeof(FileHeader) && exportOffset >= sizeof(FileHeader) && importOffset >= sizeof(FileHeader) && nameCount > 0 && nameCount <= uint32_max / 5u && exportCount > 0 && exportCount <= uint32_max / 8u && importCount > 0 && importCount <= uint32_max / 4u && uint32_max - nameCount * 5u >= nameOffset && uint32_max - exportCount * 8u >= exportOffset && uint32_max - importCount * 4u >= importOffset; } uint32 FileHeader::GetMinimumAdditionalFileSize() const { return std::max({nameOffset + nameCount * 5u, exportOffset + exportCount * 8u, importOffset + importCount * 4u}) - sizeof(FileHeader); } CSoundFile::ProbeResult ProbeFileHeader(MemoryFileReader file, const uint64 *pfilesize, const char *requiredType) { FileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { return CSoundFile::ProbeWantMoreData; } if(!fileHeader.IsValid()) { return CSoundFile::ProbeFailure; } if(requiredType != nullptr && !FindNameTableEntryMemory(file, fileHeader, requiredType)) { return CSoundFile::ProbeFailure; } return CSoundFile::ProbeAdditionalSize(file, pfilesize, fileHeader.GetMinimumAdditionalFileSize()); } // Read compressed unreal integers - similar to MIDI integers, but signed values are possible. template static int32 ReadIndexImpl(Tfile &chunk) { enum { signMask = 0x80, // Highest bit of first byte indicates if value is signed valueMask1 = 0x3F, // Low 6 bits of first byte are actual value continueMask1 = 0x40, // Second-highest bit of first byte indicates if further bytes follow valueMask = 0x7F, // Low 7 bits of following bytes are actual value continueMask = 0x80, // Highest bit of following bytes indicates if further bytes follow }; // Read first byte uint8 b = chunk.ReadUint8(); bool isSigned = (b & signMask) != 0; uint32 result = (b & valueMask1); int shift = 6; if(b & continueMask1) { // Read remaining bytes do { b = chunk.ReadUint8(); uint32 data = static_cast(b) & valueMask; data <<= shift; result |= data; shift += 7; } while((b & continueMask) != 0 && (shift < 32)); } if(isSigned && result <= int32_max) return -static_cast(result); else if(isSigned) return int32_min; else return result; } int32 ReadIndex(FileReader &chunk) { return ReadIndexImpl(chunk); } // Returns true if the given nme exists in the name table. template static bool FindNameTableEntryImpl(TFile &file, const FileHeader &fileHeader, const char *name) { if(!name) { return false; } const std::size_t nameLen = std::strlen(name); if(nameLen == 0) { return false; } bool result = false; const FileReader::off_t oldpos = file.GetPosition(); if(file.Seek(fileHeader.nameOffset)) { for(uint32 i = 0; i < fileHeader.nameCount && file.CanRead(5); i++) { if(fileHeader.packageVersion >= 64) { int32 length = ReadIndexImpl(file); if(length <= 0) { continue; } } bool match = true; std::size_t pos = 0; char c = 0; while((c = file.ReadUint8()) != 0) { c = mpt::ToLowerCaseAscii(c); if(pos < nameLen) { match = match && (c == name[pos]); } pos++; } if(pos != nameLen) { match = false; } if(match) { result = true; } file.Skip(4); // Object flags } } file.Seek(oldpos); return result; } bool FindNameTableEntry(FileReader &file, const FileHeader &fileHeader, const char *name) { return FindNameTableEntryImpl(file, fileHeader, name); } bool FindNameTableEntryMemory(MemoryFileReader &file, const FileHeader &fileHeader, const char *name) { return FindNameTableEntryImpl(file, fileHeader, name); } // Read an entry from the name table. std::string ReadNameTableEntry(FileReader &chunk, uint16 packageVersion) { std::string name; if(packageVersion >= 64) { // String length int32 length = ReadIndex(chunk); if(length <= 0) { return ""; } name.reserve(std::min(length, mpt::saturate_cast(chunk.BytesLeft()))); } // Simple zero-terminated string uint8 chr; while((chr = chunk.ReadUint8()) != 0) { // Convert string to lower case if(chr >= 'A' && chr <= 'Z') { chr = chr - 'A' + 'a'; } name.append(1, static_cast(chr)); } chunk.Skip(4); // Object flags return name; } // Read complete name table. std::vector ReadNameTable(FileReader &file, const FileHeader &fileHeader) { file.Seek(fileHeader.nameOffset); // nameOffset and nameCount were validated when parsing header std::vector names; names.reserve(fileHeader.nameCount); for(uint32 i = 0; i < fileHeader.nameCount && file.CanRead(5); i++) { names.push_back(ReadNameTableEntry(file, fileHeader.packageVersion)); } return names; } // Read an entry from the import table. int32 ReadImportTableEntry(FileReader &chunk, uint16 packageVersion) { ReadIndex(chunk); // Class package ReadIndex(chunk); // Class name if(packageVersion >= 60) { chunk.Skip(4); // Package } else { ReadIndex(chunk); // ?? } return ReadIndex(chunk); // Object name (offset into the name table) } // Read import table. std::vector ReadImportTable(FileReader &file, const FileHeader &fileHeader, const std::vector &names) { file.Seek(fileHeader.importOffset); // importOffset and importCount were validated when parsing header std::vector classes; classes.reserve(fileHeader.importCount); for(uint32 i = 0; i < fileHeader.importCount && file.CanRead(4); i++) { int32 objName = ReadImportTableEntry(file, fileHeader.packageVersion); if(static_cast(objName) < names.size()) { classes.push_back(objName); } } return classes; } // Read an entry from the export table. std::pair ReadExportTableEntry(FileReader &file, const FileHeader &fileHeader, const std::vector &classes, const std::vector &names, const char *filterType) { const uint32 objClass = ~static_cast(ReadIndex(file)); // Object class if(objClass >= classes.size()) return {}; ReadIndex(file); // Object parent if(fileHeader.packageVersion >= 60) { file.Skip(4); // Internal package / group of the object } int32 objName = ReadIndex(file); // Object name (offset into the name table) file.Skip(4); // Object flags int32 objSize = ReadIndex(file); int32 objOffset = ReadIndex(file); if(objSize <= 0 || objOffset <= static_cast(sizeof(FileHeader))) return {}; // If filterType is set, reject any objects not of that type if(filterType != nullptr && names[classes[objClass]] != filterType) return {}; FileReader chunk = file.GetChunkAt(objOffset, objSize); if(!chunk.IsValid()) return {}; if(fileHeader.packageVersion < 40) { chunk.Skip(8); // 00 00 00 00 00 00 00 00 } if(fileHeader.packageVersion < 60) { chunk.Skip(16); // 81 00 00 00 00 00 FF FF FF FF FF FF FF FF 00 00 } // Read object properties #if 0 size_t propertyName = static_cast(ReadIndex(chunk)); if(propertyName >= names.size() || names[propertyName] != "none") { // Can't bother to implement property reading, as no UMX files I've seen so far use properties for the relevant objects, // and only the UAX files in the Unreal 1997/98 beta seem to use this and still load just fine when ignoring it. // If it should be necessary to implement this, check CUnProperty.cpp in http://ut-files.com/index.php?dir=Utilities/&file=utcms_source.zip MPT_ASSERT_NOTREACHED(); continue; } #else ReadIndex(chunk); #endif if(fileHeader.packageVersion >= 120) { // UT2003 Packages ReadIndex(chunk); chunk.Skip(8); } else if(fileHeader.packageVersion >= 100) { // AAO Packages chunk.Skip(4); ReadIndex(chunk); chunk.Skip(4); } else if(fileHeader.packageVersion >= 62) { // UT Packages // Mech8.umx and a few other UT tunes have packageVersion = 62. // In CUnSound.cpp, the condition above reads "packageVersion >= 63" but if that is used, those tunes won't load properly. ReadIndex(chunk); chunk.Skip(4); } else { // Old Unreal Packagaes ReadIndex(chunk); } int32 size = ReadIndex(chunk); return {chunk.ReadChunk(size), objName}; } } // namespace UMX OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/UMXTools.h0000644000175000017500000000415714052666041017751 00000000000000/* * UMXTools.h * ------------ * Purpose: UMX/UAX (Unreal) helper functions * Notes : (currently none) * Authors: OpenMPT Devs (inspired by code from http://wiki.beyondunreal.com/Legacy:Package_File_Format) * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" OPENMPT_NAMESPACE_BEGIN namespace UMX { // UMX File Header struct FileHeader { char magic[4]; // C1 83 2A 9E uint16le packageVersion; uint16le licenseMode; uint32le flags; uint32le nameCount; uint32le nameOffset; uint32le exportCount; uint32le exportOffset; uint32le importCount; uint32le importOffset; bool IsValid() const; uint32 GetMinimumAdditionalFileSize() const; }; MPT_BINARY_STRUCT(FileHeader, 36) // Check validity of file header CSoundFile::ProbeResult ProbeFileHeader(MemoryFileReader file, const uint64* pfilesize, const char *requiredType); // Read compressed unreal integers - similar to MIDI integers, but signed values are possible. int32 ReadIndex(FileReader &chunk); // Returns true if the given nme exists in the name table. bool FindNameTableEntry(FileReader &file, const FileHeader &fileHeader, const char *name); // Returns true if the given nme exists in the name table. bool FindNameTableEntryMemory(MemoryFileReader &file, const FileHeader &fileHeader, const char *name); // Read an entry from the name table. std::string ReadNameTableEntry(FileReader &chunk, uint16 packageVersion); // Read complete name table. std::vector ReadNameTable(FileReader &file, const FileHeader &fileHeader); // Read import table. std::vector ReadImportTable(FileReader &file, const FileHeader &fileHeader, const std::vector &names); // Read an entry from the import table. int32 ReadImportTableEntry(FileReader &chunk, uint16 packageVersion); // Read an entry from the export table. std::pair ReadExportTableEntry(FileReader &file, const FileHeader &fileHeader, const std::vector &classes, const std::vector &names, const char *filterType); } // namespace UMX OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/UpgradeModule.cpp0000644000175000017500000006724214137512114021346 00000000000000/* * UpdateModule.cpp * ---------------- * Purpose: CSoundFile functions for correcting modules made with previous versions of OpenMPT. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Sndfile.h" #include "plugins/PluginManager.h" #include "../common/mptStringBuffer.h" #include "../common/version.h" OPENMPT_NAMESPACE_BEGIN struct UpgradePatternData { UpgradePatternData(CSoundFile &sf) : sndFile(sf) , compatPlay(sf.m_playBehaviour[MSF_COMPATIBLE_PLAY]) { } void operator() (ModCommand &m) { const CHANNELINDEX curChn = chn; chn++; if(chn >= sndFile.GetNumChannels()) { chn = 0; } if(m.IsPcNote()) { return; } const auto version = sndFile.m_dwLastSavedWithVersion; const auto modType = sndFile.GetType(); if(modType == MOD_TYPE_S3M) { // Out-of-range global volume commands should be ignored in S3M. Fixed in OpenMPT 1.19 (r831). // So for tracks made with older versions of OpenMPT, we limit invalid global volume commands. if(version < MPT_V("1.19.00.00") && m.command == CMD_GLOBALVOLUME) { LimitMax(m.param, ModCommand::PARAM(64)); } } else if(modType & (MOD_TYPE_IT | MOD_TYPE_MPT)) { if(version < MPT_V("1.17.03.02") || (!compatPlay && version < MPT_V("1.20.00.00"))) { if(m.command == CMD_GLOBALVOLUME) { // Out-of-range global volume commands should be ignored in IT. // OpenMPT 1.17.03.02 fixed this in compatible mode, OpenMPT 1.20 fixes it in normal mode as well. // So for tracks made with older versions than OpenMPT 1.17.03.02 or tracks made with 1.17.03.02 <= version < 1.20, we limit invalid global volume commands. LimitMax(m.param, ModCommand::PARAM(128)); } // SC0 and SD0 should be interpreted as SC1 and SD1 in IT files. // OpenMPT 1.17.03.02 fixed this in compatible mode, OpenMPT 1.20 fixes it in normal mode as well. else if(m.command == CMD_S3MCMDEX) { if(m.param == 0xC0) { m.command = CMD_NONE; m.note = NOTE_NOTECUT; } else if(m.param == 0xD0) { m.command = CMD_NONE; } } } // In the IT format, slide commands with both nibbles set should be ignored. // For note volume slides, OpenMPT 1.18 fixes this in compatible mode, OpenMPT 1.20 fixes this in normal mode as well. const bool noteVolSlide = (version < MPT_V("1.18.00.00") || (!compatPlay && version < MPT_V("1.20.00.00"))) && (m.command == CMD_VOLUMESLIDE || m.command == CMD_VIBRATOVOL || m.command == CMD_TONEPORTAVOL || m.command == CMD_PANNINGSLIDE); // OpenMPT 1.20 also fixes this for global volume and channel volume slides. const bool chanVolSlide = (version < MPT_V("1.20.00.00")) && (m.command == CMD_GLOBALVOLSLIDE || m.command == CMD_CHANNELVOLSLIDE); if(noteVolSlide || chanVolSlide) { if((m.param & 0x0F) != 0x00 && (m.param & 0x0F) != 0x0F && (m.param & 0xF0) != 0x00 && (m.param & 0xF0) != 0xF0) { if(m.command == CMD_GLOBALVOLSLIDE) m.param &= 0xF0; else m.param &= 0x0F; } } if(version < MPT_V("1.22.01.04") && version != MPT_V("1.22.00.00")) // Ignore compatibility export { // OpenMPT 1.22.01.04 fixes illegal (out of range) instrument numbers; they should do nothing. In previous versions, they stopped the playing sample. if(sndFile.GetNumInstruments() && m.instr > sndFile.GetNumInstruments() && !compatPlay) { m.volcmd = VOLCMD_VOLUME; m.vol = 0; } } // Command I11 accidentally behaved the same as command I00 with compatible IT tremor and old effects disabled if(m.command == CMD_TREMOR && m.param == 0x11 && version < MPT_V("1.29.12.02") && sndFile.m_playBehaviour[kITTremor] && !sndFile.m_SongFlags[SONG_ITOLDEFFECTS]) { m.param = 0; } } else if(modType == MOD_TYPE_XM) { // Something made be believe that out-of-range global volume commands are ignored in XM // just like they are ignored in IT, but apparently they are not. Aaaaaargh! if(((version >= MPT_V("1.17.03.02") && compatPlay) || (version >= MPT_V("1.20.00.00"))) && version < MPT_V("1.24.02.02") && m.command == CMD_GLOBALVOLUME && m.param > 64) { m.command = CMD_NONE; } if(version < MPT_V("1.19.00.00") || (!compatPlay && version < MPT_V("1.20.00.00"))) { if(m.command == CMD_OFFSET && m.volcmd == VOLCMD_TONEPORTAMENTO) { // If there are both a portamento and an offset effect, the portamento should be preferred in XM files. // OpenMPT 1.19 fixed this in compatible mode, OpenMPT 1.20 fixes it in normal mode as well. m.command = CMD_NONE; } } if(version < MPT_V("1.20.01.10") && m.volcmd == VOLCMD_TONEPORTAMENTO && m.command == CMD_TONEPORTAMENTO && (m.vol != 0 || compatPlay) && m.param != 0) { // Mx and 3xx on the same row does weird things in FT2: 3xx is completely ignored and the Mx parameter is doubled. Fixed in revision 1312 / OpenMPT 1.20.01.10 // Previously the values were just added up, so let's fix this! m.volcmd = VOLCMD_NONE; const uint16 param = static_cast(m.param) + static_cast(m.vol << 4); m.param = mpt::saturate_cast(param); } if(version < MPT_V("1.22.07.09") && m.command == CMD_SPEED && m.param == 0) { // OpenMPT can emulate FT2's F00 behaviour now. m.command = CMD_NONE; } } if(version < MPT_V("1.20.00.00")) { // Pattern Delay fixes const bool fixS6x = (m.command == CMD_S3MCMDEX && (m.param & 0xF0) == 0x60); // We also fix X6x commands in hacked XM files, since they are treated identically to the S6x command in IT/S3M files. // We don't treat them in files made with OpenMPT 1.18+ that have compatible play enabled, though, since they are ignored there anyway. const bool fixX6x = (m.command == CMD_XFINEPORTAUPDOWN && (m.param & 0xF0) == 0x60 && (!(compatPlay && modType == MOD_TYPE_XM) || version < MPT_V("1.18.00.00"))); if(fixS6x || fixX6x) { // OpenMPT 1.20 fixes multiple fine pattern delays on the same row. Previously, only the last command was considered, // but all commands should be added up. Since Scream Tracker 3 itself doesn't support S6x, we also use Impulse Tracker's behaviour here, // since we can assume that most S3Ms that make use of S6x were composed with Impulse Tracker. for(ModCommand *fixCmd = (&m) - curChn; fixCmd < &m; fixCmd++) { if((fixCmd->command == CMD_S3MCMDEX || fixCmd->command == CMD_XFINEPORTAUPDOWN) && (fixCmd->param & 0xF0) == 0x60) { fixCmd->command = CMD_NONE; } } } if(m.command == CMD_S3MCMDEX && (m.param & 0xF0) == 0xE0) { // OpenMPT 1.20 fixes multiple pattern delays on the same row. Previously, only the *last* command was considered, // but Scream Tracker 3 and Impulse Tracker only consider the *first* command. for(ModCommand *fixCmd = (&m) - curChn; fixCmd < &m; fixCmd++) { if(fixCmd->command == CMD_S3MCMDEX && (fixCmd->param & 0xF0) == 0xE0) { fixCmd->command = CMD_NONE; } } } } if(m.volcmd == VOLCMD_VIBRATODEPTH && version < MPT_V("1.27.00.37") && version != MPT_V("1.27.00.00")) { // Fix handling of double vibrato commands - previously only one of them was applied at a time if(m.command == CMD_VIBRATOVOL && m.vol > 0) { m.command = CMD_VOLUMESLIDE; } else if((m.command == CMD_VIBRATO || m.command == CMD_FINEVIBRATO) && (m.param & 0x0F) == 0) { m.command = CMD_VIBRATO; m.param |= (m.vol & 0x0F); m.volcmd = VOLCMD_NONE; } else if(m.command == CMD_VIBRATO || m.command == CMD_VIBRATOVOL || m.command == CMD_FINEVIBRATO) { m.volcmd = VOLCMD_NONE; } } // Volume column offset in IT/XM is bad, mkay? if(modType != MOD_TYPE_MPT && m.volcmd == VOLCMD_OFFSET && m.command == CMD_NONE) { m.command = CMD_OFFSET; m.param = m.vol << 3; m.volcmd = VOLCMD_NONE; } // Previously CMD_OFFSET simply overrode VOLCMD_OFFSET, now they work together as a combined command if(m.volcmd == VOLCMD_OFFSET && m.command == CMD_OFFSET && version < MPT_V("1.30.00.14")) { if(m.param != 0 || m.vol == 0) m.volcmd = VOLCMD_NONE; else m.command = CMD_NONE; } } const CSoundFile &sndFile; CHANNELINDEX chn = 0; const bool compatPlay; }; void CSoundFile::UpgradeModule() { if(m_dwLastSavedWithVersion < MPT_V("1.17.02.46") && m_dwLastSavedWithVersion != MPT_V("1.17.00.00")) { // Compatible playback mode didn't exist in earlier versions, so definitely disable it. m_playBehaviour.reset(MSF_COMPATIBLE_PLAY); } const bool compatModeIT = m_playBehaviour[MSF_COMPATIBLE_PLAY] && (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)); const bool compatModeXM = m_playBehaviour[MSF_COMPATIBLE_PLAY] && GetType() == MOD_TYPE_XM; if(m_dwLastSavedWithVersion < MPT_V("1.20.00.00")) { for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++) if(Instruments[i] != nullptr) { ModInstrument *ins = Instruments[i]; // Previously, volume swing values ranged from 0 to 64. They should reach from 0 to 100 instead. ins->nVolSwing = static_cast(std::min(static_cast(ins->nVolSwing * 100 / 64), uint32(100))); if(!compatModeIT || m_dwLastSavedWithVersion < MPT_V("1.18.00.00")) { // Previously, Pitch/Pan Separation was only half depth (plot twist: it was actually only quarter depth). // This was corrected in compatible mode in OpenMPT 1.18, and in OpenMPT 1.20 it is corrected in normal mode as well. ins->nPPS = (ins->nPPS + (ins->nPPS >= 0 ? 1 : -1)) / 2; } if(!compatModeIT || m_dwLastSavedWithVersion < MPT_V("1.17.03.02")) { // IT compatibility 24. Short envelope loops // Previously, the pitch / filter envelope loop handling was broken, the loop was shortened by a tick (like in XM). // This was corrected in compatible mode in OpenMPT 1.17.03.02, and in OpenMPT 1.20 it is corrected in normal mode as well. ins->GetEnvelope(ENV_PITCH).Convert(MOD_TYPE_XM, GetType()); } if(m_dwLastSavedWithVersion >= MPT_V("1.17.00.00") && m_dwLastSavedWithVersion < MPT_V("1.17.02.50")) { // If there are any plugins that can receive volume commands, enable volume bug emulation. if(ins->nMixPlug && ins->HasValidMIDIChannel()) { m_playBehaviour.set(kMIDICCBugEmulation); } } if(m_dwLastSavedWithVersion < MPT_V("1.17.02.50") && (ins->nVolSwing | ins->nPanSwing | ins->nCutSwing | ins->nResSwing)) { // If there are any instruments with random variation, enable the old random variation behaviour. m_playBehaviour.set(kMPTOldSwingBehaviour); break; } } if((GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) && (m_dwLastSavedWithVersion < MPT_V("1.17.03.02") || !compatModeIT)) { // In the IT format, a sweep value of 0 shouldn't apply vibrato at all. Previously, a value of 0 was treated as "no sweep". // In OpenMPT 1.17.03.02, this was corrected in compatible mode, in OpenMPT 1.20 it is corrected in normal mode as well, // so we have to fix the setting while loading. for(SAMPLEINDEX i = 1; i <= GetNumSamples(); i++) { if(Samples[i].nVibSweep == 0 && (Samples[i].nVibDepth | Samples[i].nVibRate)) { Samples[i].nVibSweep = 255; } } } // Fix old nasty broken (non-standard) MIDI configs in files. m_MidiCfg.UpgradeMacros(); } if(m_dwLastSavedWithVersion < MPT_V("1.20.02.10") && m_dwLastSavedWithVersion != MPT_V("1.20.00.00") && (GetType() & (MOD_TYPE_XM | MOD_TYPE_IT | MOD_TYPE_MPT))) { bool instrPlugs = false; // Old pitch wheel commands were closest to sample pitch bend commands if the PWD is 13. for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++) { if(Instruments[i] != nullptr && Instruments[i]->nMidiChannel != MidiNoChannel) { Instruments[i]->midiPWD = 13; instrPlugs = true; } } if(instrPlugs) { m_playBehaviour.set(kOldMIDIPitchBends); } } if(m_dwLastSavedWithVersion < MPT_V("1.22.03.12") && m_dwLastSavedWithVersion != MPT_V("1.22.00.00") && (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) && (m_playBehaviour[MSF_COMPATIBLE_PLAY] || m_playBehaviour[kMPTOldSwingBehaviour])) { // The "correct" pan swing implementation did nothing if the instrument also had a pan envelope. // If there's a pan envelope, disable pan swing for such modules. for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++) { if(Instruments[i] != nullptr && Instruments[i]->nPanSwing != 0 && Instruments[i]->PanEnv.dwFlags[ENV_ENABLED]) { Instruments[i]->nPanSwing = 0; } } } #ifndef NO_PLUGINS if(m_dwLastSavedWithVersion < MPT_V("1.22.07.01")) { // Convert ANSI plugin path names to UTF-8 (irrelevant in probably 99% of all cases anyway, I think I've never seen a VST plugin with a non-ASCII file name) for(auto &plugin : m_MixPlugins) { #if defined(MODPLUG_TRACKER) const std::string name = mpt::ToCharset(mpt::Charset::UTF8, mpt::Charset::Locale, plugin.Info.szLibraryName); #else const std::string name = mpt::ToCharset(mpt::Charset::UTF8, mpt::Charset::Windows1252, plugin.Info.szLibraryName); #endif plugin.Info.szLibraryName = name; } } #endif // NO_PLUGINS // Starting from OpenMPT 1.22.07.19, FT2-style panning was applied in compatible mix mode. // Starting from OpenMPT 1.23.01.04, FT2-style panning has its own mix mode instead. if(GetType() == MOD_TYPE_XM) { if(m_dwLastSavedWithVersion >= MPT_V("1.22.07.19") && m_dwLastSavedWithVersion < MPT_V("1.23.01.04") && GetMixLevels() == MixLevels::Compatible) { SetMixLevels(MixLevels::CompatibleFT2); } } if(m_dwLastSavedWithVersion < MPT_V("1.25.00.07") && m_dwLastSavedWithVersion != MPT_V("1.25.00.00")) { // Instrument plugins can now receive random volume variation. // For old instruments, disable volume swing in case there was no sample associated. for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++) { if(Instruments[i] != nullptr && Instruments[i]->nVolSwing != 0 && Instruments[i]->nMidiChannel != MidiNoChannel) { bool hasSample = false; for(auto smp : Instruments[i]->Keyboard) { if(smp != 0) { hasSample = true; break; } } if(!hasSample) { Instruments[i]->nVolSwing = 0; } } } } if(m_dwLastSavedWithVersion < MPT_V("1.26.00.00")) { for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++) if(Instruments[i] != nullptr) { ModInstrument *ins = Instruments[i]; // Even after fixing it in OpenMPT 1.18, instrument PPS was only half the depth. ins->nPPS = (ins->nPPS + (ins->nPPS >= 0 ? 1 : -1)) / 2; // OpenMPT 1.18 fixed the depth of random pan in compatible mode. // OpenMPT 1.26 fixes it in normal mode too. if(!compatModeIT || m_dwLastSavedWithVersion < MPT_V("1.18.00.00")) { ins->nPanSwing = (ins->nPanSwing + 3) / 4u; } } } if(m_dwLastSavedWithVersion < MPT_V("1.28.00.12")) { for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++) if(Instruments[i] != nullptr) { if(Instruments[i]->VolEnv.nReleaseNode != ENV_RELEASE_NODE_UNSET) { m_playBehaviour.set(kLegacyReleaseNode); break; } } } if(m_dwLastSavedWithVersion < MPT_V("1.28.03.04")) { for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++) if (Instruments[i] != nullptr) { if(Instruments[i]->pluginVolumeHandling == PLUGIN_VOLUMEHANDLING_MIDI || Instruments[i]->pluginVolumeHandling == PLUGIN_VOLUMEHANDLING_DRYWET) { m_playBehaviour.set(kMIDIVolumeOnNoteOffBug); break; } } } if(m_dwLastSavedWithVersion < MPT_V("1.30.00.54")) { for(SAMPLEINDEX i = 1; i <= GetNumSamples(); i++) { if(Samples[i].HasSampleData() && Samples[i].uFlags[CHN_PINGPONGLOOP | CHN_PINGPONGSUSTAIN]) { m_playBehaviour.set(kImprecisePingPongLoops); break; } } } Patterns.ForEachModCommand(UpgradePatternData(*this)); // Convert compatibility flags // NOTE: Some of these version numbers are just approximations. // Sometimes a quirk flag is shared by several code locations which might have been fixed at different times. // Sometimes the quirk behaviour has been revised over time, in which case the first version that emulated the quirk enables it. struct PlayBehaviourVersion { PlayBehaviour behaviour; Version version; }; if(compatModeIT && m_dwLastSavedWithVersion < MPT_V("1.26.00.00")) { // Pre-1.26: Detailed compatibility flags did not exist. static constexpr PlayBehaviourVersion behaviours[] = { { kTempoClamp, MPT_V("1.17.03.02") }, { kPerChannelGlobalVolSlide, MPT_V("1.17.03.02") }, { kPanOverride, MPT_V("1.17.03.02") }, { kITInstrWithoutNote, MPT_V("1.17.02.46") }, { kITVolColFinePortamento, MPT_V("1.17.02.49") }, { kITArpeggio, MPT_V("1.17.02.49") }, { kITOutOfRangeDelay, MPT_V("1.17.02.49") }, { kITPortaMemoryShare, MPT_V("1.17.02.49") }, { kITPatternLoopTargetReset, MPT_V("1.17.02.49") }, { kITFT2PatternLoop, MPT_V("1.17.02.49") }, { kITPingPongNoReset, MPT_V("1.17.02.51") }, { kITEnvelopeReset, MPT_V("1.17.02.51") }, { kITClearOldNoteAfterCut, MPT_V("1.17.02.52") }, { kITVibratoTremoloPanbrello, MPT_V("1.17.03.02") }, { kITTremor, MPT_V("1.17.03.02") }, { kITRetrigger, MPT_V("1.17.03.02") }, { kITMultiSampleBehaviour, MPT_V("1.17.03.02") }, { kITPortaTargetReached, MPT_V("1.17.03.02") }, { kITPatternLoopBreak, MPT_V("1.17.03.02") }, { kITOffset, MPT_V("1.17.03.02") }, { kITSwingBehaviour, MPT_V("1.18.00.00") }, { kITNNAReset, MPT_V("1.18.00.00") }, { kITSCxStopsSample, MPT_V("1.18.00.01") }, { kITEnvelopePositionHandling, MPT_V("1.18.01.00") }, { kITPortamentoInstrument, MPT_V("1.19.00.01") }, { kITPingPongMode, MPT_V("1.19.00.21") }, { kITRealNoteMapping, MPT_V("1.19.00.30") }, { kITHighOffsetNoRetrig, MPT_V("1.20.00.14") }, { kITFilterBehaviour, MPT_V("1.20.00.35") }, { kITNoSurroundPan, MPT_V("1.20.00.53") }, { kITShortSampleRetrig, MPT_V("1.20.00.54") }, { kITPortaNoNote, MPT_V("1.20.00.56") }, { kRowDelayWithNoteDelay, MPT_V("1.20.00.76") }, { kITFT2DontResetNoteOffOnPorta, MPT_V("1.20.02.06") }, { kITVolColMemory, MPT_V("1.21.01.16") }, { kITPortamentoSwapResetsPos, MPT_V("1.21.01.25") }, { kITEmptyNoteMapSlot, MPT_V("1.21.01.25") }, { kITFirstTickHandling, MPT_V("1.22.07.09") }, { kITSampleAndHoldPanbrello, MPT_V("1.22.07.19") }, { kITClearPortaTarget, MPT_V("1.23.04.03") }, { kITPanbrelloHold, MPT_V("1.24.01.06") }, { kITPanningReset, MPT_V("1.24.01.06") }, { kITPatternLoopWithJumpsOld, MPT_V("1.25.00.19") }, }; for(const auto &b : behaviours) { m_playBehaviour.set(b.behaviour, (m_dwLastSavedWithVersion >= b.version || m_dwLastSavedWithVersion == b.version.Masked(0xFFFF0000u))); } } else if(compatModeXM && m_dwLastSavedWithVersion < MPT_V("1.26.00.00")) { // Pre-1.26: Detailed compatibility flags did not exist. static constexpr PlayBehaviourVersion behaviours[] = { { kTempoClamp, MPT_V("1.17.03.02") }, { kPerChannelGlobalVolSlide, MPT_V("1.17.03.02") }, { kPanOverride, MPT_V("1.17.03.02") }, { kITFT2PatternLoop, MPT_V("1.17.03.02") }, { kFT2Arpeggio, MPT_V("1.17.03.02") }, { kFT2Retrigger, MPT_V("1.17.03.02") }, { kFT2VolColVibrato, MPT_V("1.17.03.02") }, { kFT2PortaNoNote, MPT_V("1.17.03.02") }, { kFT2KeyOff, MPT_V("1.17.03.02") }, { kFT2PanSlide, MPT_V("1.17.03.02") }, { kFT2ST3OffsetOutOfRange, MPT_V("1.17.03.02") }, { kFT2RestrictXCommand, MPT_V("1.18.00.00") }, { kFT2RetrigWithNoteDelay, MPT_V("1.18.00.00") }, { kFT2SetPanEnvPos, MPT_V("1.18.00.00") }, { kFT2PortaIgnoreInstr, MPT_V("1.18.00.01") }, { kFT2VolColMemory, MPT_V("1.18.01.00") }, { kFT2LoopE60Restart, MPT_V("1.18.02.01") }, { kFT2ProcessSilentChannels, MPT_V("1.18.02.01") }, { kFT2ReloadSampleSettings, MPT_V("1.20.00.36") }, { kFT2PortaDelay, MPT_V("1.20.00.40") }, { kFT2Transpose, MPT_V("1.20.00.62") }, { kFT2PatternLoopWithJumps, MPT_V("1.20.00.69") }, { kFT2PortaTargetNoReset, MPT_V("1.20.00.69") }, { kFT2EnvelopeEscape, MPT_V("1.20.00.77") }, { kFT2Tremor, MPT_V("1.20.01.11") }, { kFT2OutOfRangeDelay, MPT_V("1.20.02.02") }, { kFT2Periods, MPT_V("1.22.03.01") }, { kFT2PanWithDelayedNoteOff, MPT_V("1.22.03.02") }, { kFT2VolColDelay, MPT_V("1.22.07.19") }, { kFT2FinetunePrecision, MPT_V("1.22.07.19") }, }; for(const auto &b : behaviours) { m_playBehaviour.set(b.behaviour, m_dwLastSavedWithVersion >= b.version); } } if(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) { // The following behaviours were added in/after OpenMPT 1.26, so are not affected by the upgrade mechanism above. static constexpr PlayBehaviourVersion behaviours[] = { { kITInstrWithNoteOff, MPT_V("1.26.00.01") }, { kITMultiSampleInstrumentNumber, MPT_V("1.27.00.27") }, { kITInstrWithNoteOffOldEffects, MPT_V("1.28.02.06") }, { kITDoNotOverrideChannelPan, MPT_V("1.29.00.22") }, { kITPatternLoopWithJumps, MPT_V("1.29.00.32") }, { kITDCTBehaviour, MPT_V("1.29.00.57") }, { kITPitchPanSeparation, MPT_V("1.30.00.53") }, }; for(const auto &b : behaviours) { if(m_dwLastSavedWithVersion < b.version.Masked(0xFFFF0000u)) m_playBehaviour.reset(b.behaviour); // Full version information available, i.e. not compatibility-exported. else if(m_dwLastSavedWithVersion > b.version.Masked(0xFFFF0000u) && m_dwLastSavedWithVersion < b.version) m_playBehaviour.reset(b.behaviour); } } else if(GetType() == MOD_TYPE_XM) { // The following behaviours were added after OpenMPT 1.26, so are not affected by the upgrade mechanism above. static constexpr PlayBehaviourVersion behaviours[] = { { kFT2NoteOffFlags, MPT_V("1.27.00.27") }, { kRowDelayWithNoteDelay, MPT_V("1.27.00.37") }, { kFT2MODTremoloRampWaveform, MPT_V("1.27.00.37") }, { kFT2PortaUpDownMemory, MPT_V("1.27.00.37") }, { kFT2PanSustainRelease, MPT_V("1.28.00.09") }, { kFT2NoteDelayWithoutInstr, MPT_V("1.28.00.44") }, { kITFT2DontResetNoteOffOnPorta, MPT_V("1.29.00.34") }, { kFT2PortaResetDirection, MPT_V("1.30.00.40") }, }; for(const auto &b : behaviours) { if(m_dwLastSavedWithVersion < b.version) m_playBehaviour.reset(b.behaviour); } } else if(GetType() == MOD_TYPE_S3M) { // We do not store any of these flags in S3M files. static constexpr PlayBehaviourVersion behaviours[] = { { kST3NoMutedChannels, MPT_V("1.18.00.00") }, { kST3EffectMemory, MPT_V("1.20.00.00") }, { kRowDelayWithNoteDelay, MPT_V("1.20.00.00") }, { kST3PortaSampleChange, MPT_V("1.22.00.00") }, { kST3VibratoMemory, MPT_V("1.26.00.00") }, { kITPanbrelloHold, MPT_V("1.26.00.00") }, { KST3PortaAfterArpeggio, MPT_V("1.27.00.00") }, { kST3OffsetWithoutInstrument, MPT_V("1.28.00.00") }, { kST3RetrigAfterNoteCut, MPT_V("1.29.00.00") }, { kFT2ST3OffsetOutOfRange, MPT_V("1.29.00.00") }, { kApplyUpperPeriodLimit, MPT_V("1.30.00.45") }, }; for(const auto &b : behaviours) { if(m_dwLastSavedWithVersion < b.version) m_playBehaviour.reset(b.behaviour); } } if(GetType() == MOD_TYPE_XM && m_dwLastSavedWithVersion < MPT_V("1.19.00.00")) { // This bug was introduced sometime between 1.18.03.00 and 1.19.01.00 m_playBehaviour.set(kFT2NoteDelayWithoutInstr); } if(m_dwLastSavedWithVersion >= MPT_V("1.27.00.27") && m_dwLastSavedWithVersion < MPT_V("1.27.00.49")) { // OpenMPT 1.27 inserted some IT/FT2 flags before the S3M flags that are never saved to files anyway, to keep the flag IDs a bit more compact. // However, it was overlooked that these flags would still be read by OpenMPT 1.26 and thus S3M-specific behaviour would be enabled in IT/XM files. // Hence, in OpenMPT 1.27.00.49 the flag IDs got remapped to no longer conflict with OpenMPT 1.26. // Files made with the affected pre-release versions of OpenMPT 1.27 are upgraded here to use the new IDs. for(int i = 0; i < 5; i++) { m_playBehaviour.set(kFT2NoteOffFlags + i, m_playBehaviour[kST3NoMutedChannels + i]); m_playBehaviour.reset(kST3NoMutedChannels + i); } } if(m_dwLastSavedWithVersion < MPT_V("1.17.00.00")) { // MPT 1.16 has a maximum tempo of 255. m_playBehaviour.set(kTempoClamp); } else if(m_dwLastSavedWithVersion >= MPT_V("1.17.00.00") && m_dwLastSavedWithVersion <= MPT_V("1.20.01.03") && m_dwLastSavedWithVersion != MPT_V("1.20.00.00")) { // OpenMPT introduced some "fixes" that execute regular portamentos also at speed 1. m_playBehaviour.set(kSlidesAtSpeed1); } if(m_SongFlags[SONG_LINEARSLIDES]) { if(m_dwLastSavedWithVersion < MPT_V("1.24.00.00")) { // No frequency slides in Hz before OpenMPT 1.24 m_playBehaviour.reset(kPeriodsAreHertz); } else if(m_dwLastSavedWithVersion >= MPT_V("1.24.00.00") && m_dwLastSavedWithVersion < MPT_V("1.26.00.00") && (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT))) { // Frequency slides were always in Hz rather than periods in this version range. m_playBehaviour.set(kPeriodsAreHertz); } } else { if(m_dwLastSavedWithVersion < MPT_V("1.30.00.36") && m_dwLastSavedWithVersion != MPT_V("1.30.00.00")) { // No frequency slides in Hz before OpenMPT 1.30 m_playBehaviour.reset(kPeriodsAreHertz); } } if(m_playBehaviour[kITEnvelopePositionHandling] && m_dwLastSavedWithVersion >= MPT_V("1.23.01.02") && m_dwLastSavedWithVersion < MPT_V("1.28.00.43")) { // Bug that effectively clamped the release node to the sustain end for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++) if(Instruments[i] != nullptr) { if(Instruments[i]->VolEnv.nReleaseNode != ENV_RELEASE_NODE_UNSET && Instruments[i]->VolEnv.dwFlags[ENV_SUSTAIN] && Instruments[i]->VolEnv.nReleaseNode > Instruments[i]->VolEnv.nSustainEnd) { m_playBehaviour.set(kReleaseNodePastSustainBug); break; } } } if(GetType() & (MOD_TYPE_MPT | MOD_TYPE_S3M)) { for(SAMPLEINDEX i = 1; i <= GetNumSamples(); i++) { if(Samples[i].uFlags[CHN_ADLIB]) { if(GetType() == MOD_TYPE_MPT && GetNumInstruments() && m_dwLastSavedWithVersion >= MPT_V("1.28.00.20") && m_dwLastSavedWithVersion <= MPT_V("1.29.00.55")) m_playBehaviour.set(kOPLNoResetAtEnvelopeEnd); if(m_dwLastSavedWithVersion <= MPT_V("1.30.00.34") && m_dwLastSavedWithVersion != MPT_V("1.30")) m_playBehaviour.reset(kOPLNoteOffOnNoteChange); if(GetType() == MOD_TYPE_S3M && m_dwLastSavedWithVersion < MPT_V("1.29")) m_playBehaviour.set(kOPLRealRetrig); else if(GetType() != MOD_TYPE_S3M) m_playBehaviour.reset(kOPLRealRetrig); break; } } } if(m_dwLastSavedWithVersion >= MPT_V("1.27.00.42") && m_dwLastSavedWithVersion < MPT_V("1.30.00.46") && (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_XM))) { // The Flanger DMO plugin is almost identical to the Chorus... but only almost. // The effect implementation was the same in OpenMPT 1.27-1.29, now it isn't anymore. // As the old implementation continues to exist for the Chorus plugin, there is a legacy wrapper for the Flanger plugin. for(auto &plugin : m_MixPlugins) { if(plugin.Info.dwPluginId1 == kDmoMagic && plugin.Info.dwPluginId2 == int32(0xEFCA3D92) && plugin.pluginData.size() == 32) plugin.Info.szLibraryName = "Flanger (Legacy)"; } } } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/WAVTools.cpp0000644000175000017500000004475414072320411020265 00000000000000/* * WAVTools.cpp * ------------ * Purpose: Definition of WAV file structures and helper functions * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Loaders.h" #include "WAVTools.h" #include "Tagging.h" #include "../common/version.h" #ifndef MODPLUG_NO_FILESAVE #include "mpt/io/io.hpp" #include "mpt/io/io_virtual_wrapper.hpp" #include "../common/mptFileIO.h" #endif OPENMPT_NAMESPACE_BEGIN /////////////////////////////////////////////////////////// // WAV Reading WAVReader::WAVReader(FileReader &inputFile) : file(inputFile) { file.Rewind(); RIFFHeader fileHeader; codePage = 28591; // ISO 8859-1 isDLS = false; subFormat = 0; mayBeCoolEdit16_8 = false; if(!file.ReadStruct(fileHeader) || (fileHeader.magic != RIFFHeader::idRIFF && fileHeader.magic != RIFFHeader::idLIST) || (fileHeader.type != RIFFHeader::idWAVE && fileHeader.type != RIFFHeader::idwave)) { return; } isDLS = (fileHeader.magic == RIFFHeader::idLIST); auto chunks = file.ReadChunks(2); if(chunks.chunks.size() >= 4 && chunks.chunks[1].GetHeader().GetID() == RIFFChunk::iddata && chunks.chunks[1].GetHeader().GetLength() % 2u != 0 && chunks.chunks[2].GetHeader().GetLength() == 0 && chunks.chunks[3].GetHeader().GetID() == RIFFChunk::id____) { // Houston, we have a problem: Old versions of (Open)MPT didn't write RIFF padding bytes. -_- // Luckily, the only RIFF chunk with an odd size those versions would ever write would be the "data" chunk // (which contains the sample data), and its size is only odd iff the sample has an odd length and is in // 8-Bit mono format. In all other cases, the sample size (and thus the chunk size) is even. // And we're even more lucky: The versions of (Open)MPT in question will always write a relatively small // (smaller than 256 bytes) "smpl" chunk after the "data" chunk. This means that after an unpadded sample, // we will always read "mpl?" (? being the length of the "smpl" chunk) as the next chunk magic. The first two // 32-Bit members of the "smpl" chunk are always zero in our case, so we are going to read a chunk length of 0 // next and the next chunk magic, which will always consist of four zero bytes. Hooray! We just checked for those // four zero bytes and can be pretty confident that we should not have applied padding. file.Seek(sizeof(RIFFHeader)); chunks = file.ReadChunks(1); } // Read format chunk FileReader formatChunk = chunks.GetChunk(RIFFChunk::idfmt_); if(!formatChunk.ReadStruct(formatInfo)) { return; } if(formatInfo.format == WAVFormatChunk::fmtPCM && formatChunk.BytesLeft() == 4) { uint16 size = formatChunk.ReadIntLE(); uint16 value = formatChunk.ReadIntLE(); if(size == 2 && value == 1) { // May be Cool Edit 16.8 format. // See SampleFormats.cpp for details. mayBeCoolEdit16_8 = true; } } else if(formatInfo.format == WAVFormatChunk::fmtExtensible) { WAVFormatChunkExtension extFormat; if(!formatChunk.ReadStruct(extFormat)) { return; } subFormat = static_cast(mpt::UUID(extFormat.subFormat).GetData1()); } // Read sample data sampleData = chunks.GetChunk(RIFFChunk::iddata); if(!sampleData.IsValid()) { // The old IMA ADPCM loader code looked for the "pcm " chunk instead of the "data" chunk... // Dunno why (Windows XP's audio recorder saves IMA ADPCM files with a "data" chunk), but we will just look for both. sampleData = chunks.GetChunk(RIFFChunk::idpcm_); } // "fact" chunk should contain sample length of compressed samples. sampleLength = chunks.GetChunk(RIFFChunk::idfact).ReadUint32LE(); if((formatInfo.format != WAVFormatChunk::fmtIMA_ADPCM || sampleLength == 0) && GetSampleSize() != 0) { if((GetBlockAlign() == 0) || (GetBlockAlign() / GetNumChannels() >= 2 * GetSampleSize())) { // Some samples have an incorrect blockAlign / sample size set (e.g. it's 8 in SQUARE.WAV while it should be 1), so let's better not always trust this value. // The idea here is, if block align is off by twice or more, it is unlikely to be describing sample padding inside the block. // Ignore it in this case and calculate the length based on the single sample size and number of channels instead. sampleLength = sampleData.GetLength() / GetSampleSize(); } else { // Correct case (so that 20bit WAVEFORMATEX files work). sampleLength = sampleData.GetLength() / GetBlockAlign(); } } // Determine string encoding codePage = GetFileCodePage(chunks); // Check for loop points, texts, etc... FindMetadataChunks(chunks); // DLS bank chunk wsmpChunk = chunks.GetChunk(RIFFChunk::idwsmp); } void WAVReader::FindMetadataChunks(FileReader::ChunkList &chunks) { // Read sample loop points and other sampler information smplChunk = chunks.GetChunk(RIFFChunk::idsmpl); instChunk = chunks.GetChunk(RIFFChunk::idinst); // Read sample cues cueChunk = chunks.GetChunk(RIFFChunk::idcue_); // Read text chunks FileReader listChunk = chunks.GetChunk(RIFFChunk::idLIST); if(listChunk.ReadMagic("INFO")) { infoChunk = listChunk.ReadChunks(2); } // Read MPT sample information xtraChunk = chunks.GetChunk(RIFFChunk::idxtra); } uint16 WAVReader::GetFileCodePage(FileReader::ChunkList &chunks) { FileReader csetChunk = chunks.GetChunk(RIFFChunk::idCSET); if(!csetChunk.IsValid()) { FileReader iSFT = infoChunk.GetChunk(RIFFChunk::idISFT); if(iSFT.ReadMagic("OpenMPT")) { std::string versionString; iSFT.ReadString(versionString, iSFT.BytesLeft()); versionString = mpt::trim(versionString); Version version = Version::Parse(mpt::ToUnicode(mpt::Charset::ISO8859_1, versionString)); if(version && version < MPT_V("1.28.00.02")) { return 1252; // mpt::Charset::Windows1252; // OpenMPT up to and including 1.28.00.01 wrote metadata in windows-1252 encoding } else { return 28591; // mpt::Charset::ISO8859_1; // as per spec } } else { return 28591; // mpt::Charset::ISO8859_1; // as per spec } } if(!csetChunk.CanRead(2)) { // chunk not parsable return 28591; // mpt::Charset::ISO8859_1; } uint16 codepage = csetChunk.ReadUint16LE(); return codepage; } void WAVReader::ApplySampleSettings(ModSample &sample, mpt::Charset sampleCharset, mpt::charbuf &sampleName) { // Read sample name FileReader textChunk = infoChunk.GetChunk(RIFFChunk::idINAM); if(textChunk.IsValid()) { std::string sampleNameEncoded; textChunk.ReadString(sampleNameEncoded, textChunk.GetLength()); sampleName = mpt::ToCharset(sampleCharset, mpt::ToUnicode(codePage, mpt::Charset::Windows1252, sampleNameEncoded)); } if(isDLS) { // DLS sample -> sample filename sample.filename = sampleName; } // Read software name const bool isOldMPT = infoChunk.GetChunk(RIFFChunk::idISFT).ReadMagic("Modplug Tracker"); // Convert loops WAVSampleInfoChunk sampleInfo; smplChunk.Rewind(); if(smplChunk.ReadStruct(sampleInfo)) { WAVSampleLoop loopData; if(sampleInfo.numLoops > 1 && smplChunk.ReadStruct(loopData)) { // First loop: Sustain loop loopData.ApplyToSample(sample.nSustainStart, sample.nSustainEnd, sample.nLength, sample.uFlags, CHN_SUSTAINLOOP, CHN_PINGPONGSUSTAIN, isOldMPT); } // First loop (if only one loop is present) or second loop (if more than one loop is present): Normal sample loop if(smplChunk.ReadStruct(loopData)) { loopData.ApplyToSample(sample.nLoopStart, sample.nLoopEnd, sample.nLength, sample.uFlags, CHN_LOOP, CHN_PINGPONGLOOP, isOldMPT); } //sample.Transpose((60 - sampleInfo.baseNote) / 12.0); sample.rootNote = static_cast(sampleInfo.baseNote); if(sample.rootNote < 128) sample.rootNote += NOTE_MIN; else sample.rootNote = NOTE_NONE; sample.SanitizeLoops(); } if(sample.rootNote == NOTE_NONE && instChunk.LengthIsAtLeast(sizeof(WAVInstrumentChunk))) { WAVInstrumentChunk inst; instChunk.Rewind(); if(instChunk.ReadStruct(inst)) { sample.rootNote = inst.unshiftedNote; if(sample.rootNote < 128) sample.rootNote += NOTE_MIN; else sample.rootNote = NOTE_NONE; } } // Read cue points if(cueChunk.IsValid()) { uint32 numPoints = cueChunk.ReadUint32LE(); LimitMax(numPoints, mpt::saturate_cast(std::size(sample.cues))); for(uint32 i = 0; i < numPoints; i++) { WAVCuePoint cuePoint; cueChunk.ReadStruct(cuePoint); sample.cues[i] = cuePoint.position; } std::fill(std::begin(sample.cues) + numPoints, std::end(sample.cues), MAX_SAMPLE_LENGTH); } // Read MPT extra info WAVExtraChunk mptInfo; xtraChunk.Rewind(); if(xtraChunk.ReadStruct(mptInfo)) { if(mptInfo.flags & WAVExtraChunk::setPanning) sample.uFlags.set(CHN_PANNING); sample.nPan = std::min(static_cast(mptInfo.defaultPan), uint16(256)); sample.nVolume = std::min(static_cast(mptInfo.defaultVolume), uint16(256)); sample.nGlobalVol = std::min(static_cast(mptInfo.globalVolume), uint16(64)); sample.nVibType = static_cast(mptInfo.vibratoType.get()); sample.nVibSweep = mptInfo.vibratoSweep; sample.nVibDepth = mptInfo.vibratoDepth; sample.nVibRate = mptInfo.vibratoRate; if(xtraChunk.CanRead(MAX_SAMPLENAME)) { // Name present (clipboard only) // FIXME: When modules can have individual encoding in OpenMPT or when // internal metadata gets converted to Unicode, we must adjust this to // also specify encoding. xtraChunk.ReadString(sampleName, MAX_SAMPLENAME); xtraChunk.ReadString(sample.filename, xtraChunk.BytesLeft()); } } } // Apply WAV loop information to a mod sample. void WAVSampleLoop::ApplyToSample(SmpLength &start, SmpLength &end, SmpLength sampleLength, SampleFlags &flags, ChannelFlags enableFlag, ChannelFlags bidiFlag, bool mptLoopFix) const { if(loopEnd == 0) { // Some WAV files seem to have loops going from 0 to 0... We should ignore those. return; } start = std::min(static_cast(loopStart), sampleLength); end = Clamp(static_cast(loopEnd), start, sampleLength); if(!mptLoopFix && end < sampleLength) { // RIFF loop end points are inclusive - old versions of MPT didn't consider this. end++; } flags.set(enableFlag); if(loopType == loopBidi) { flags.set(bidiFlag); } } // Convert internal loop information into a WAV loop. void WAVSampleLoop::ConvertToWAV(SmpLength start, SmpLength end, bool bidi) { identifier = 0; loopType = bidi ? loopBidi : loopForward; loopStart = mpt::saturate_cast(start); // Loop ends are *inclusive* in the RIFF standard, while they're *exclusive* in OpenMPT. if(end > start) { loopEnd = mpt::saturate_cast(end - 1); } else { loopEnd = loopStart; } fraction = 0; playCount = 0; } #ifndef MODPLUG_NO_FILESAVE /////////////////////////////////////////////////////////// // WAV Writing // Output to stream: Initialize with std::ostream*. WAVWriter::WAVWriter(mpt::IO::OFileBase &stream) : s(stream) { // Skip file header for now Seek(sizeof(RIFFHeader)); } WAVWriter::~WAVWriter() { MPT_ASSERT(finalized); } // Finalize the file by closing the last open chunk and updating the file header. Returns total size of file. std::size_t WAVWriter::Finalize() { FinalizeChunk(); RIFFHeader fileHeader; Clear(fileHeader); fileHeader.magic = RIFFHeader::idRIFF; fileHeader.length = static_cast(totalSize - 8); fileHeader.type = RIFFHeader::idWAVE; Seek(0); Write(fileHeader); finalized = true; return totalSize; } // Write a new chunk header to the file. void WAVWriter::StartChunk(RIFFChunk::ChunkIdentifiers id) { FinalizeChunk(); chunkStartPos = position; chunkHeader.id = id; Skip(sizeof(chunkHeader)); } // End current chunk by updating the chunk header and writing a padding byte if necessary. void WAVWriter::FinalizeChunk() { if(chunkStartPos != 0) { const std::size_t chunkSize = position - (chunkStartPos + sizeof(RIFFChunk)); chunkHeader.length = mpt::saturate_cast(chunkSize); std::size_t curPos = position; Seek(chunkStartPos); Write(chunkHeader); Seek(curPos); if((chunkSize % 2u) != 0) { // Write padding uint8 padding = 0; Write(padding); } chunkStartPos = 0; } } // Seek to a position in file. void WAVWriter::Seek(std::size_t pos) { position = pos; totalSize = std::max(totalSize, position); mpt::IO::SeekAbsolute(s, pos); } // Write some data to the file. void WAVWriter::Write(mpt::const_byte_span data) { MPT_ASSERT(!finalized); auto success = mpt::IO::WriteRaw(s, data); MPT_ASSERT(success); // this assertion is useful to catch mis-calculation of required buffer size for pre-allocate in-memory file buffers (like in View_smp.cpp for clipboard) if(!success) { return; } position += data.size(); totalSize = std::max(totalSize, position); } void WAVWriter::WriteBeforeDirect() { MPT_ASSERT(!finalized); } void WAVWriter::WriteAfterDirect(bool success, std::size_t count) { MPT_ASSERT(success); // this assertion is useful to catch mis-calculation of required buffer size for pre-allocate in-memory file buffers (like in View_smp.cpp for clipboard) if (!success) { return; } position += count; totalSize = std::max(totalSize, position); } // Write the WAV format to the file. void WAVWriter::WriteFormat(uint32 sampleRate, uint16 bitDepth, uint16 numChannels, WAVFormatChunk::SampleFormats encoding) { StartChunk(RIFFChunk::idfmt_); WAVFormatChunk wavFormat; Clear(wavFormat); bool extensible = (numChannels > 2); wavFormat.format = static_cast(extensible ? WAVFormatChunk::fmtExtensible : encoding); wavFormat.numChannels = numChannels; wavFormat.sampleRate = sampleRate; wavFormat.blockAlign = (bitDepth * numChannels + 7) / 8; wavFormat.byteRate = wavFormat.sampleRate * wavFormat.blockAlign; wavFormat.bitsPerSample = bitDepth; Write(wavFormat); if(extensible) { WAVFormatChunkExtension extFormat; Clear(extFormat); extFormat.size = sizeof(WAVFormatChunkExtension) - sizeof(uint16); extFormat.validBitsPerSample = bitDepth; switch(numChannels) { case 1: extFormat.channelMask = 0x0004; // FRONT_CENTER break; case 2: extFormat.channelMask = 0x0003; // FRONT_LEFT | FRONT_RIGHT break; case 3: extFormat.channelMask = 0x0103; // FRONT_LEFT | FRONT_RIGHT | BACK_CENTER break; case 4: extFormat.channelMask = 0x0033; // FRONT_LEFT | FRONT_RIGHT | BACK_LEFT | BACK_RIGHT break; default: extFormat.channelMask = 0; break; } extFormat.subFormat = mpt::UUID(static_cast(encoding), 0x0000, 0x0010, 0x800000AA00389B71ull); Write(extFormat); } } // Write text tags to the file. void WAVWriter::WriteMetatags(const FileTags &tags) { StartChunk(RIFFChunk::idCSET); Write(mpt::as_le(uint16(65001))); // code page (UTF-8) Write(mpt::as_le(uint16(0))); // country code (unset) Write(mpt::as_le(uint16(0))); // language (unset) Write(mpt::as_le(uint16(0))); // dialect (unset) StartChunk(RIFFChunk::idLIST); const char info[] = { 'I', 'N', 'F', 'O' }; Write(info); WriteTag(RIFFChunk::idINAM, tags.title); WriteTag(RIFFChunk::idIART, tags.artist); WriteTag(RIFFChunk::idIPRD, tags.album); WriteTag(RIFFChunk::idICRD, tags.year); WriteTag(RIFFChunk::idICMT, tags.comments); WriteTag(RIFFChunk::idIGNR, tags.genre); WriteTag(RIFFChunk::idTURL, tags.url); WriteTag(RIFFChunk::idISFT, tags.encoder); //WriteTag(RIFFChunk:: , tags.bpm); WriteTag(RIFFChunk::idTRCK, tags.trackno); } // Write a single tag into a open idLIST chunk void WAVWriter::WriteTag(RIFFChunk::ChunkIdentifiers id, const mpt::ustring &utext) { std::string text = mpt::ToCharset(mpt::Charset::UTF8, utext); text = text.substr(0, uint32_max - 1u); if(!text.empty()) { const uint32 length = mpt::saturate_cast(text.length() + 1); RIFFChunk chunk; Clear(chunk); chunk.id = static_cast(id); chunk.length = length; Write(chunk); Write(mpt::byte_cast(mpt::span(text.c_str(), length))); if((length % 2u) != 0) { uint8 padding = 0; Write(padding); } } } // Write a sample loop information chunk to the file. void WAVWriter::WriteLoopInformation(const ModSample &sample) { if(!sample.uFlags[CHN_LOOP | CHN_SUSTAINLOOP] && !ModCommand::IsNote(sample.rootNote)) { return; } StartChunk(RIFFChunk::idsmpl); WAVSampleInfoChunk info; uint32 sampleRate = sample.nC5Speed; if(sampleRate == 0) { sampleRate = ModSample::TransposeToFrequency(sample.RelativeTone, sample.nFineTune); } info.ConvertToWAV(sampleRate, sample.rootNote); // Set up loops WAVSampleLoop loops[2]; Clear(loops); if(sample.uFlags[CHN_SUSTAINLOOP]) { loops[info.numLoops++].ConvertToWAV(sample.nSustainStart, sample.nSustainEnd, sample.uFlags[CHN_PINGPONGSUSTAIN]); } if(sample.uFlags[CHN_LOOP]) { loops[info.numLoops++].ConvertToWAV(sample.nLoopStart, sample.nLoopEnd, sample.uFlags[CHN_PINGPONGLOOP]); } else if(sample.uFlags[CHN_SUSTAINLOOP]) { // Since there are no "loop types" to distinguish between sustain and normal loops, OpenMPT assumes // that the first loop is a sustain loop if there are two loops. If we only want a sustain loop, // we will have to write a second bogus loop. loops[info.numLoops++].ConvertToWAV(0, 0, false); } Write(info); for(uint32 i = 0; i < info.numLoops; i++) { Write(loops[i]); } } // Write a sample's cue points to the file. void WAVWriter::WriteCueInformation(const ModSample &sample) { uint32 numMarkers = 0; for(const auto cue : sample.cues) { if(cue < sample.nLength) numMarkers++; } StartChunk(RIFFChunk::idcue_); Write(mpt::as_le(numMarkers)); uint32 i = 0; for(const auto cue : sample.cues) { if(cue < sample.nLength) { WAVCuePoint cuePoint; cuePoint.ConvertToWAV(i++, cue); Write(cuePoint); } } } // Write MPT's sample information chunk to the file. void WAVWriter::WriteExtraInformation(const ModSample &sample, MODTYPE modType, const char *sampleName) { StartChunk(RIFFChunk::idxtra); WAVExtraChunk mptInfo; mptInfo.ConvertToWAV(sample, modType); Write(mptInfo); if(sampleName != nullptr) { // Write sample name (clipboard only) // FIXME: When modules can have individual encoding in OpenMPT or when // internal metadata gets converted to Unicode, we must adjust this to // also specify encoding. char name[MAX_SAMPLENAME]; mpt::String::WriteBuf(mpt::String::nullTerminated, name) = sampleName; Write(name); char filename[MAX_SAMPLEFILENAME]; mpt::String::WriteBuf(mpt::String::nullTerminated, filename) = sample.filename; Write(filename); } } #endif // MODPLUG_NO_FILESAVE OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/WAVTools.h0000644000175000017500000002607014154731641017735 00000000000000/* * WAVTools.h * ---------- * Purpose: Definition of WAV file structures and helper functions * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "mpt/uuid/uuid.hpp" #include "../common/FileReader.h" #include "Loaders.h" #ifndef MODPLUG_NO_FILESAVE #include "mpt/io/io.hpp" #include "mpt/io/io_virtual_wrapper.hpp" #endif OPENMPT_NAMESPACE_BEGIN struct FileTags; // RIFF header struct RIFFHeader { // 32-Bit chunk identifiers enum RIFFMagic { idRIFF = MagicLE("RIFF"), // magic for WAV files idLIST = MagicLE("LIST"), // magic for samples in DLS banks idWAVE = MagicLE("WAVE"), // type for WAV files idwave = MagicLE("wave"), // type for samples in DLS banks }; uint32le magic; // RIFF (in WAV files) or LIST (in DLS banks) uint32le length; // Size of the file, not including magic and length uint32le type; // WAVE (in WAV files) or wave (in DLS banks) }; MPT_BINARY_STRUCT(RIFFHeader, 12) // General RIFF Chunk header struct RIFFChunk { // 32-Bit chunk identifiers enum ChunkIdentifiers { idfmt_ = MagicLE("fmt "), // Sample format information iddata = MagicLE("data"), // Sample data idpcm_ = MagicLE("pcm "), // IMA ADPCM samples idfact = MagicLE("fact"), // Compressed samples idsmpl = MagicLE("smpl"), // Sampler and loop information idinst = MagicLE("inst"), // Instrument information idLIST = MagicLE("LIST"), // List of chunks idxtra = MagicLE("xtra"), // OpenMPT extra infomration idcue_ = MagicLE("cue "), // Cue points idwsmp = MagicLE("wsmp"), // DLS bank samples idCSET = MagicLE("CSET"), // Character Set id____ = 0x00000000, // Found when loading buggy MPT samples // Identifiers in "LIST" chunk idINAM = MagicLE("INAM"), // title idISFT = MagicLE("ISFT"), // software idICOP = MagicLE("ICOP"), // copyright idIART = MagicLE("IART"), // artist idIPRD = MagicLE("IPRD"), // product (album) idICMT = MagicLE("ICMT"), // comment idIENG = MagicLE("IENG"), // engineer idISBJ = MagicLE("ISBJ"), // subject idIGNR = MagicLE("IGNR"), // genre idICRD = MagicLE("ICRD"), // date created idYEAR = MagicLE("YEAR"), // year idTRCK = MagicLE("TRCK"), // track number idTURL = MagicLE("TURL"), // url }; uint32le id; // See ChunkIdentifiers uint32le length; // Chunk size without header size_t GetLength() const { return length; } ChunkIdentifiers GetID() const { return static_cast(id.get()); } }; MPT_BINARY_STRUCT(RIFFChunk, 8) // Format Chunk struct WAVFormatChunk { // Sample formats enum SampleFormats { fmtPCM = 1, fmtFloat = 3, fmtALaw = 6, fmtULaw = 7, fmtIMA_ADPCM = 17, fmtMP3 = 85, fmtExtensible = 0xFFFE, }; uint16le format; // Sample format, see SampleFormats uint16le numChannels; // Number of audio channels uint32le sampleRate; // Sample rate in Hz uint32le byteRate; // Bytes per second (should be freqHz * blockAlign) uint16le blockAlign; // Size of a sample, in bytes (do not trust this value, it's incorrect in some files) uint16le bitsPerSample; // Bits per sample }; MPT_BINARY_STRUCT(WAVFormatChunk, 16) // Extension of the WAVFormatChunk structure, used if format == formatExtensible struct WAVFormatChunkExtension { uint16le size; uint16le validBitsPerSample; uint32le channelMask; mpt::GUIDms subFormat; }; MPT_BINARY_STRUCT(WAVFormatChunkExtension, 24) // Sample information chunk struct WAVSampleInfoChunk { uint32le manufacturer; uint32le product; uint32le samplePeriod; // 1000000000 / sampleRate uint32le baseNote; // MIDI base note of sample uint32le pitchFraction; uint32le SMPTEFormat; uint32le SMPTEOffset; uint32le numLoops; // number of loops uint32le samplerData; // Set up information void ConvertToWAV(uint32 freq, uint8 rootNote) { manufacturer = 0; product = 0; samplePeriod = 1000000000 / freq; if(rootNote != 0) baseNote = rootNote - NOTE_MIN; else baseNote = NOTE_MIDDLEC - NOTE_MIN; pitchFraction = 0; SMPTEFormat = 0; SMPTEOffset = 0; numLoops = 0; samplerData = 0; } }; MPT_BINARY_STRUCT(WAVSampleInfoChunk, 36) // Sample loop information chunk (found after WAVSampleInfoChunk in "smpl" chunk) struct WAVSampleLoop { // Sample Loop Types enum LoopType { loopForward = 0, loopBidi = 1, loopBackward = 2, }; uint32le identifier; uint32le loopType; // See LoopType uint32le loopStart; // Loop start in samples uint32le loopEnd; // Loop end in samples uint32le fraction; uint32le playCount; // Loop Count, 0 = infinite // Apply WAV loop information to a mod sample. void ApplyToSample(SmpLength &start, SmpLength &end, SmpLength sampleLength, SampleFlags &flags, ChannelFlags enableFlag, ChannelFlags bidiFlag, bool mptLoopFix) const; // Convert internal loop information into a WAV loop. void ConvertToWAV(SmpLength start, SmpLength end, bool bidi); }; MPT_BINARY_STRUCT(WAVSampleLoop, 24) // Instrument information chunk struct WAVInstrumentChunk { uint8 unshiftedNote; // Root key of sample, 0...127 int8 finetune; // Finetune of root key in cents int8 gain; // in dB uint8 lowNote; // Note range, 0...127 uint8 highNote; uint8 lowVelocity; // Velocity range, 0...127 uint8 highVelocity; }; MPT_BINARY_STRUCT(WAVInstrumentChunk, 7) // MPT-specific "xtra" chunk struct WAVExtraChunk { enum Flags { setPanning = 0x20, }; uint32le flags; uint16le defaultPan; uint16le defaultVolume; uint16le globalVolume; uint16le reserved; uint8le vibratoType; uint8le vibratoSweep; uint8le vibratoDepth; uint8le vibratoRate; // Set up sample information void ConvertToWAV(const ModSample &sample, MODTYPE modType) { if(sample.uFlags[CHN_PANNING]) { flags = WAVExtraChunk::setPanning; } else { flags = 0; } defaultPan = sample.nPan; defaultVolume = sample.nVolume; globalVolume = sample.nGlobalVol; vibratoType = sample.nVibType; vibratoSweep = sample.nVibSweep; vibratoDepth = sample.nVibDepth; vibratoRate = sample.nVibRate; if((modType & MOD_TYPE_XM) && (vibratoDepth | vibratoRate)) { // XM vibrato is upside down vibratoSweep = 255 - vibratoSweep; } } }; MPT_BINARY_STRUCT(WAVExtraChunk, 16) // Sample cue point structure for the "cue " chunk struct WAVCuePoint { uint32le id; // Unique identification value uint32le position; // Play order position uint32le riffChunkID; // RIFF ID of corresponding data chunk uint32le chunkStart; // Byte Offset of Data Chunk uint32le blockStart; // Byte Offset to sample of First Channel uint32le offset; // Byte Offset to sample byte of First Channel // Set up sample information void ConvertToWAV(uint32 id_, SmpLength offset_) { id = id_; position = offset_; riffChunkID = static_cast(RIFFChunk::iddata); chunkStart = 0; // we use no Wave List Chunk (wavl) as we have only one data block, so this should be 0. blockStart = 0; // ditto offset = offset_; } }; MPT_BINARY_STRUCT(WAVCuePoint, 24) class WAVReader { protected: FileReader file; FileReader sampleData, smplChunk, instChunk, xtraChunk, wsmpChunk, cueChunk; FileReader::ChunkList infoChunk; FileReader::off_t sampleLength; WAVFormatChunk formatInfo; uint16 subFormat; uint16 codePage; bool isDLS; bool mayBeCoolEdit16_8; uint16 GetFileCodePage(FileReader::ChunkList &chunks); public: WAVReader(FileReader &inputFile); bool IsValid() const { return sampleData.IsValid(); } void FindMetadataChunks(FileReader::ChunkList &chunks); // Self-explanatory getters. WAVFormatChunk::SampleFormats GetSampleFormat() const { return IsExtensibleFormat() ? static_cast(subFormat) : static_cast(formatInfo.format.get()); } uint16 GetNumChannels() const { return formatInfo.numChannels; } uint16 GetBitsPerSample() const { return formatInfo.bitsPerSample; } uint32 GetSampleRate() const { return formatInfo.sampleRate; } uint16 GetBlockAlign() const { return formatInfo.blockAlign; } FileReader GetSampleData() const { return sampleData; } FileReader GetWsmpChunk() const { return wsmpChunk; } bool IsExtensibleFormat() const { return formatInfo.format == WAVFormatChunk::fmtExtensible; } bool MayBeCoolEdit16_8() const { return mayBeCoolEdit16_8; } // Get size of a single sample point, in bytes. uint16 GetSampleSize() const { return static_cast(((static_cast(GetNumChannels()) * static_cast(GetBitsPerSample())) + 7) / 8); } // Get sample length (in samples) SmpLength GetSampleLength() const { return mpt::saturate_cast(sampleLength); } // Apply sample settings from file (loop points, MPT extra settings, ...) to a sample. void ApplySampleSettings(ModSample &sample, mpt::Charset sampleCharset, mpt::charbuf &sampleName); }; #ifndef MODPLUG_NO_FILESAVE class WAVWriter { protected: // Output stream mpt::IO::OFileBase &s; // Cursor position std::size_t position = 0; // Total number of bytes written to file / memory std::size_t totalSize = 0; // Currently written chunk std::size_t chunkStartPos = 0; RIFFChunk chunkHeader; bool finalized = false; public: // Output to stream WAVWriter(mpt::IO::OFileBase &stream); ~WAVWriter(); // Finalize the file by closing the last open chunk and updating the file header. Returns total size of file. std::size_t Finalize(); // Begin writing a new chunk to the file. void StartChunk(RIFFChunk::ChunkIdentifiers id); // Skip some bytes... For example after writing sample data. void Skip(size_t numBytes) { Seek(position + numBytes); } // Get position in file (not counting any changes done to the file from outside this class, i.e. through GetFile()) std::size_t GetPosition() const { return position; } // Write some data to the file. template void Write(const T &data) { Write(mpt::as_raw_memory(data)); } // Write a buffer to the file. void Write(mpt::const_byte_span data); // Use before writing raw data directly to the underlying stream s void WriteBeforeDirect(); // Use after writing raw data directly to the underlying stream s void WriteAfterDirect(bool success, std::size_t count); // Write the WAV format to the file. void WriteFormat(uint32 sampleRate, uint16 bitDepth, uint16 numChannels, WAVFormatChunk::SampleFormats encoding); // Write text tags to the file. void WriteMetatags(const FileTags &tags); // Write a sample loop information chunk to the file. void WriteLoopInformation(const ModSample &sample); // Write a sample's cue points to the file. void WriteCueInformation(const ModSample &sample); // Write MPT's sample information chunk to the file. void WriteExtraInformation(const ModSample &sample, MODTYPE modType, const char *sampleName = nullptr); protected: // Seek to a position in file. void Seek(std::size_t pos); // End current chunk by updating the chunk header and writing a padding byte if necessary. void FinalizeChunk(); // Write a single tag into a open idLIST chunk void WriteTag(RIFFChunk::ChunkIdentifiers id, const mpt::ustring &utext); }; #endif // MODPLUG_NO_FILESAVE OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/WindowedFIR.cpp0000644000175000017500000000623714047736406020743 00000000000000/* * WindowedFIR.cpp * --------------- * Purpose: FIR resampling code * Notes : Original code from modplug-xmms * Authors: OpenMPT Devs * ModPlug-XMMS Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "WindowedFIR.h" #include "mpt/base/numbers.hpp" #include OPENMPT_NAMESPACE_BEGIN double CWindowedFIR::coef( int _PCnr, double _POfs, double _PCut, int _PWidth, int _PType ) //float _PPos, float _PFc, int _PLen ) { const double epsilon = 1e-8; const double _LWidthM1 = _PWidth - 1; const double _LWidthM1Half = 0.5 * _LWidthM1; const double _LPosU = (_PCnr - _POfs); const double _LPIdl = (2.0 * mpt::numbers::pi) / _LWidthM1; double _LPos = _LPosU - _LWidthM1Half; double _LWc, _LSi; if(std::abs(_LPos) < epsilon) { _LWc = 1.0; _LSi = _PCut; } else { switch(_PType) { case WFIR_HANN: _LWc = 0.50 - 0.50 * std::cos(_LPIdl * _LPosU); break; case WFIR_HAMMING: _LWc = 0.54 - 0.46 * std::cos(_LPIdl * _LPosU); break; case WFIR_BLACKMANEXACT: _LWc = 0.42 - 0.50 * std::cos(_LPIdl * _LPosU) + 0.08 * std::cos(2.0 * _LPIdl * _LPosU); break; case WFIR_BLACKMAN3T61: _LWc = 0.44959 - 0.49364 * std::cos(_LPIdl * _LPosU) + 0.05677 * std::cos(2.0 * _LPIdl * _LPosU); break; case WFIR_BLACKMAN3T67: _LWc = 0.42323 - 0.49755 * std::cos(_LPIdl * _LPosU) + 0.07922 * std::cos(2.0 * _LPIdl * _LPosU); break; case WFIR_BLACKMAN4T92: // blackman harris _LWc = 0.35875 - 0.48829 * std::cos(_LPIdl * _LPosU) + 0.14128 * std::cos(2.0 * _LPIdl * _LPosU) - 0.01168 * std::cos(3.0 * _LPIdl * _LPosU); break; case WFIR_BLACKMAN4T74: _LWc = 0.40217 - 0.49703 * std::cos(_LPIdl * _LPosU) + 0.09392 * std::cos(2.0 * _LPIdl * _LPosU) - 0.00183 * std::cos(3.0 * _LPIdl * _LPosU); break; case WFIR_KAISER4T: // kaiser-bessel, alpha~7.5 _LWc = 0.40243 - 0.49804 * std::cos(_LPIdl * _LPosU) + 0.09831 * std::cos(2.0 * _LPIdl * _LPosU) - 0.00122 * std::cos(3.0 * _LPIdl * _LPosU); break; default: _LWc = 1.0; break; } _LPos *= mpt::numbers::pi; _LSi = std::sin(_PCut * _LPos) / _LPos; } return (_LWc * _LSi); } void CWindowedFIR::InitTable(double WFIRCutoff, uint8 WFIRType) { const double _LPcllen = (double)(1 << WFIR_FRACBITS); // number of precalculated lines for 0..1 (-1..0) const double _LNorm = 1.0 / (2.0 * _LPcllen); const double _LCut = WFIRCutoff; for(int _LPcl = 0; _LPcl < WFIR_LUTLEN; _LPcl++) { double _LGain = 0.0, _LCoefs[WFIR_WIDTH]; const double _LOfs = (_LPcl - _LPcllen) * _LNorm; const int _LIdx = _LPcl << WFIR_LOG2WIDTH; for(int _LCc = 0; _LCc < WFIR_WIDTH; _LCc++) { _LGain += (_LCoefs[_LCc] = coef(_LCc, _LOfs, _LCut, WFIR_WIDTH, WFIRType)); } _LGain = 1.0 / _LGain; for(int _LCc = 0; _LCc < WFIR_WIDTH; _LCc++) { #ifdef MPT_INTMIXER double _LCoef = std::floor(0.5 + WFIR_QUANTSCALE * _LCoefs[_LCc] * _LGain); lut[_LIdx + _LCc] = (signed short)((_LCoef < -WFIR_QUANTSCALE) ? -WFIR_QUANTSCALE : ((_LCoef > WFIR_QUANTSCALE) ? WFIR_QUANTSCALE : _LCoef)); #else double _LCoef = _LCoefs[_LCc] * _LGain; lut[_LIdx + _LCc] = (float)_LCoef; #endif // MPT_INTMIXER } } } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/WindowedFIR.h0000644000175000017500000000500214052666041020366 00000000000000/* * WindowedFIR.h * ------------- * Purpose: FIR resampling code * Notes : (currently none) * Authors: OpenMPT Devs * ModPlug-XMMS Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "Mixer.h" OPENMPT_NAMESPACE_BEGIN /* ------------------------------------------------------------------------------------------------ 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. ------------------------------------------------------------------------------------------------ */ #ifdef MPT_INTMIXER // quantizer scale of window coefs - only required for integer mixing inline constexpr int WFIR_QUANTBITS = 15; inline constexpr double WFIR_QUANTSCALE = 1 << WFIR_QUANTBITS; inline constexpr int WFIR_8SHIFT = (WFIR_QUANTBITS - 8); inline constexpr int WFIR_16BITSHIFT = (WFIR_QUANTBITS); using WFIR_TYPE = int16; #else using WFIR_TYPE = mixsample_t; #endif // INTMIXER // log2(number)-1 of precalculated taps range is [4..12] inline constexpr int WFIR_FRACBITS = 12; //10 inline constexpr int WFIR_LUTLEN = ((1 << (WFIR_FRACBITS + 1)) + 1); // number of samples in window inline constexpr int WFIR_LOG2WIDTH = 3; inline constexpr int WFIR_WIDTH = (1 << WFIR_LOG2WIDTH); // cutoff (1.0 == pi/2) // wfir type enum WFIRType { WFIR_HANN = 0, // Hann WFIR_HAMMING = 1, // Hamming WFIR_BLACKMANEXACT = 2, // Blackman Exact WFIR_BLACKMAN3T61 = 3, // Blackman 3-Tap 61 WFIR_BLACKMAN3T67 = 4, // Blackman 3-Tap 67 WFIR_BLACKMAN4T92 = 5, // Blackman-Harris WFIR_BLACKMAN4T74 = 6, // Blackman 4-Tap 74 WFIR_KAISER4T = 7, // Kaiser a=7.5 }; // fir interpolation inline constexpr int WFIR_FRACSHIFT = (16 - (WFIR_FRACBITS + 1 + WFIR_LOG2WIDTH)); inline constexpr int WFIR_FRACMASK = ((((1 << (17 - WFIR_FRACSHIFT)) - 1) & ~(WFIR_WIDTH - 1))); inline constexpr int WFIR_FRACHALVE = (1 << (16 - (WFIR_FRACBITS + 2))); class CWindowedFIR { private: double coef(int,double,double,int,int); public: void InitTable(double WFIRCutoff, uint8 WFIRType); WFIR_TYPE lut[WFIR_LUTLEN * WFIR_WIDTH]; }; OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/XMTools.cpp0000644000175000017500000003041714042045724020153 00000000000000/* * XMTools.cpp * ----------- * Purpose: Definition of XM file structures and helper functions * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Loaders.h" #include "XMTools.h" #include "Sndfile.h" #include "../common/version.h" #include OPENMPT_NAMESPACE_BEGIN // Convert OpenMPT's internal envelope representation to XM envelope data. void XMInstrument::ConvertEnvelopeToXM(const InstrumentEnvelope &mptEnv, uint8le &numPoints, uint8le &flags, uint8le &sustain, uint8le &loopStart, uint8le &loopEnd, EnvType env) { numPoints = static_cast(std::min(std::size_t(12), static_cast(mptEnv.size()))); // Envelope Data for(uint8 i = 0; i < numPoints; i++) { switch(env) { case EnvTypeVol: volEnv[i * 2] = std::min(mptEnv[i].tick, uint16_max); volEnv[i * 2 + 1] = std::min(mptEnv[i].value, uint8(64)); break; case EnvTypePan: panEnv[i * 2] = std::min(mptEnv[i].tick, uint16_max); panEnv[i * 2 + 1] = std::min(mptEnv[i].value, uint8(63)); break; } } // Envelope Flags if(mptEnv.dwFlags[ENV_ENABLED]) flags |= XMInstrument::envEnabled; if(mptEnv.dwFlags[ENV_SUSTAIN]) flags |= XMInstrument::envSustain; if(mptEnv.dwFlags[ENV_LOOP]) flags |= XMInstrument::envLoop; // Envelope Loops sustain = std::min(uint8(12), mptEnv.nSustainStart); loopStart = std::min(uint8(12), mptEnv.nLoopStart); loopEnd = std::min(uint8(12), mptEnv.nLoopEnd); } // Convert OpenMPT's internal sample representation to an XMInstrument. uint16 XMInstrument::ConvertToXM(const ModInstrument &mptIns, bool compatibilityExport) { MemsetZero(*this); // FFF is maximum in the FT2 GUI, but it can also accept other values. MilkyTracker just allows 0...4095 and 32767 ("cut") volFade = static_cast(std::min(mptIns.nFadeOut, uint32(32767))); // Convert envelopes ConvertEnvelopeToXM(mptIns.VolEnv, volPoints, volFlags, volSustain, volLoopStart, volLoopEnd, EnvTypeVol); ConvertEnvelopeToXM(mptIns.PanEnv, panPoints, panFlags, panSustain, panLoopStart, panLoopEnd, EnvTypePan); // Create sample assignment table auto sampleList = GetSampleList(mptIns, compatibilityExport); for(std::size_t i = 0; i < std::size(sampleMap); i++) { if(mptIns.Keyboard[i + 12] > 0) { auto sample = std::find(sampleList.begin(), sampleList.end(), mptIns.Keyboard[i + 12]); if(sample != sampleList.end()) { // Yep, we want to export this sample. sampleMap[i] = static_cast(sample - sampleList.begin()); } } } if(mptIns.nMidiChannel != MidiNoChannel) { midiEnabled = 1; midiChannel = (mptIns.nMidiChannel != MidiMappedChannel ? (mptIns.nMidiChannel - MidiFirstChannel) : 0); } midiProgram = (mptIns.nMidiProgram != 0 ? mptIns.nMidiProgram - 1 : 0); pitchWheelRange = std::min(mptIns.midiPWD, int8(36)); return static_cast(sampleList.size()); } // Get a list of samples that should be written to the file. std::vector XMInstrument::GetSampleList(const ModInstrument &mptIns, bool compatibilityExport) const { std::vector sampleList; // List of samples associated with this instrument std::vector addedToList; // Which samples did we already add to the sample list? uint8 numSamples = 0; for(std::size_t i = 0; i < std::size(sampleMap); i++) { const SAMPLEINDEX smp = mptIns.Keyboard[i + 12]; if(smp > 0) { if(smp > addedToList.size()) { addedToList.resize(smp, false); } if(!addedToList[smp - 1] && numSamples < (compatibilityExport ? 16 : 32)) { // We haven't considered this sample yet. addedToList[smp - 1] = true; numSamples++; sampleList.push_back(smp); } } } return sampleList; } // Convert XM envelope data to an OpenMPT's internal envelope representation. void XMInstrument::ConvertEnvelopeToMPT(InstrumentEnvelope &mptEnv, uint8 numPoints, uint8 flags, uint8 sustain, uint8 loopStart, uint8 loopEnd, EnvType env) const { mptEnv.resize(std::min(numPoints, uint8(12))); // Envelope Data for(uint32 i = 0; i < mptEnv.size(); i++) { switch(env) { case EnvTypeVol: mptEnv[i].tick = volEnv[i * 2]; mptEnv[i].value = static_cast(volEnv[i * 2 + 1]); break; case EnvTypePan: mptEnv[i].tick = panEnv[i * 2]; mptEnv[i].value = static_cast(panEnv[i * 2 + 1]); break; } if(i > 0 && mptEnv[i].tick < mptEnv[i - 1].tick && !(mptEnv[i].tick & 0xFF00)) { // libmikmod code says: "Some broken XM editing program will only save the low byte of the position // value. Try to compensate by adding the missing high byte." // Note: MPT 1.07's XI instrument saver omitted the high byte of envelope nodes. // This might be the source for some broken envelopes in IT and XM files. mptEnv[i].tick |= mptEnv[i - 1].tick & 0xFF00; if(mptEnv[i].tick < mptEnv[i - 1].tick) mptEnv[i].tick += 0x100; } } // Envelope Flags mptEnv.dwFlags.reset(); if((flags & XMInstrument::envEnabled) != 0 && !mptEnv.empty()) mptEnv.dwFlags.set(ENV_ENABLED); // Envelope Loops if(sustain < 12) { if((flags & XMInstrument::envSustain) != 0) mptEnv.dwFlags.set(ENV_SUSTAIN); mptEnv.nSustainStart = mptEnv.nSustainEnd = sustain; } if(loopEnd < 12 && loopEnd >= loopStart) { if((flags & XMInstrument::envLoop) != 0) mptEnv.dwFlags.set(ENV_LOOP); mptEnv.nLoopStart = loopStart; mptEnv.nLoopEnd = loopEnd; } } // Convert an XMInstrument to OpenMPT's internal instrument representation. void XMInstrument::ConvertToMPT(ModInstrument &mptIns) const { mptIns.nFadeOut = volFade; // Convert envelopes ConvertEnvelopeToMPT(mptIns.VolEnv, volPoints, volFlags, volSustain, volLoopStart, volLoopEnd, EnvTypeVol); ConvertEnvelopeToMPT(mptIns.PanEnv, panPoints, panFlags, panSustain, panLoopStart, panLoopEnd, EnvTypePan); // Create sample assignment table for(std::size_t i = 0; i < std::size(sampleMap); i++) { mptIns.Keyboard[i + 12] = sampleMap[i]; } if(midiEnabled) { mptIns.nMidiChannel = midiChannel + MidiFirstChannel; Limit(mptIns.nMidiChannel, uint8(MidiFirstChannel), uint8(MidiLastChannel)); mptIns.nMidiProgram = static_cast(std::min(static_cast(midiProgram), uint16(127)) + 1); } mptIns.midiPWD = static_cast(pitchWheelRange); } // Apply auto-vibrato settings from sample to file. void XMInstrument::ApplyAutoVibratoToXM(const ModSample &mptSmp, MODTYPE fromType) { vibType = mptSmp.nVibType; vibSweep = mptSmp.nVibSweep; vibDepth = mptSmp.nVibDepth; vibRate = mptSmp.nVibRate; if((vibDepth | vibRate) != 0 && !(fromType & MOD_TYPE_XM)) { if(mptSmp.nVibSweep != 0) vibSweep = mpt::saturate_cast(Util::muldivr_unsigned(mptSmp.nVibDepth, 256, mptSmp.nVibSweep)); else vibSweep = 255; } } // Apply auto-vibrato settings from file to a sample. void XMInstrument::ApplyAutoVibratoToMPT(ModSample &mptSmp) const { mptSmp.nVibType = static_cast(vibType.get()); mptSmp.nVibSweep = vibSweep; mptSmp.nVibDepth = vibDepth; mptSmp.nVibRate = vibRate; } // Write stuff to the header that's always necessary (also for empty instruments) void XMInstrumentHeader::Finalise() { size = sizeof(XMInstrumentHeader); if(numSamples > 0) { sampleHeaderSize = sizeof(XMSample); } else { // TODO: FT2 completely ignores MIDI settings (and also the less important stuff) if not at least one (empty) sample is assigned to this instrument! size -= sizeof(XMInstrument); sampleHeaderSize = 0; } } // Convert OpenMPT's internal sample representation to an XMInstrumentHeader. void XMInstrumentHeader::ConvertToXM(const ModInstrument &mptIns, bool compatibilityExport) { numSamples = instrument.ConvertToXM(mptIns, compatibilityExport); mpt::String::WriteBuf(mpt::String::spacePadded, name) = mptIns.name; type = mptIns.nMidiProgram; // If FT2 writes crap here, we can do so, too! (we probably shouldn't, though. This is just for backwards compatibility with old MPT versions.) } // Convert an XMInstrumentHeader to OpenMPT's internal instrument representation. void XMInstrumentHeader::ConvertToMPT(ModInstrument &mptIns) const { instrument.ConvertToMPT(mptIns); // Create sample assignment table for(std::size_t i = 0; i < std::size(instrument.sampleMap); i++) { if(instrument.sampleMap[i] < numSamples) { mptIns.Keyboard[i + 12] = instrument.sampleMap[i]; } else { mptIns.Keyboard[i + 12] = 0; } } mptIns.name = mpt::String::ReadBuf(mpt::String::spacePadded, name); // Old MPT backwards compatibility if(!instrument.midiEnabled) { mptIns.nMidiProgram = type; } } // Convert OpenMPT's internal sample representation to an XIInstrumentHeader. void XIInstrumentHeader::ConvertToXM(const ModInstrument &mptIns, bool compatibilityExport) { numSamples = instrument.ConvertToXM(mptIns, compatibilityExport); memcpy(signature, "Extended Instrument: ", 21); mpt::String::WriteBuf(mpt::String::spacePadded, name) = mptIns.name; eof = 0x1A; const std::string openMptTrackerName = mpt::ToCharset(mpt::Charset::CP437, Version::Current().GetOpenMPTVersionString()); mpt::String::WriteBuf(mpt::String::spacePadded, trackerName) = openMptTrackerName; version = 0x102; } // Convert an XIInstrumentHeader to OpenMPT's internal instrument representation. void XIInstrumentHeader::ConvertToMPT(ModInstrument &mptIns) const { instrument.ConvertToMPT(mptIns); // Fix sample assignment table for(std::size_t i = 12; i < std::size(instrument.sampleMap) + 12; i++) { if(mptIns.Keyboard[i] >= numSamples) { mptIns.Keyboard[i] = 0; } } mptIns.name = mpt::String::ReadBuf(mpt::String::spacePadded, name); } // Convert OpenMPT's internal sample representation to an XMSample. void XMSample::ConvertToXM(const ModSample &mptSmp, MODTYPE fromType, bool compatibilityExport) { MemsetZero(*this); // Volume / Panning vol = static_cast(std::min(mptSmp.nVolume / 4u, 64u)); pan = static_cast(std::min(mptSmp.nPan, uint16(255))); // Sample Frequency if((fromType & (MOD_TYPE_MOD | MOD_TYPE_XM))) { finetune = mptSmp.nFineTune; relnote = mptSmp.RelativeTone; } else { std::tie(relnote, finetune) = ModSample::FrequencyToTranspose(mptSmp.nC5Speed); } flags = 0; if(mptSmp.uFlags[CHN_PINGPONGLOOP]) flags |= XMSample::sampleBidiLoop; else if(mptSmp.uFlags[CHN_LOOP]) flags |= XMSample::sampleLoop; // Sample Length and Loops length = mpt::saturate_cast(mptSmp.nLength); loopStart = mpt::saturate_cast(mptSmp.nLoopStart); loopLength = mpt::saturate_cast(mptSmp.nLoopEnd - mptSmp.nLoopStart); if(mptSmp.uFlags[CHN_16BIT]) { flags |= XMSample::sample16Bit; length *= 2; loopStart *= 2; loopLength *= 2; } if(mptSmp.uFlags[CHN_STEREO] && !compatibilityExport) { flags |= XMSample::sampleStereo; length *= 2; loopStart *= 2; loopLength *= 2; } } // Convert an XMSample to OpenMPT's internal sample representation. void XMSample::ConvertToMPT(ModSample &mptSmp) const { mptSmp.Initialize(MOD_TYPE_XM); // Volume mptSmp.nVolume = vol * 4; LimitMax(mptSmp.nVolume, uint16(256)); // Panning mptSmp.nPan = pan; mptSmp.uFlags = CHN_PANNING; // Sample Frequency mptSmp.nFineTune = finetune; mptSmp.RelativeTone = relnote; // Sample Length and Loops mptSmp.nLength = length; mptSmp.nLoopStart = loopStart; mptSmp.nLoopEnd = mptSmp.nLoopStart + loopLength; if((flags & XMSample::sample16Bit)) { mptSmp.nLength /= 2; mptSmp.nLoopStart /= 2; mptSmp.nLoopEnd /= 2; } if((flags & XMSample::sampleStereo)) { mptSmp.nLength /= 2; mptSmp.nLoopStart /= 2; mptSmp.nLoopEnd /= 2; } if((flags & (XMSample::sampleLoop | XMSample::sampleBidiLoop)) && mptSmp.nLoopEnd > mptSmp.nLoopStart) { mptSmp.uFlags.set(CHN_LOOP); if((flags & XMSample::sampleBidiLoop)) { mptSmp.uFlags.set(CHN_PINGPONGLOOP); } } mptSmp.filename = ""; } // Retrieve the internal sample format flags for this instrument. SampleIO XMSample::GetSampleFormat() const { if(reserved == sampleADPCM && !(flags & (XMSample::sample16Bit | XMSample::sampleStereo))) { // MODPlugin :( return SampleIO(SampleIO::_8bit, SampleIO::mono, SampleIO::littleEndian, SampleIO::ADPCM); } return SampleIO( (flags & XMSample::sample16Bit) ? SampleIO::_16bit : SampleIO::_8bit, (flags & XMSample::sampleStereo) ? SampleIO::stereoSplit : SampleIO::mono, SampleIO::littleEndian, SampleIO::deltaPCM); } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/soundlib/XMTools.h0000644000175000017500000001516714052666041017627 00000000000000/* * XMTools.h * --------- * Purpose: Definition of XM file structures and helper functions * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" OPENMPT_NAMESPACE_BEGIN // XM File Header struct XMFileHeader { enum XMHeaderFlags { linearSlides = 0x01, extendedFilterRange = 0x1000, }; char signature[17]; // "Extended Module: " char songName[20]; // Song Name, not null-terminated (any nulls are treated as spaces) uint8le eof; // DOS EOF Character (0x1A) char trackerName[20]; // Software that was used to create the XM file uint16le version; // File version (1.02 - 1.04 are supported) uint32le size; // Header Size uint16le orders; // Number of Orders uint16le restartPos; // Restart Position uint16le channels; // Number of Channels uint16le patterns; // Number of Patterns uint16le instruments; // Number of Unstruments uint16le flags; // Song Flags uint16le speed; // Default Speed uint16le tempo; // Default Tempo }; MPT_BINARY_STRUCT(XMFileHeader, 80) // XM Instrument Data struct XMInstrument { // Envelope Flags enum XMEnvelopeFlags { envEnabled = 0x01, envSustain = 0x02, envLoop = 0x04, }; uint8le sampleMap[96]; // Note -> Sample assignment uint16le volEnv[24]; // Volume envelope nodes / values (0...64) uint16le panEnv[24]; // Panning envelope nodes / values (0...63) uint8le volPoints; // Volume envelope length uint8le panPoints; // Panning envelope length uint8le volSustain; // Volume envelope sustain point uint8le volLoopStart; // Volume envelope loop start point uint8le volLoopEnd; // Volume envelope loop end point uint8le panSustain; // Panning envelope sustain point uint8le panLoopStart; // Panning envelope loop start point uint8le panLoopEnd; // Panning envelope loop end point uint8le volFlags; // Volume envelope flags uint8le panFlags; // Panning envelope flags uint8le vibType; // Sample Auto-Vibrato Type uint8le vibSweep; // Sample Auto-Vibrato Sweep uint8le vibDepth; // Sample Auto-Vibrato Depth uint8le vibRate; // Sample Auto-Vibrato Rate uint16le volFade; // Volume Fade-Out uint8le midiEnabled; // MIDI Out Enabled (0 / 1) uint8le midiChannel; // MIDI Channel (0...15) uint16le midiProgram; // MIDI Program (0...127) uint16le pitchWheelRange; // MIDI Pitch Wheel Range (0...36 halftones) uint8le muteComputer; // Mute instrument if MIDI is enabled (0 / 1) uint8le reserved[15]; // Reserved enum EnvType { EnvTypeVol, EnvTypePan, }; // Convert OpenMPT's internal envelope representation to XM envelope data. void ConvertEnvelopeToXM(const InstrumentEnvelope &mptEnv, uint8le &numPoints, uint8le &flags, uint8le &sustain, uint8le &loopStart, uint8le &loopEnd, EnvType env); // Convert XM envelope data to an OpenMPT's internal envelope representation. void ConvertEnvelopeToMPT(InstrumentEnvelope &mptEnv, uint8 numPoints, uint8 flags, uint8 sustain, uint8 loopStart, uint8 loopEnd, EnvType env) const; // Convert OpenMPT's internal sample representation to an XMInstrument. uint16 ConvertToXM(const ModInstrument &mptIns, bool compatibilityExport); // Convert an XMInstrument to OpenMPT's internal instrument representation. void ConvertToMPT(ModInstrument &mptIns) const; // Apply auto-vibrato settings from sample to file. void ApplyAutoVibratoToXM(const ModSample &mptSmp, MODTYPE fromType); // Apply auto-vibrato settings from file to a sample. void ApplyAutoVibratoToMPT(ModSample &mptSmp) const; // Get a list of samples that should be written to the file. std::vector GetSampleList(const ModInstrument &mptIns, bool compatibilityExport) const; }; MPT_BINARY_STRUCT(XMInstrument, 230) // XM Instrument Header struct XMInstrumentHeader { uint32le size; // Size of XMInstrumentHeader + XMInstrument char name[22]; // Instrument Name, not null-terminated (any nulls are treated as spaces) uint8le type; // Instrument Type (Apparently FT2 writes some crap here, but it's the same crap for all instruments of the same module!) uint16le numSamples; // Number of Samples associated with instrument uint32le sampleHeaderSize; // Size of XMSample XMInstrument instrument; // Write stuff to the header that's always necessary (also for empty instruments) void Finalise(); // Convert OpenMPT's internal sample representation to an XMInstrument. void ConvertToXM(const ModInstrument &mptIns, bool compatibilityExport); // Convert an XMInstrument to OpenMPT's internal instrument representation. void ConvertToMPT(ModInstrument &mptIns) const; }; MPT_BINARY_STRUCT(XMInstrumentHeader, 263) // XI Instrument Header struct XIInstrumentHeader { enum { fileVersion = 0x102, }; char signature[21]; // "Extended Instrument: " char name[22]; // Instrument Name, not null-terminated (any nulls are treated as spaces) uint8le eof; // DOS EOF Character (0x1A) char trackerName[20]; // Software that was used to create the XI file uint16le version; // File Version (1.02) XMInstrument instrument; uint16le numSamples; // Number of embedded sample headers + samples // Convert OpenMPT's internal sample representation to an XIInstrumentHeader. void ConvertToXM(const ModInstrument &mptIns, bool compatibilityExport); // Convert an XIInstrumentHeader to OpenMPT's internal instrument representation. void ConvertToMPT(ModInstrument &mptIns) const; }; MPT_BINARY_STRUCT(XIInstrumentHeader, 298) // XM Sample Header struct XMSample { enum XMSampleFlags { sampleLoop = 0x01, sampleBidiLoop = 0x02, sample16Bit = 0x10, sampleStereo = 0x20, sampleADPCM = 0xAD, // MODPlugin :( }; uint32le length; // Sample Length (in bytes) uint32le loopStart; // Loop Start (in bytes) uint32le loopLength; // Loop Length (in bytes) uint8le vol; // Default Volume int8le finetune; // Sample Finetune uint8le flags; // Sample Flags uint8le pan; // Sample Panning int8le relnote; // Sample Transpose uint8le reserved; // Reserved (abused for ModPlug's ADPCM compression) char name[22]; // Sample Name, not null-terminated (any nulls are treated as spaces) // Convert OpenMPT's internal sample representation to an XMSample. void ConvertToXM(const ModSample &mptSmp, MODTYPE fromType, bool compatibilityExport); // Convert an XMSample to OpenMPT's internal sample representation. void ConvertToMPT(ModSample &mptSmp) const; // Retrieve the internal sample format flags for this instrument. SampleIO GetSampleFormat() const; }; MPT_BINARY_STRUCT(XMSample, 40) OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/src/0000755000175000017500000000000014175541574015140 500000000000000libopenmpt-0.6.1+release.autotools/src/mpt/0000755000175000017500000000000014175541576015742 500000000000000libopenmpt-0.6.1+release.autotools/src/mpt/audio/0000755000175000017500000000000014175541574017041 500000000000000libopenmpt-0.6.1+release.autotools/src/mpt/audio/sample.hpp0000644000175000017500000000113714047746737020763 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_AUDIO_SAMPLE_HPP #define MPT_AUDIO_SAMPLE_HPP #include "mpt/base/floatingpoint.hpp" #include "mpt/base/integer.hpp" #include "mpt/base/namespace.hpp" #include #include namespace mpt { inline namespace MPT_INLINE_NS { using audio_sample_int = int16; using audio_sample_float = nativefloat; using audio_sample = std::conditional::is_hard, audio_sample_float, audio_sample_int>::type; } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_BINARY_HEX_HPP libopenmpt-0.6.1+release.autotools/src/mpt/audio/span.hpp0000644000175000017500000002457114056664703020442 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_AUDIO_SPAN_HPP #define MPT_AUDIO_SPAN_HPP #include "mpt/base/namespace.hpp" #include #include #include namespace mpt { inline namespace MPT_INLINE_NS { // LxxxLxxxLxxxLxxxLxxx xRxxxRxxxRxxxRxxxRxx template struct audio_span_planar_strided { public: using sample_type = SampleType; private: SampleType * const * m_buffers; std::ptrdiff_t m_frame_stride; std::size_t m_channels; std::size_t m_frames; public: constexpr audio_span_planar_strided(SampleType * const * buffers, std::size_t channels, std::size_t frames, std::ptrdiff_t frame_stride) noexcept : m_buffers(buffers) , m_frame_stride(frame_stride) , m_channels(channels) , m_frames(frames) { return; } SampleType * const * data_planar() const noexcept { return m_buffers; } SampleType * data() const noexcept { return nullptr; } SampleType & operator()(std::size_t channel, std::size_t frame) const { return m_buffers[channel][static_cast(frame) * m_frame_stride]; } bool is_contiguous() const noexcept { return false; } bool channels_are_contiguous() const noexcept { return false; } bool frames_are_contiguous() const noexcept { return false; } std::size_t size_channels() const noexcept { return m_channels; } std::size_t size_frames() const noexcept { return m_frames; } std::size_t size_samples() const noexcept { return m_channels * m_frames; } std::ptrdiff_t frame_stride() const noexcept { return m_frame_stride; } }; // LLLLL RRRRR template struct audio_span_planar { public: using sample_type = SampleType; private: SampleType * const * m_buffers; std::size_t m_channels; std::size_t m_frames; public: constexpr audio_span_planar(SampleType * const * buffers, std::size_t channels, std::size_t frames) noexcept : m_buffers(buffers) , m_channels(channels) , m_frames(frames) { return; } SampleType * const * data_planar() const noexcept { return m_buffers; } SampleType * data() const noexcept { return nullptr; } SampleType & operator()(std::size_t channel, std::size_t frame) const { return m_buffers[channel][frame]; } bool is_contiguous() const noexcept { return false; } bool channels_are_contiguous() const noexcept { return false; } bool frames_are_contiguous() const noexcept { return false; } std::size_t size_channels() const noexcept { return m_channels; } std::size_t size_frames() const noexcept { return m_frames; } std::size_t size_samples() const noexcept { return m_channels * m_frames; } }; // LLLLLRRRRR template struct audio_span_contiguous { public: using sample_type = SampleType; private: SampleType * const m_buffer; std::size_t m_channels; std::size_t m_frames; public: constexpr audio_span_contiguous(SampleType * buffer, std::size_t channels, std::size_t frames) noexcept : m_buffer(buffer) , m_channels(channels) , m_frames(frames) { return; } SampleType * const * data_planar() const noexcept { return nullptr; } SampleType * data() const noexcept { return m_buffer; } SampleType & operator()(std::size_t channel, std::size_t frame) const { return m_buffer[(m_frames * channel) + frame]; } bool is_contiguous() const noexcept { return true; } bool channels_are_contiguous() const noexcept { return true; } bool frames_are_contiguous() const noexcept { return false; } std::size_t size_channels() const noexcept { return m_channels; } std::size_t size_frames() const noexcept { return m_frames; } std::size_t size_samples() const noexcept { return m_channels * m_frames; } }; // LRLRLRLRLR template struct audio_span_interleaved { public: using sample_type = SampleType; private: SampleType * const m_buffer; std::size_t m_channels; std::size_t m_frames; public: constexpr audio_span_interleaved(SampleType * buffer, std::size_t channels, std::size_t frames) noexcept : m_buffer(buffer) , m_channels(channels) , m_frames(frames) { return; } SampleType * const * data_planar() const noexcept { return nullptr; } SampleType * data() const noexcept { return m_buffer; } SampleType & operator()(std::size_t channel, std::size_t frame) const { return m_buffer[m_channels * frame + channel]; } bool is_contiguous() const noexcept { return true; } bool channels_are_contiguous() const noexcept { return false; } bool frames_are_contiguous() const noexcept { return true; } std::size_t size_channels() const noexcept { return m_channels; } std::size_t size_frames() const noexcept { return m_frames; } std::size_t size_samples() const noexcept { return m_channels * m_frames; } }; struct audio_span_frames_are_contiguous_t { }; struct audio_span_channels_are_contiguous_t { }; struct audio_span_channels_are_planar_t { }; struct audio_span_channels_are_planar_and_strided_t { }; // LRLRLRLRLR inline constexpr audio_span_frames_are_contiguous_t audio_span_frames_are_contiguous; // LLLLLRRRRR inline constexpr audio_span_channels_are_contiguous_t audio_span_channels_are_contiguous; // LLLLL RRRRR inline constexpr audio_span_channels_are_planar_t audio_span_channels_are_planar; // LxxxLxxxLxxxLxxxLxxx xRxxxRxxxRxxxRxxxRxx inline constexpr audio_span_channels_are_planar_and_strided_t audio_span_channels_are_planar_and_strided; template struct audio_span { public: using sample_type = SampleType; private: union { SampleType * const contiguous; SampleType * const * const planes; } m_buffer; std::ptrdiff_t m_frame_stride; std::ptrdiff_t m_channel_stride; std::size_t m_channels; std::size_t m_frames; public: constexpr audio_span(audio_span_interleaved buffer) noexcept : m_frame_stride(static_cast(buffer.size_channels())) , m_channel_stride(1) , m_channels(buffer.size_channels()) , m_frames(buffer.size_frames()) { m_buffer.contiguous = buffer.data(); } constexpr audio_span(SampleType * buffer, std::size_t channels, std::size_t frames, audio_span_frames_are_contiguous_t) noexcept : m_frame_stride(static_cast(channels)) , m_channel_stride(1) , m_channels(channels) , m_frames(frames) { m_buffer.contiguous = buffer; } constexpr audio_span(audio_span_contiguous buffer) noexcept : m_frame_stride(1) , m_channel_stride(buffer.size_frames()) , m_channels(buffer.size_channels()) , m_frames(buffer.size_frames()) { m_buffer.contiguous = buffer.data(); } constexpr audio_span(SampleType * buffer, std::size_t channels, std::size_t frames, audio_span_channels_are_contiguous_t) noexcept : m_frame_stride(1) , m_channel_stride(static_cast(frames)) , m_channels(channels) , m_frames(frames) { m_buffer.contiguous = buffer; } constexpr audio_span(audio_span_planar buffer) noexcept : m_frame_stride(1) , m_channel_stride(0) , m_channels(buffer.size_channels()) , m_frames(buffer.size_frames()) { m_buffer.planes = buffer.data_planar(); } constexpr audio_span(SampleType * const * planes, std::size_t channels, std::size_t frames, audio_span_channels_are_planar_t) noexcept : m_frame_stride(1) , m_channel_stride(0) , m_channels(channels) , m_frames(frames) { m_buffer.planes = planes; } constexpr audio_span(audio_span_planar_strided buffer) noexcept : m_frame_stride(static_cast(buffer.frame_stride())) , m_channel_stride(0) , m_channels(buffer.size_channels()) , m_frames(buffer.size_frames()) { m_buffer.planes = buffer.data_planar(); } constexpr audio_span(SampleType * const * planes, std::size_t channels, std::size_t frames, std::ptrdiff_t frame_stride, audio_span_channels_are_planar_and_strided_t) noexcept : m_frame_stride(frame_stride) , m_channel_stride(0) , m_channels(channels) , m_frames(frames) { m_buffer.planes = planes; } bool is_contiguous() const noexcept { return (m_channel_stride != 0); } SampleType * const * data_planar() const noexcept { return (!is_contiguous()) ? m_buffer.planes : nullptr; } SampleType * data() const noexcept { return is_contiguous() ? m_buffer.contiguous : nullptr; } SampleType & operator()(std::size_t channel, std::size_t frame) const { return is_contiguous() ? m_buffer.contiguous[(m_channel_stride * static_cast(channel)) + (m_frame_stride * static_cast(frame))] : m_buffer.planes[channel][frame * static_cast(m_frame_stride)]; } bool channels_are_contiguous() const noexcept { return (m_channel_stride == static_cast(m_frames)); } bool frames_are_contiguous() const noexcept { return (m_frame_stride == static_cast(m_channels)); } std::size_t size_channels() const noexcept { return m_channels; } std::size_t size_frames() const noexcept { return m_frames; } std::size_t size_samples() const noexcept { return m_channels * m_frames; } }; template struct audio_span_with_offset { public: using sample_type = typename Taudio_span::sample_type; private: Taudio_span m_buffer; std::size_t m_offset; public: audio_span_with_offset(Taudio_span buffer, std::size_t offsetFrames) noexcept : m_buffer(buffer) , m_offset(offsetFrames) { return; } sample_type * data() const noexcept { if (!is_contiguous()) { return nullptr; } return m_buffer.data() + (size_channels() * m_offset); } sample_type & operator()(std::size_t channel, std::size_t frame) const { return m_buffer(channel, m_offset + frame); } bool is_contiguous() const noexcept { return m_buffer.is_contiguous() && m_buffer.frames_are_contiguous(); } bool channels_are_contiguous() const noexcept { return m_buffer.channels_are_contiguous(); } bool frames_are_contiguous() const noexcept { return m_buffer.frames_are_contiguous(); } std::size_t size_channels() const noexcept { return m_buffer.size_channels(); } std::size_t size_frames() const noexcept { return m_buffer.size_frames() - m_offset; } std::size_t size_samples() const noexcept { return size_channels() * size_frames(); } }; template inline audio_span_with_offset make_audio_span_with_offset(BufferType buf, std::size_t offsetFrames) noexcept { assert(offsetFrames <= buf.size_frames()); return audio_span_with_offset{buf, offsetFrames}; } } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_AUDIO_SPAN_HPP libopenmpt-0.6.1+release.autotools/src/mpt/base/0000755000175000017500000000000014175541574016652 500000000000000libopenmpt-0.6.1+release.autotools/src/mpt/base/tests/0000755000175000017500000000000014175541575020015 500000000000000libopenmpt-0.6.1+release.autotools/src/mpt/base/tests/tests_base_arithmetic_shift.hpp0000644000175000017500000006510614044173026026203 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_BASE_TESTS_ARITHMETIC_SHIFT_HPP #define MPT_BASE_TESTS_ARITHMETIC_SHIFT_HPP #include "mpt/base/arithmetic_shift.hpp" #include "mpt/base/detect.hpp" #include "mpt/base/namespace.hpp" #include "mpt/test/test.hpp" #include "mpt/test/test_macros.hpp" #include namespace mpt { inline namespace MPT_INLINE_NS { namespace tests { namespace base { namespace arithmetic_shift { #if MPT_COMPILER_CLANG #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wglobal-constructors" #endif MPT_TEST_GROUP_INLINE("mpt/base/arithmetic_shift") #if MPT_COMPILER_CLANG #pragma clang diagnostic pop #endif { MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-32768, 1), mpt::rshift_signed_standard(-32768, 1)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-32767, 1), mpt::rshift_signed_standard(-32767, 1)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-32766, 1), mpt::rshift_signed_standard(-32766, 1)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-2, 1), mpt::rshift_signed_standard(-2, 1)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-1, 1), mpt::rshift_signed_standard(-1, 1)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(0, 1), mpt::rshift_signed_standard(0, 1)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(1, 1), mpt::rshift_signed_standard(1, 1)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(2, 1), mpt::rshift_signed_standard(2, 1)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(32766, 1), mpt::rshift_signed_standard(32766, 1)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(32767, 1), mpt::rshift_signed_standard(32767, 1)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-32768, 14), mpt::rshift_signed_standard(-32768, 14)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-32767, 14), mpt::rshift_signed_standard(-32767, 14)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-32766, 14), mpt::rshift_signed_standard(-32766, 14)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-2, 14), mpt::rshift_signed_standard(-2, 14)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-1, 14), mpt::rshift_signed_standard(-1, 14)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(0, 14), mpt::rshift_signed_standard(0, 14)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(1, 14), mpt::rshift_signed_standard(1, 14)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(2, 14), mpt::rshift_signed_standard(2, 14)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(32766, 14), mpt::rshift_signed_standard(32766, 14)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(32767, 14), mpt::rshift_signed_standard(32767, 14)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-32768, 15), mpt::rshift_signed_standard(-32768, 15)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-32767, 15), mpt::rshift_signed_standard(-32767, 15)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-32766, 15), mpt::rshift_signed_standard(-32766, 15)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-2, 15), mpt::rshift_signed_standard(-2, 15)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-1, 15), mpt::rshift_signed_standard(-1, 15)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(0, 15), mpt::rshift_signed_standard(0, 15)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(1, 15), mpt::rshift_signed_standard(1, 15)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(2, 15), mpt::rshift_signed_standard(2, 15)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(32766, 15), mpt::rshift_signed_standard(32766, 15)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(32767, 15), mpt::rshift_signed_standard(32767, 15)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-32768, 1), mpt::lshift_signed_standard(-32768, 1)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-32767, 1), mpt::lshift_signed_standard(-32767, 1)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-32766, 1), mpt::lshift_signed_standard(-32766, 1)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-2, 1), mpt::lshift_signed_standard(-2, 1)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-1, 1), mpt::lshift_signed_standard(-1, 1)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(0, 1), mpt::lshift_signed_standard(0, 1)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(1, 1), mpt::lshift_signed_standard(1, 1)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(2, 1), mpt::lshift_signed_standard(2, 1)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(32766, 1), mpt::lshift_signed_standard(32766, 1)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(32767, 1), mpt::lshift_signed_standard(32767, 1)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-32768, 14), mpt::lshift_signed_standard(-32768, 14)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-32767, 14), mpt::lshift_signed_standard(-32767, 14)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-32766, 14), mpt::lshift_signed_standard(-32766, 14)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-2, 14), mpt::lshift_signed_standard(-2, 14)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-1, 14), mpt::lshift_signed_standard(-1, 14)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(0, 14), mpt::lshift_signed_standard(0, 14)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(1, 14), mpt::lshift_signed_standard(1, 14)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(2, 14), mpt::lshift_signed_standard(2, 14)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(32766, 14), mpt::lshift_signed_standard(32766, 14)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(32767, 14), mpt::lshift_signed_standard(32767, 14)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-32768, 15), mpt::lshift_signed_standard(-32768, 15)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-32767, 15), mpt::lshift_signed_standard(-32767, 15)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-32766, 15), mpt::lshift_signed_standard(-32766, 15)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-2, 15), mpt::lshift_signed_standard(-2, 15)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-1, 15), mpt::lshift_signed_standard(-1, 15)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(0, 15), mpt::lshift_signed_standard(0, 15)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(1, 15), mpt::lshift_signed_standard(1, 15)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(2, 15), mpt::lshift_signed_standard(2, 15)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(32766, 15), mpt::lshift_signed_standard(32766, 15)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(32767, 15), mpt::lshift_signed_standard(32767, 15)); #if MPT_COMPILER_SHIFT_SIGNED MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-32768, 1), (-32768) >> 1); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-32767, 1), (-32767) >> 1); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-32766, 1), (-32766) >> 1); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-2, 1), (-2) >> 1); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-1, 1), (-1) >> 1); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(0, 1), (0) >> 1); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(1, 1), (1) >> 1); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(2, 1), (2) >> 1); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(32766, 1), (32766) >> 1); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(32767, 1), (32767) >> 1); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-32768, 14), (-32768) >> 14); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-32767, 14), (-32767) >> 14); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-32766, 14), (-32766) >> 14); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-2, 14), (-2) >> 14); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-1, 14), (-1) >> 14); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(0, 14), (0) >> 14); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(1, 14), (1) >> 14); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(2, 14), (2) >> 14); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(32766, 14), (32766) >> 14); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(32767, 14), (32767) >> 14); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-32768, 15), (-32768) >> 15); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-32767, 15), (-32767) >> 15); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-32766, 15), (-32766) >> 15); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-2, 15), (-2) >> 15); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-1, 15), (-1) >> 15); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(0, 15), (0) >> 15); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(1, 15), (1) >> 15); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(2, 15), (2) >> 15); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(32766, 15), (32766) >> 15); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(32767, 15), (32767) >> 15); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-32768, 1), (-32768) << 1); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-32767, 1), (-32767) << 1); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-32766, 1), (-32766) << 1); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-2, 1), (-2) << 1); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-1, 1), (-1) << 1); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(0, 1), (0) << 1); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(1, 1), (1) << 1); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(2, 1), (2) << 1); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(32766, 1), (32766) << 1); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(32767, 1), (32767) << 1); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-32768, 14), (-32768) << 14); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-32767, 14), (-32767) << 14); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-32766, 14), (-32766) << 14); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-2, 14), (-2) << 14); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-1, 14), (-1) << 14); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(0, 14), (0) << 14); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(1, 14), (1) << 14); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(2, 14), (2) << 14); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(32766, 14), (32766) << 14); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(32767, 14), (32767) << 14); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-32768, 15), (-32768) << 15); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-32767, 15), (-32767) << 15); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-32766, 15), (-32766) << 15); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-2, 15), (-2) << 15); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-1, 15), (-1) << 15); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(0, 15), (0) << 15); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(1, 15), (1) << 15); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(2, 15), (2) << 15); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(32766, 15), (32766) << 15); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(32767, 15), (32767) << 15); #endif MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(0 - 0x80000000, 1), mpt::rshift_signed_standard(0 - 0x80000000, 1)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-0x7fffffff, 1), mpt::rshift_signed_standard(-0x7fffffff, 1)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-0x7ffffffe, 1), mpt::rshift_signed_standard(-0x7ffffffe, 1)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-1, 1), mpt::rshift_signed_standard(-1, 1)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(0, 1), mpt::rshift_signed_standard(0, 1)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(1, 1), mpt::rshift_signed_standard(1, 1)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(0x7ffffffe, 1), mpt::rshift_signed_standard(0x7ffffffe, 1)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(0x7fffffff, 1), mpt::rshift_signed_standard(0x7fffffff, 1)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(0 - 0x80000000, 31), mpt::rshift_signed_standard(0 - 0x80000000, 31)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-0x7fffffff, 31), mpt::rshift_signed_standard(-0x7fffffff, 31)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-0x7ffffffe, 31), mpt::rshift_signed_standard(-0x7ffffffe, 31)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-1, 31), mpt::rshift_signed_standard(-1, 31)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(0, 31), mpt::rshift_signed_standard(0, 31)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(1, 31), mpt::rshift_signed_standard(1, 31)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(0x7ffffffe, 31), mpt::rshift_signed_standard(0x7ffffffe, 31)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(0x7fffffff, 31), mpt::rshift_signed_standard(0x7fffffff, 31)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(0 - 0x80000000, 1), mpt::lshift_signed_standard(0 - 0x80000000, 1)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-0x7fffffff, 1), mpt::lshift_signed_standard(-0x7fffffff, 1)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-0x7ffffffe, 1), mpt::lshift_signed_standard(-0x7ffffffe, 1)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-1, 1), mpt::lshift_signed_standard(-1, 1)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(0, 1), mpt::lshift_signed_standard(0, 1)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(1, 1), mpt::lshift_signed_standard(1, 1)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(0x7ffffffe, 1), mpt::lshift_signed_standard(0x7ffffffe, 1)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(0x7fffffff, 1), mpt::lshift_signed_standard(0x7fffffff, 1)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(0 - 0x80000000, 31), mpt::lshift_signed_standard(0 - 0x80000000, 31)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-0x7fffffff, 31), mpt::lshift_signed_standard(-0x7fffffff, 31)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-0x7ffffffe, 31), mpt::lshift_signed_standard(-0x7ffffffe, 31)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-1, 31), mpt::lshift_signed_standard(-1, 31)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(0, 31), mpt::lshift_signed_standard(0, 31)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(1, 31), mpt::lshift_signed_standard(1, 31)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(0x7ffffffe, 31), mpt::lshift_signed_standard(0x7ffffffe, 31)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(0x7fffffff, 31), mpt::lshift_signed_standard(0x7fffffff, 31)); #if MPT_COMPILER_SHIFT_SIGNED MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(0 - 0x80000000, 1), mpt::rshift_signed_undefined(0 - 0x80000000, 1)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-0x7fffffff, 1), mpt::rshift_signed_undefined(-0x7fffffff, 1)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-0x7ffffffe, 1), mpt::rshift_signed_undefined(-0x7ffffffe, 1)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-1, 1), mpt::rshift_signed_undefined(-1, 1)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(0, 1), mpt::rshift_signed_undefined(0, 1)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(1, 1), mpt::rshift_signed_undefined(1, 1)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(0x7ffffffe, 1), mpt::rshift_signed_undefined(0x7ffffffe, 1)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(0x7fffffff, 1), mpt::rshift_signed_undefined(0x7fffffff, 1)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(0 - 0x80000000, 31), mpt::rshift_signed_undefined(0 - 0x80000000, 31)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-0x7fffffff, 31), mpt::rshift_signed_undefined(-0x7fffffff, 31)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-0x7ffffffe, 31), mpt::rshift_signed_undefined(-0x7ffffffe, 31)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-1, 31), mpt::rshift_signed_undefined(-1, 31)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(0, 31), mpt::rshift_signed_undefined(0, 31)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(1, 31), mpt::rshift_signed_undefined(1, 31)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(0x7ffffffe, 31), mpt::rshift_signed_undefined(0x7ffffffe, 31)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(0x7fffffff, 31), mpt::rshift_signed_undefined(0x7fffffff, 31)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(0 - 0x80000000, 1), mpt::lshift_signed_undefined(0 - 0x80000000, 1)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-0x7fffffff, 1), mpt::lshift_signed_undefined(-0x7fffffff, 1)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-0x7ffffffe, 1), mpt::lshift_signed_undefined(-0x7ffffffe, 1)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-1, 1), mpt::lshift_signed_undefined(-1, 1)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(0, 1), mpt::lshift_signed_undefined(0, 1)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(1, 1), mpt::lshift_signed_undefined(1, 1)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(0x7ffffffe, 1), mpt::lshift_signed_undefined(0x7ffffffe, 1)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(0x7fffffff, 1), mpt::lshift_signed_undefined(0x7fffffff, 1)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(0 - 0x80000000, 31), mpt::lshift_signed_undefined(0 - 0x80000000, 31)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-0x7fffffff, 31), mpt::lshift_signed_undefined(-0x7fffffff, 31)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-0x7ffffffe, 31), mpt::lshift_signed_undefined(-0x7ffffffe, 31)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-1, 31), mpt::lshift_signed_undefined(-1, 31)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(0, 31), mpt::lshift_signed_undefined(0, 31)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(1, 31), mpt::lshift_signed_undefined(1, 31)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(0x7ffffffe, 31), mpt::lshift_signed_undefined(0x7ffffffe, 31)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(0x7fffffff, 31), mpt::lshift_signed_undefined(0x7fffffff, 31)); #endif MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(0ull - 0x8000000000000000ull, 1), mpt::rshift_signed_standard(0ull - 0x8000000000000000ull, 1)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-0x7fffffffffffffffll, 1), mpt::rshift_signed_standard(-0x7fffffffffffffffll, 1)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-0x7ffffffffffffffell, 1), mpt::rshift_signed_standard(-0x7ffffffffffffffell, 1)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-1ll, 1), mpt::rshift_signed_standard(-1ll, 1)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(0ll, 1), mpt::rshift_signed_standard(0ll, 1)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(1ll, 1), mpt::rshift_signed_standard(1ll, 1)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(0x7ffffffffffffffell, 1), mpt::rshift_signed_standard(0x7ffffffffffffffell, 1)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(0x7fffffffffffffffll, 1), mpt::rshift_signed_standard(0x7fffffffffffffffll, 1)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(0ull - 0x8000000000000000ull, 63), mpt::rshift_signed_standard(0ull - 0x8000000000000000ull, 63)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-0x7fffffffffffffffll, 63), mpt::rshift_signed_standard(-0x7fffffffffffffffll, 63)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-0x7ffffffffffffffell, 63), mpt::rshift_signed_standard(-0x7ffffffffffffffell, 63)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-1ll, 63), mpt::rshift_signed_standard(-1ll, 63)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(0ll, 63), mpt::rshift_signed_standard(0ll, 63)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(1ll, 63), mpt::rshift_signed_standard(1ll, 63)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(0x7ffffffffffffffell, 63), mpt::rshift_signed_standard(0x7ffffffffffffffell, 63)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(0x7fffffffffffffffll, 63), mpt::rshift_signed_standard(0x7fffffffffffffffll, 63)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(0ull - 0x8000000000000000ull, 1), mpt::lshift_signed_standard(0ull - 0x8000000000000000ull, 1)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-0x7fffffffffffffffll, 1), mpt::lshift_signed_standard(-0x7fffffffffffffffll, 1)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-0x7ffffffffffffffell, 1), mpt::lshift_signed_standard(-0x7ffffffffffffffell, 1)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-1ll, 1), mpt::lshift_signed_standard(-1ll, 1)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(0ll, 1), mpt::lshift_signed_standard(0ll, 1)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(1ll, 1), mpt::lshift_signed_standard(1ll, 1)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(0x7ffffffffffffffell, 1), mpt::lshift_signed_standard(0x7ffffffffffffffell, 1)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(0x7fffffffffffffffll, 1), mpt::lshift_signed_standard(0x7fffffffffffffffll, 1)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(0ull - 0x8000000000000000ull, 63), mpt::lshift_signed_standard(0ull - 0x8000000000000000ull, 63)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-0x7fffffffffffffffll, 63), mpt::lshift_signed_standard(-0x7fffffffffffffffll, 63)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-0x7ffffffffffffffell, 63), mpt::lshift_signed_standard(-0x7ffffffffffffffell, 63)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-1ll, 63), mpt::lshift_signed_standard(-1ll, 63)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(0ll, 63), mpt::lshift_signed_standard(0ll, 63)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(1ll, 63), mpt::lshift_signed_standard(1ll, 63)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(0x7ffffffffffffffell, 63), mpt::lshift_signed_standard(0x7ffffffffffffffell, 63)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(0x7fffffffffffffffll, 63), mpt::lshift_signed_standard(0x7fffffffffffffffll, 63)); #if MPT_COMPILER_SHIFT_SIGNED MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(0ull - 0x8000000000000000ull, 1), mpt::rshift_signed_undefined(0ull - 0x8000000000000000ull, 1)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-0x7fffffffffffffffll, 1), mpt::rshift_signed_undefined(-0x7fffffffffffffffll, 1)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-0x7ffffffffffffffell, 1), mpt::rshift_signed_undefined(-0x7ffffffffffffffell, 1)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-1ll, 1), mpt::rshift_signed_undefined(-1ll, 1)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(0ll, 1), mpt::rshift_signed_undefined(0ll, 1)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(1ll, 1), mpt::rshift_signed_undefined(1ll, 1)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(0x7ffffffffffffffell, 1), mpt::rshift_signed_undefined(0x7ffffffffffffffell, 1)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(0x7fffffffffffffffll, 1), mpt::rshift_signed_undefined(0x7fffffffffffffffll, 1)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(0ull - 0x8000000000000000ull, 63), mpt::rshift_signed_undefined(0ull - 0x8000000000000000ull, 63)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-0x7fffffffffffffffll, 63), mpt::rshift_signed_undefined(-0x7fffffffffffffffll, 63)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-0x7ffffffffffffffell, 63), mpt::rshift_signed_undefined(-0x7ffffffffffffffell, 63)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(-1ll, 63), mpt::rshift_signed_undefined(-1ll, 63)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(0ll, 63), mpt::rshift_signed_undefined(0ll, 63)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(1ll, 63), mpt::rshift_signed_undefined(1ll, 63)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(0x7ffffffffffffffell, 63), mpt::rshift_signed_undefined(0x7ffffffffffffffell, 63)); MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed(0x7fffffffffffffffll, 63), mpt::rshift_signed_undefined(0x7fffffffffffffffll, 63)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(0ull - 0x8000000000000000ull, 1), mpt::lshift_signed_undefined(0ull - 0x8000000000000000ull, 1)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-0x7fffffffffffffffll, 1), mpt::lshift_signed_undefined(-0x7fffffffffffffffll, 1)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-0x7ffffffffffffffell, 1), mpt::lshift_signed_undefined(-0x7ffffffffffffffell, 1)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-1ll, 1), mpt::lshift_signed_undefined(-1ll, 1)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(0ll, 1), mpt::lshift_signed_undefined(0ll, 1)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(1ll, 1), mpt::lshift_signed_undefined(1ll, 1)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(0x7ffffffffffffffell, 1), mpt::lshift_signed_undefined(0x7ffffffffffffffell, 1)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(0x7fffffffffffffffll, 1), mpt::lshift_signed_undefined(0x7fffffffffffffffll, 1)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(0ull - 0x8000000000000000ull, 63), mpt::lshift_signed_undefined(0ull - 0x8000000000000000ull, 63)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-0x7fffffffffffffffll, 63), mpt::lshift_signed_undefined(-0x7fffffffffffffffll, 63)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-0x7ffffffffffffffell, 63), mpt::lshift_signed_undefined(-0x7ffffffffffffffell, 63)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(-1ll, 63), mpt::lshift_signed_undefined(-1ll, 63)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(0ll, 63), mpt::lshift_signed_undefined(0ll, 63)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(1ll, 63), mpt::lshift_signed_undefined(1ll, 63)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(0x7ffffffffffffffell, 63), mpt::lshift_signed_undefined(0x7ffffffffffffffell, 63)); MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed(0x7fffffffffffffffll, 63), mpt::lshift_signed_undefined(0x7fffffffffffffffll, 63)); #endif } } // namespace arithmetic_shift } // namespace base } // namespace tests } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_BASE_TESTS_ARITHMETIC_SHIFT_HPP libopenmpt-0.6.1+release.autotools/src/mpt/base/tests/tests_base_bit.hpp0000644000175000017500000002455414044173026023435 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_BASE_TESTS_BASE_BIT_HPP #define MPT_BASE_TESTS_BASE_BIT_HPP #include "mpt/base/detect.hpp" #include "mpt/base/integer.hpp" #include "mpt/base/macros.hpp" #include "mpt/base/namespace.hpp" #include "mpt/test/test.hpp" #include "mpt/test/test_macros.hpp" namespace mpt { inline namespace MPT_INLINE_NS { namespace tests { namespace base { namespace bit { #if MPT_COMPILER_CLANG #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wglobal-constructors" #endif MPT_TEST_GROUP_INLINE("mpt/base/bit") #if MPT_COMPILER_CLANG #pragma clang diagnostic pop #endif { #if MPT_CXX_BEFORE(20) MPT_TEST_EXPECT_EQUAL(mpt::get_endian(), mpt::endian_probe()); #endif MPT_MAYBE_CONSTANT_IF(mpt::endian_is_little()) { MPT_TEST_EXPECT_EQUAL(mpt::get_endian(), mpt::endian::little); MPT_MAYBE_CONSTANT_IF((mpt::endian::native == mpt::endian::little) || (mpt::endian::native == mpt::endian::big)) { MPT_TEST_EXPECT_EQUAL(mpt::endian::native, mpt::endian::little); } #if MPT_CXX_BEFORE(20) MPT_TEST_EXPECT_EQUAL(mpt::endian_probe(), mpt::endian::little); #endif } MPT_MAYBE_CONSTANT_IF(mpt::endian_is_big()) { MPT_TEST_EXPECT_EQUAL(mpt::get_endian(), mpt::endian::big); MPT_MAYBE_CONSTANT_IF((mpt::endian::native == mpt::endian::little) || (mpt::endian::native == mpt::endian::big)) { MPT_TEST_EXPECT_EQUAL(mpt::endian::native, mpt::endian::big); } #if MPT_CXX_BEFORE(20) MPT_TEST_EXPECT_EQUAL(mpt::endian_probe(), mpt::endian::big); #endif } MPT_TEST_EXPECT_EQUAL(mpt::popcount(static_cast(int32(-1))), 32); MPT_TEST_EXPECT_EQUAL(mpt::popcount(0u), 0); MPT_TEST_EXPECT_EQUAL(mpt::popcount(1u), 1); MPT_TEST_EXPECT_EQUAL(mpt::popcount(2u), 1); MPT_TEST_EXPECT_EQUAL(mpt::popcount(3u), 2); MPT_TEST_EXPECT_EQUAL(mpt::has_single_bit(0u), false); MPT_TEST_EXPECT_EQUAL(mpt::has_single_bit(1u), true); MPT_TEST_EXPECT_EQUAL(mpt::has_single_bit(2u), true); MPT_TEST_EXPECT_EQUAL(mpt::has_single_bit(3u), false); MPT_TEST_EXPECT_EQUAL(mpt::has_single_bit(4u), true); MPT_TEST_EXPECT_EQUAL(mpt::has_single_bit(5u), false); MPT_TEST_EXPECT_EQUAL(mpt::has_single_bit(6u), false); MPT_TEST_EXPECT_EQUAL(mpt::has_single_bit(7u), false); MPT_TEST_EXPECT_EQUAL(mpt::has_single_bit(8u), true); MPT_TEST_EXPECT_EQUAL(mpt::has_single_bit(9u), false); MPT_TEST_EXPECT_EQUAL(mpt::has_single_bit(uint32(0x7fffffffu)), false); MPT_TEST_EXPECT_EQUAL(mpt::has_single_bit(uint32(0x80000000u)), true); MPT_TEST_EXPECT_EQUAL(mpt::has_single_bit(uint32(0x80000001u)), false); MPT_TEST_EXPECT_EQUAL(mpt::has_single_bit(uint32(0xfffffffeu)), false); MPT_TEST_EXPECT_EQUAL(mpt::has_single_bit(uint32(0xffffffffu)), false); MPT_TEST_EXPECT_EQUAL(mpt::bit_ceil(0u), 1u); MPT_TEST_EXPECT_EQUAL(mpt::bit_ceil(1u), 1u); MPT_TEST_EXPECT_EQUAL(mpt::bit_ceil(2u), 2u); MPT_TEST_EXPECT_EQUAL(mpt::bit_ceil(3u), 4u); MPT_TEST_EXPECT_EQUAL(mpt::bit_ceil(4u), 4u); MPT_TEST_EXPECT_EQUAL(mpt::bit_ceil(5u), 8u); MPT_TEST_EXPECT_EQUAL(mpt::bit_ceil(6u), 8u); MPT_TEST_EXPECT_EQUAL(mpt::bit_ceil(7u), 8u); MPT_TEST_EXPECT_EQUAL(mpt::bit_ceil(8u), 8u); MPT_TEST_EXPECT_EQUAL(mpt::bit_ceil(9u), 16u); MPT_TEST_EXPECT_EQUAL(mpt::bit_ceil(uint32(0x7fffffffu)), 0x80000000u); MPT_TEST_EXPECT_EQUAL(mpt::bit_ceil(uint32(0x80000000u)), 0x80000000u); //MPT_TEST_EXPECT_EQUAL(mpt::bit_ceil(uint32(0x80000001u)), 0u); //MPT_TEST_EXPECT_EQUAL(mpt::bit_ceil(uint32(0xfffffffeu)), 0u); //MPT_TEST_EXPECT_EQUAL(mpt::bit_ceil(uint32(0xffffffffu)), 0u); MPT_TEST_EXPECT_EQUAL(mpt::bit_floor(0u), 0u); MPT_TEST_EXPECT_EQUAL(mpt::bit_floor(1u), 1u); MPT_TEST_EXPECT_EQUAL(mpt::bit_floor(2u), 2u); MPT_TEST_EXPECT_EQUAL(mpt::bit_floor(3u), 2u); MPT_TEST_EXPECT_EQUAL(mpt::bit_floor(4u), 4u); MPT_TEST_EXPECT_EQUAL(mpt::bit_floor(5u), 4u); MPT_TEST_EXPECT_EQUAL(mpt::bit_floor(6u), 4u); MPT_TEST_EXPECT_EQUAL(mpt::bit_floor(7u), 4u); MPT_TEST_EXPECT_EQUAL(mpt::bit_floor(8u), 8u); MPT_TEST_EXPECT_EQUAL(mpt::bit_floor(9u), 8u); MPT_TEST_EXPECT_EQUAL(mpt::bit_floor(uint32(0x7fffffffu)), 0x40000000u); MPT_TEST_EXPECT_EQUAL(mpt::bit_floor(uint32(0x80000000u)), 0x80000000u); MPT_TEST_EXPECT_EQUAL(mpt::bit_floor(uint32(0x80000001u)), 0x80000000u); MPT_TEST_EXPECT_EQUAL(mpt::bit_floor(uint32(0xfffffffeu)), 0x80000000u); MPT_TEST_EXPECT_EQUAL(mpt::bit_floor(uint32(0xffffffffu)), 0x80000000u); MPT_TEST_EXPECT_EQUAL(mpt::bit_width(0u), 0u); MPT_TEST_EXPECT_EQUAL(mpt::bit_width(1u), 1u); MPT_TEST_EXPECT_EQUAL(mpt::bit_width(2u), 2u); MPT_TEST_EXPECT_EQUAL(mpt::bit_width(3u), 2u); MPT_TEST_EXPECT_EQUAL(mpt::bit_width(4u), 3u); MPT_TEST_EXPECT_EQUAL(mpt::bit_width(5u), 3u); MPT_TEST_EXPECT_EQUAL(mpt::bit_width(6u), 3u); MPT_TEST_EXPECT_EQUAL(mpt::bit_width(7u), 3u); MPT_TEST_EXPECT_EQUAL(mpt::bit_width(8u), 4u); MPT_TEST_EXPECT_EQUAL(mpt::bit_width(9u), 4u); MPT_TEST_EXPECT_EQUAL(mpt::bit_width(uint32(0x7fffffffu)), 31u); MPT_TEST_EXPECT_EQUAL(mpt::bit_width(uint32(0x80000000u)), 32u); MPT_TEST_EXPECT_EQUAL(mpt::bit_width(uint32(0x80000001u)), 32u); MPT_TEST_EXPECT_EQUAL(mpt::bit_width(uint32(0xfffffffeu)), 32u); MPT_TEST_EXPECT_EQUAL(mpt::bit_width(uint32(0xffffffffu)), 32u); MPT_TEST_EXPECT_EQUAL(mpt::countl_one(uint8(0b00000000)), 0); MPT_TEST_EXPECT_EQUAL(mpt::countl_one(uint8(0b00000001)), 0); MPT_TEST_EXPECT_EQUAL(mpt::countl_one(uint8(0b00000011)), 0); MPT_TEST_EXPECT_EQUAL(mpt::countl_one(uint8(0b00000111)), 0); MPT_TEST_EXPECT_EQUAL(mpt::countl_one(uint8(0b00001111)), 0); MPT_TEST_EXPECT_EQUAL(mpt::countl_one(uint8(0b00011111)), 0); MPT_TEST_EXPECT_EQUAL(mpt::countl_one(uint8(0b00111111)), 0); MPT_TEST_EXPECT_EQUAL(mpt::countl_one(uint8(0b01111111)), 0); MPT_TEST_EXPECT_EQUAL(mpt::countl_one(uint8(0b11111111)), 8); MPT_TEST_EXPECT_EQUAL(mpt::countl_one(uint8(0b11111110)), 7); MPT_TEST_EXPECT_EQUAL(mpt::countl_one(uint8(0b11111100)), 6); MPT_TEST_EXPECT_EQUAL(mpt::countl_one(uint8(0b11111000)), 5); MPT_TEST_EXPECT_EQUAL(mpt::countl_one(uint8(0b11110000)), 4); MPT_TEST_EXPECT_EQUAL(mpt::countl_one(uint8(0b11100000)), 3); MPT_TEST_EXPECT_EQUAL(mpt::countl_one(uint8(0b11000000)), 2); MPT_TEST_EXPECT_EQUAL(mpt::countl_one(uint8(0b10000000)), 1); MPT_TEST_EXPECT_EQUAL(mpt::countl_one(uint8(0b00000000)), 0); MPT_TEST_EXPECT_EQUAL(mpt::countl_zero(uint8(0b00000000)), 8); MPT_TEST_EXPECT_EQUAL(mpt::countl_zero(uint8(0b00000001)), 7); MPT_TEST_EXPECT_EQUAL(mpt::countl_zero(uint8(0b00000011)), 6); MPT_TEST_EXPECT_EQUAL(mpt::countl_zero(uint8(0b00000111)), 5); MPT_TEST_EXPECT_EQUAL(mpt::countl_zero(uint8(0b00001111)), 4); MPT_TEST_EXPECT_EQUAL(mpt::countl_zero(uint8(0b00011111)), 3); MPT_TEST_EXPECT_EQUAL(mpt::countl_zero(uint8(0b00111111)), 2); MPT_TEST_EXPECT_EQUAL(mpt::countl_zero(uint8(0b01111111)), 1); MPT_TEST_EXPECT_EQUAL(mpt::countl_zero(uint8(0b11111111)), 0); MPT_TEST_EXPECT_EQUAL(mpt::countl_zero(uint8(0b11111110)), 0); MPT_TEST_EXPECT_EQUAL(mpt::countl_zero(uint8(0b11111100)), 0); MPT_TEST_EXPECT_EQUAL(mpt::countl_zero(uint8(0b11111000)), 0); MPT_TEST_EXPECT_EQUAL(mpt::countl_zero(uint8(0b11110000)), 0); MPT_TEST_EXPECT_EQUAL(mpt::countl_zero(uint8(0b11100000)), 0); MPT_TEST_EXPECT_EQUAL(mpt::countl_zero(uint8(0b11000000)), 0); MPT_TEST_EXPECT_EQUAL(mpt::countl_zero(uint8(0b10000000)), 0); MPT_TEST_EXPECT_EQUAL(mpt::countl_zero(uint8(0b00000000)), 8); MPT_TEST_EXPECT_EQUAL(mpt::countr_one(uint8(0b00000000)), 0); MPT_TEST_EXPECT_EQUAL(mpt::countr_one(uint8(0b00000001)), 1); MPT_TEST_EXPECT_EQUAL(mpt::countr_one(uint8(0b00000011)), 2); MPT_TEST_EXPECT_EQUAL(mpt::countr_one(uint8(0b00000111)), 3); MPT_TEST_EXPECT_EQUAL(mpt::countr_one(uint8(0b00001111)), 4); MPT_TEST_EXPECT_EQUAL(mpt::countr_one(uint8(0b00011111)), 5); MPT_TEST_EXPECT_EQUAL(mpt::countr_one(uint8(0b00111111)), 6); MPT_TEST_EXPECT_EQUAL(mpt::countr_one(uint8(0b01111111)), 7); MPT_TEST_EXPECT_EQUAL(mpt::countr_one(uint8(0b11111111)), 8); MPT_TEST_EXPECT_EQUAL(mpt::countr_one(uint8(0b11111110)), 0); MPT_TEST_EXPECT_EQUAL(mpt::countr_one(uint8(0b11111100)), 0); MPT_TEST_EXPECT_EQUAL(mpt::countr_one(uint8(0b11111000)), 0); MPT_TEST_EXPECT_EQUAL(mpt::countr_one(uint8(0b11110000)), 0); MPT_TEST_EXPECT_EQUAL(mpt::countr_one(uint8(0b11100000)), 0); MPT_TEST_EXPECT_EQUAL(mpt::countr_one(uint8(0b11000000)), 0); MPT_TEST_EXPECT_EQUAL(mpt::countr_one(uint8(0b10000000)), 0); MPT_TEST_EXPECT_EQUAL(mpt::countr_one(uint8(0b00000000)), 0); MPT_TEST_EXPECT_EQUAL(mpt::countr_zero(uint8(0b00000000)), 8); MPT_TEST_EXPECT_EQUAL(mpt::countr_zero(uint8(0b00000001)), 0); MPT_TEST_EXPECT_EQUAL(mpt::countr_zero(uint8(0b00000011)), 0); MPT_TEST_EXPECT_EQUAL(mpt::countr_zero(uint8(0b00000111)), 0); MPT_TEST_EXPECT_EQUAL(mpt::countr_zero(uint8(0b00001111)), 0); MPT_TEST_EXPECT_EQUAL(mpt::countr_zero(uint8(0b00011111)), 0); MPT_TEST_EXPECT_EQUAL(mpt::countr_zero(uint8(0b00111111)), 0); MPT_TEST_EXPECT_EQUAL(mpt::countr_zero(uint8(0b01111111)), 0); MPT_TEST_EXPECT_EQUAL(mpt::countr_zero(uint8(0b11111111)), 0); MPT_TEST_EXPECT_EQUAL(mpt::countr_zero(uint8(0b11111110)), 1); MPT_TEST_EXPECT_EQUAL(mpt::countr_zero(uint8(0b11111100)), 2); MPT_TEST_EXPECT_EQUAL(mpt::countr_zero(uint8(0b11111000)), 3); MPT_TEST_EXPECT_EQUAL(mpt::countr_zero(uint8(0b11110000)), 4); MPT_TEST_EXPECT_EQUAL(mpt::countr_zero(uint8(0b11100000)), 5); MPT_TEST_EXPECT_EQUAL(mpt::countr_zero(uint8(0b11000000)), 6); MPT_TEST_EXPECT_EQUAL(mpt::countr_zero(uint8(0b10000000)), 7); MPT_TEST_EXPECT_EQUAL(mpt::countr_zero(uint8(0b00000000)), 8); MPT_TEST_EXPECT_EQUAL(mpt::lower_bound_entropy_bits(0xffffffffu), 32); MPT_TEST_EXPECT_EQUAL(mpt::lower_bound_entropy_bits(0xfffffffeu), 31); MPT_TEST_EXPECT_EQUAL(mpt::lower_bound_entropy_bits(0x80000000u), 31); MPT_TEST_EXPECT_EQUAL(mpt::lower_bound_entropy_bits(0x7fffffffu), 31); MPT_TEST_EXPECT_EQUAL(mpt::lower_bound_entropy_bits(0x7ffffffeu), 30); MPT_TEST_EXPECT_EQUAL(mpt::lower_bound_entropy_bits(0x00000007u), 3); MPT_TEST_EXPECT_EQUAL(mpt::lower_bound_entropy_bits(0x00000006u), 2); MPT_TEST_EXPECT_EQUAL(mpt::lower_bound_entropy_bits(0x00000005u), 2); MPT_TEST_EXPECT_EQUAL(mpt::lower_bound_entropy_bits(0x00000004u), 2); MPT_TEST_EXPECT_EQUAL(mpt::lower_bound_entropy_bits(0x00000003u), 2); MPT_TEST_EXPECT_EQUAL(mpt::lower_bound_entropy_bits(0x00000002u), 1); MPT_TEST_EXPECT_EQUAL(mpt::lower_bound_entropy_bits(0x00000001u), 1); MPT_TEST_EXPECT_EQUAL(mpt::lower_bound_entropy_bits(0x00000000u), 0); } } // namespace bit } // namespace base } // namespace tests } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_BASE_TESTS_BASE_BIT_HPP libopenmpt-0.6.1+release.autotools/src/mpt/base/tests/tests_base_math.hpp0000644000175000017500000000217214044173026023600 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_BASE_TESTS_MATH_HPP #define MPT_BASE_TESTS_MATH_HPP #include "mpt/base/detect_compiler.hpp" #include "mpt/base/math.hpp" #include "mpt/base/namespace.hpp" #include "mpt/test/test.hpp" #include "mpt/test/test_macros.hpp" namespace mpt { inline namespace MPT_INLINE_NS { namespace tests { namespace base { namespace math { #if MPT_COMPILER_CLANG #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wglobal-constructors" #endif MPT_TEST_GROUP_INLINE("mpt/base/math") #if MPT_COMPILER_CLANG #pragma clang diagnostic pop #endif { MPT_TEST_EXPECT_EQUAL(mpt::round(1.99), 2.0); MPT_TEST_EXPECT_EQUAL(mpt::round(1.5), 2.0); MPT_TEST_EXPECT_EQUAL(mpt::round(1.1), 1.0); MPT_TEST_EXPECT_EQUAL(mpt::round(-0.1), 0.0); MPT_TEST_EXPECT_EQUAL(mpt::round(-0.5), -1.0); MPT_TEST_EXPECT_EQUAL(mpt::round(-0.9), -1.0); MPT_TEST_EXPECT_EQUAL(mpt::round(-1.4), -1.0); MPT_TEST_EXPECT_EQUAL(mpt::round(-1.7), -2.0); } } // namespace math } // namespace base } // namespace tests } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_BASE_TESTS_MATH_HPP libopenmpt-0.6.1+release.autotools/src/mpt/base/tests/tests_base_saturate_cast.hpp0000644000175000017500000001360414044173026025513 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_BASE_TESTS_SATURATE_CAST_HPP #define MPT_BASE_TESTS_SATURATE_CAST_HPP #include "mpt/base/detect_compiler.hpp" #include "mpt/base/integer.hpp" #include "mpt/base/namespace.hpp" #include "mpt/base/saturate_cast.hpp" #include "mpt/test/test.hpp" #include "mpt/test/test_macros.hpp" #include namespace mpt { inline namespace MPT_INLINE_NS { namespace tests { namespace base { namespace saturate_cast { #if MPT_COMPILER_CLANG #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wglobal-constructors" #endif MPT_TEST_GROUP_INLINE("mpt/base/saturate_cast") #if MPT_COMPILER_CLANG #pragma clang diagnostic pop #endif { // trivials MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(-1), -1); MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(0), 0); MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(1), 1); MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(std::numeric_limits::min()), std::numeric_limits::min()); MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(std::numeric_limits::max()), std::numeric_limits::max()); // signed / unsigned MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(std::numeric_limits::min()), std::numeric_limits::min()); MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(std::numeric_limits::max()), std::numeric_limits::max()); MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(std::numeric_limits::min()), (int32)std::numeric_limits::min()); MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(std::numeric_limits::max()), std::numeric_limits::max()); MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(std::numeric_limits::min()), (int64)std::numeric_limits::min()); MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(std::numeric_limits::max()), std::numeric_limits::max()); MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(std::numeric_limits::min()), std::numeric_limits::min()); MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(std::numeric_limits::max()), std::numeric_limits::max()); MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(std::numeric_limits::min()), std::numeric_limits::min()); MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(std::numeric_limits::max()), (uint32)std::numeric_limits::max()); MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(std::numeric_limits::min()), std::numeric_limits::min()); MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(std::numeric_limits::max()), (uint64)std::numeric_limits::max()); // overflow MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(std::numeric_limits::min() - 1), std::numeric_limits::min()); MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(std::numeric_limits::max() + 1), std::numeric_limits::max()); MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(std::numeric_limits::min() - int64(1)), std::numeric_limits::min()); MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(std::numeric_limits::max() + int64(1)), std::numeric_limits::max()); MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(std::numeric_limits::min() - 1), std::numeric_limits::min()); MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(std::numeric_limits::max() + 1), (uint16)std::numeric_limits::max() + 1); MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(std::numeric_limits::min() - int64(1)), std::numeric_limits::min()); MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(std::numeric_limits::max() + int64(1)), (uint32)std::numeric_limits::max() + 1); MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(int16(32000)), 127); MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(int16(-32000)), -128); MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(uint16(32000)), 127); MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(uint16(64000)), 127); MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(int16(32000)), 255); MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(int16(-32000)), 0); MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(uint16(32000)), 255); MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(uint16(64000)), 255); MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(int16(32000)), 32000); MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(int16(-32000)), -32000); MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(uint16(32000)), 32000); MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(uint16(64000)), 32767); MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(int16(32000)), 32000); MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(int16(-32000)), 0); MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(uint16(32000)), 32000); MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(uint16(64000)), 64000); MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(int16(32000)), 32000); MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(int16(-32000)), -32000); MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(uint16(32000)), 32000); MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(uint16(64000)), 64000); MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(int16(32000)), 32000u); MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(int16(-32000)), 0u); MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(uint16(32000)), 32000u); MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(uint16(64000)), 64000u); MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(std::numeric_limits::max() - 1), std::numeric_limits::max()); MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(std::numeric_limits::max() - 1), std::numeric_limits::max()); MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(static_cast(std::numeric_limits::max())), std::numeric_limits::max()); } } // namespace saturate_cast } // namespace base } // namespace tests } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_BASE_TESTS_SATURATE_CAST_HPP libopenmpt-0.6.1+release.autotools/src/mpt/base/tests/tests_base_saturate_round.hpp0000644000175000017500000000357314044173026025714 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_BASE_TESTS_SATURATE_ROUND_HPP #define MPT_BASE_TESTS_SATURATE_ROUND_HPP #include "mpt/base/detect_compiler.hpp" #include "mpt/base/integer.hpp" #include "mpt/base/namespace.hpp" #include "mpt/base/saturate_round.hpp" #include "mpt/test/test.hpp" #include "mpt/test/test_macros.hpp" #include namespace mpt { inline namespace MPT_INLINE_NS { namespace tests { namespace base { namespace saturate_round { #if MPT_COMPILER_CLANG #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wglobal-constructors" #endif MPT_TEST_GROUP_INLINE("mpt/base/saturate_round") #if MPT_COMPILER_CLANG #pragma clang diagnostic pop #endif { MPT_TEST_EXPECT_EQUAL(mpt::saturate_round(std::numeric_limits::max() + 0.1), std::numeric_limits::max()); MPT_TEST_EXPECT_EQUAL(mpt::saturate_round(std::numeric_limits::max() - 0.4), std::numeric_limits::max()); MPT_TEST_EXPECT_EQUAL(mpt::saturate_round(std::numeric_limits::min() + 0.1), std::numeric_limits::min()); MPT_TEST_EXPECT_EQUAL(mpt::saturate_round(std::numeric_limits::min() - 0.1), std::numeric_limits::min()); MPT_TEST_EXPECT_EQUAL(mpt::saturate_round(std::numeric_limits::max() + 0.499), std::numeric_limits::max()); MPT_TEST_EXPECT_EQUAL(mpt::saturate_round(110.1), 110); MPT_TEST_EXPECT_EQUAL(mpt::saturate_round(-110.1), -110); // These should fail to compile //mpt::saturate_round(1.0); //mpt::saturate_round(1.0); //mpt::saturate_round(1.0); // This should trigger assert in Round. //MPT_TEST_EXPECT_EQUAL(mpt::saturate_round(-129), 0); } } // namespace saturate_round } // namespace base } // namespace tests } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_BASE_TESTS_SATURATE_ROUND_HPP libopenmpt-0.6.1+release.autotools/src/mpt/base/tests/tests_base_wrapping_divide.hpp0000644000175000017500000002232014044173026026017 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_BASE_TESTS_WRAPPING_DIVIDE_HPP #define MPT_BASE_TESTS_WRAPPING_DIVIDE_HPP #include "mpt/base/detect_compiler.hpp" #include "mpt/base/integer.hpp" #include "mpt/base/namespace.hpp" #include "mpt/base/wrapping_divide.hpp" #include "mpt/test/test.hpp" #include "mpt/test/test_macros.hpp" #include namespace mpt { inline namespace MPT_INLINE_NS { namespace tests { namespace base { namespace wrapping_divide { #if MPT_COMPILER_CLANG #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wglobal-constructors" #endif MPT_TEST_GROUP_INLINE("mpt/base/wrapping_divide") #if MPT_COMPILER_CLANG #pragma clang diagnostic pop #endif { MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(-25, 12), 11); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(-24, 12), 0); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(-23, 12), 1); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(-8, 7), 6); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(-7, 7), 0); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(-6, 7), 1); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(-5, 7), 2); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(-4, 7), 3); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(-3, 7), 4); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(-2, 7), 5); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(-1, 7), 6); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(0, 12), 0); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(0, 7), 0); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(1, 7), 1); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(2, 7), 2); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(3, 7), 3); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(4, 7), 4); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(5, 7), 5); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(6, 7), 6); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(7, 7), 0); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(8, 7), 1); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(23, 12), 11); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(24, 12), 0); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(25, 12), 1); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(uint32(0x7fffffff), uint32(0x80000000)), uint32(0x7fffffff)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(0x7ffffffe), int32(0x7fffffff)), int32(0x7ffffffe)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x80000000ll), int32(1)), int32(0)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x80000000ll), int32(2)), int32(0)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7fffffff), int32(1)), int32(0)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7fffffff), int32(2)), int32(1)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7ffffffe), int32(1)), int32(0)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7ffffffe), int32(2)), int32(0)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x80000000ll), int32(0x7fffffff)), int32(0x7ffffffe)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7fffffff), int32(0x7fffffff)), int32(0)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7ffffffe), int32(0x7fffffff)), int32(1)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x80000000ll), int32(0x7ffffffe)), int32(0x7ffffffc)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7fffffff), int32(0x7ffffffe)), int32(0x7ffffffd)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7ffffffe), int32(0x7ffffffe)), int32(0)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x80000000ll), int32(0x7ffffffd)), int32(0x7ffffffa)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7fffffff), int32(0x7ffffffd)), int32(0x7ffffffb)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7ffffffe), int32(0x7ffffffd)), int32(0x7ffffffc)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(0), int32(0x7fffffff)), int32(0)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-1), int32(0x7fffffff)), int32(0x7ffffffe)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-2), int32(0x7fffffff)), int32(0x7ffffffd)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(0), int32(0x7ffffffe)), int32(0)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-1), int32(0x7ffffffe)), int32(0x7ffffffd)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-2), int32(0x7ffffffe)), int32(0x7ffffffc)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x80000000ll), uint32(1)), uint32(0)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x80000000ll), uint32(2)), uint32(0)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7fffffff), uint32(1)), uint32(0)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7fffffff), uint32(2)), uint32(1)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7ffffffe), uint32(1)), uint32(0)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7ffffffe), uint32(2)), uint32(0)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x40000001), uint32(0xffffffff)), uint32(0xbffffffe)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x40000000), uint32(0xffffffff)), uint32(0xbfffffff)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x3fffffff), uint32(0xffffffff)), uint32(0xc0000000)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x80000000ll), uint32(0x80000000)), uint32(0)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7fffffff), uint32(0x80000000)), uint32(1)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7ffffffe), uint32(0x80000000)), uint32(2)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x80000000ll), uint32(0x80000001)), uint32(1)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7fffffff), uint32(0x80000001)), uint32(2)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7ffffffe), uint32(0x80000001)), uint32(3)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x80000000ll), uint32(0x80000000)), uint32(0)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7fffffff), uint32(0x80000000)), uint32(1)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7ffffffe), uint32(0x80000000)), uint32(2)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x80000000ll), uint32(0x7fffffff)), uint32(0x7ffffffe)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7fffffff), uint32(0x7fffffff)), uint32(0)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7ffffffe), uint32(0x7fffffff)), uint32(1)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x80000000ll), uint32(0x7ffffffe)), uint32(0x7ffffffc)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7fffffff), uint32(0x7ffffffe)), uint32(0x7ffffffd)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7ffffffe), uint32(0x7ffffffe)), uint32(0)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x80000000ll), uint32(0x7ffffffd)), uint32(0x7ffffffa)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7fffffff), uint32(0x7ffffffd)), uint32(0x7ffffffb)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7ffffffe), uint32(0x7ffffffd)), uint32(0x7ffffffc)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(0), uint32(0x7fffffff)), uint32(0)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-1), uint32(0x7fffffff)), uint32(0x7ffffffe)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-2), uint32(0x7fffffff)), uint32(0x7ffffffd)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(0), uint32(0x7ffffffe)), uint32(0)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-1), uint32(0x7ffffffe)), uint32(0x7ffffffd)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-2), uint32(0x7ffffffe)), uint32(0x7ffffffc)); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(-15, 7), -3); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(-14, 7), -2); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(-13, 7), -2); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(-12, 7), -2); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(-11, 7), -2); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(-10, 7), -2); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(-9, 7), -2); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(-8, 7), -2); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(-7, 7), -1); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(-6, 7), -1); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(-5, 7), -1); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(-4, 7), -1); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(-3, 7), -1); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(-2, 7), -1); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(-1, 7), -1); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(0, 7), 0); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(1, 7), 0); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(2, 7), 0); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(3, 7), 0); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(4, 7), 0); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(5, 7), 0); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(6, 7), 0); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(7, 7), 1); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(8, 7), 1); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(9, 7), 1); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(10, 7), 1); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(11, 7), 1); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(12, 7), 1); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(13, 7), 1); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(14, 7), 2); MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(15, 7), 2); } } // namespace wrapping_divide } // namespace base } // namespace tests } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_BASE_TESTS_WRAPPING_DIVIDE_HPP libopenmpt-0.6.1+release.autotools/src/mpt/base/algorithm.hpp0000644000175000017500000000320014044173026021250 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_BASE_ALGORITHM_HPP #define MPT_BASE_ALGORITHM_HPP #include "mpt/base/detect_compiler.hpp" #include "mpt/base/namespace.hpp" #include "mpt/base/saturate_cast.hpp" #include #include namespace mpt { inline namespace MPT_INLINE_NS { // Grows x with an exponential factor suitable for increasing buffer sizes. // Clamps the result at limit. // And avoids integer overflows while doing its business. // The growth factor is 1.5, rounding down, execpt for the initial x==1 case. template inline T exponential_grow(const T & x, const Tlimit & limit) { if (x <= 1) { return 2; } T add = std::min(x >> 1, std::numeric_limits::max() - x); return std::min(x + add, mpt::saturate_cast(limit)); } template inline T exponential_grow(const T & x) { return mpt::exponential_grow(x, std::numeric_limits::max()); } // Check if val is in [lo,hi] without causing compiler warnings // if theses checks are always true due to the domain of T. // GCC does not warn if the type is templated. template constexpr bool is_in_range(const T & val, const C & lo, const C & hi) { return lo <= val && val <= hi; } template MPT_CONSTEXPR20_FUN bool contains(const Tcontainer & container, const Tval & value) noexcept(noexcept(std::find(std::begin(container), std::end(container), value))) { return std::find(std::begin(container), std::end(container), value) != std::end(container); } } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_BASE_ALGORITHM_HPP libopenmpt-0.6.1+release.autotools/src/mpt/base/aligned_array.hpp0000644000175000017500000000642314123355754022105 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_BASE_ALIGNED_ARRAY_HPP #define MPT_BASE_ALIGNED_ARRAY_HPP #include "mpt/base/bit.hpp" #include "mpt/base/namespace.hpp" #include #include #include #include #include namespace mpt { inline namespace MPT_INLINE_NS { template struct alignas(static_cast(alignment)) aligned_array : std::array { static_assert(static_cast(alignment) >= alignof(T)); static_assert(((count * sizeof(T)) % static_cast(alignment)) == 0); static_assert(sizeof(std::array) == (sizeof(T) * count)); }; static_assert(sizeof(mpt::aligned_array) == sizeof(std::array)); template T * align_elements(std::array & a) { static_assert(mpt::has_single_bit(alignof(T))); static_assert(mpt::has_single_bit(sizeof(T))); static_assert(mpt::has_single_bit(alignment_elements)); static_assert((expected_elements + alignment_elements - 1) <= N); void * buf = a.data(); std::size_t size = N * sizeof(T); void * result = std::align(alignment_elements * sizeof(T), expected_elements * sizeof(T), buf, size); assert(result); return reinterpret_cast(result); } template T * align_elements(T (&a)[N]) { static_assert(mpt::has_single_bit(alignof(T))); static_assert(mpt::has_single_bit(sizeof(T))); static_assert(mpt::has_single_bit(alignment_elements)); static_assert((expected_elements + alignment_elements - 1) <= N); void * buf = a; std::size_t size = N * sizeof(T); void * result = std::align(alignment_elements * sizeof(T), expected_elements * sizeof(T), buf, size); assert(result); return reinterpret_cast(result); } template T * align_bytes(std::array & a) { static_assert(mpt::has_single_bit(alignof(T))); static_assert(mpt::has_single_bit(sizeof(T))); static_assert(mpt::has_single_bit(alignment_bytes)); static_assert((alignment_bytes % alignof(T)) == 0); static_assert(((expected_elements * sizeof(T)) + alignment_bytes - 1) <= (N * sizeof(T))); void * buf = a.data(); std::size_t size = N * sizeof(T); void * result = std::align(alignment_bytes, expected_elements * sizeof(T), buf, size); assert(result); return reinterpret_cast(result); } template T * align_bytes(T (&a)[N]) { static_assert(mpt::has_single_bit(alignof(T))); static_assert(mpt::has_single_bit(sizeof(T))); static_assert(mpt::has_single_bit(alignment_bytes)); static_assert((alignment_bytes % alignof(T)) == 0); static_assert(((expected_elements * sizeof(T)) + alignment_bytes - 1) <= (N * sizeof(T))); void * buf = a; std::size_t size = N * sizeof(T); void * result = std::align(alignment_bytes, expected_elements * sizeof(T), buf, size); assert(result); return reinterpret_cast(result); } } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_BASE_ALIGNED_ARRAY_HPP libopenmpt-0.6.1+release.autotools/src/mpt/base/alloc.hpp0000644000175000017500000001203014050156523020355 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_BASE_ALLOC_HPP #define MPT_BASE_ALLOC_HPP #include "mpt/base/namespace.hpp" #include "mpt/base/memory.hpp" #include "mpt/base/span.hpp" #include #include #include #include #include #include namespace mpt { inline namespace MPT_INLINE_NS { template inline mpt::span as_span(std::vector & cont) { return mpt::span(cont.data(), cont.data() + cont.size()); } template inline mpt::span as_span(const std::vector & cont) { return mpt::span(cont.data(), cont.data() + cont.size()); } template inline span as_span(std::basic_string & str) { return span(str.data(), str.size()); } template inline span as_span(const std::basic_string & str) { return span(str.data(), str.size()); } template inline std::vector::type> make_vector(T * beg, T * end) { return std::vector::type>(beg, end); } template inline std::vector::type> make_vector(T * data, std::size_t size) { return std::vector::type>(data, data + size); } template inline std::vector::type> make_vector(mpt::span data) { return std::vector::type>(data.data(), data.data() + data.size()); } template inline std::vector::type> make_vector(T (&arr)[N]) { return std::vector::type>(std::begin(arr), std::end(arr)); } template inline std::vector::type> make_vector(const std::basic_string & str) { return std::vector::type>(str.begin(), str.end()); } template inline std::basic_string::type> make_basic_string(T * beg, T * end) { return std::basic_string::type>(beg, end); } template inline std::basic_string::type> make_basic_string(T * data, std::size_t size) { return std::basic_string::type>(data, data + size); } template inline std::basic_string::type> make_basic_string(mpt::span data) { return std::basic_string::type>(data.data(), data.data() + data.size()); } template inline std::basic_string::type> make_basic_string(T (&arr)[N]) { return std::basic_string::type>(std::begin(arr), std::end(arr)); } template inline std::basic_string::type> make_basic_string(const std::vector & str) { return std::vector::type>(str.begin(), str.end()); } template inline Tcont1 & append(Tcont1 & cont1, const Tcont2 & cont2) { cont1.insert(cont1.end(), cont2.begin(), cont2.end()); return cont1; } template inline Tcont1 & append(Tcont1 & cont1, Tit2 beg, Tit2 end) { cont1.insert(cont1.end(), beg, end); return cont1; } template struct buffer_cast_impl { inline Tdst operator()(const Tsrc & src) const { return Tdst(mpt::byte_cast(src.data()), mpt::byte_cast(src.data()) + src.size()); } }; // casts between vector<->string of byte-castable types template inline Tdst buffer_cast(Tsrc src) { return buffer_cast_impl()(src); } template struct as_raw_memory_impl> { inline mpt::const_byte_span operator()(const std::vector & v) const { static_assert(mpt::is_binary_safe::type>::value); return mpt::as_span(reinterpret_cast(v.data()), v.size() * sizeof(T)); } inline mpt::byte_span operator()(std::vector & v) const { static_assert(mpt::is_binary_safe::type>::value); return mpt::as_span(reinterpret_cast(v.data()), v.size() * sizeof(T)); } }; template struct as_raw_memory_impl> { inline mpt::const_byte_span operator()(const std::vector & v) const { static_assert(mpt::is_binary_safe::type>::value); return mpt::as_span(reinterpret_cast(v.data()), v.size() * sizeof(T)); } }; template class heap_value { private: std::unique_ptr m_value{}; public: template heap_value(Targs &&... args) : m_value(std::make_unique(std::forward(args)...)) { return; } const T & operator*() const { return *m_value; } T & operator*() { return *m_value; } }; } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_BASE_ALLOC_HPP libopenmpt-0.6.1+release.autotools/src/mpt/base/arithmetic_shift.hpp0000644000175000017500000000646014044173026022623 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_BASE_ARITHMETIC_SHIFT_HPP #define MPT_BASE_ARITHMETIC_SHIFT_HPP #include "mpt/base/detect.hpp" #include "mpt/base/namespace.hpp" #include "mpt/base/saturate_cast.hpp" namespace mpt { inline namespace MPT_INLINE_NS { // mpt::rshift_signed // mpt::lshift_signed // Shift a signed integer value in a well-defined manner. // Does the same thing as MSVC would do. This is verified by the test suite. template constexpr auto rshift_signed_standard(T x, int y) noexcept -> decltype(x >> y) { static_assert(std::numeric_limits::is_integer); static_assert(std::numeric_limits::is_signed); using result_type = decltype(x >> y); using unsigned_result_type = typename std::make_unsigned::type; const unsigned_result_type roffset = static_cast(1) << ((sizeof(result_type) * 8) - 1); result_type rx = x; unsigned_result_type urx = static_cast(rx); urx += roffset; urx >>= y; urx -= roffset >> y; return static_cast(urx); } template constexpr auto lshift_signed_standard(T x, int y) noexcept -> decltype(x << y) { static_assert(std::numeric_limits::is_integer); static_assert(std::numeric_limits::is_signed); using result_type = decltype(x << y); using unsigned_result_type = typename std::make_unsigned::type; const unsigned_result_type roffset = static_cast(1) << ((sizeof(result_type) * 8) - 1); result_type rx = x; unsigned_result_type urx = static_cast(rx); urx += roffset; urx <<= y; urx -= roffset << y; return static_cast(urx); } #if MPT_COMPILER_SHIFT_SIGNED template constexpr auto rshift_signed_undefined(T x, int y) noexcept -> decltype(x >> y) { static_assert(std::numeric_limits::is_integer); static_assert(std::numeric_limits::is_signed); return x >> y; } template constexpr auto lshift_signed_undefined(T x, int y) noexcept -> decltype(x << y) { static_assert(std::numeric_limits::is_integer); static_assert(std::numeric_limits::is_signed); return x << y; } template constexpr auto rshift_signed(T x, int y) noexcept -> decltype(x >> y) { return mpt::rshift_signed_undefined(x, y); } template constexpr auto lshift_signed(T x, int y) noexcept -> decltype(x << y) { return mpt::lshift_signed_undefined(x, y); } #else template constexpr auto rshift_signed(T x, int y) noexcept -> decltype(x >> y) { return mpt::rshift_signed_standard(x, y); } template constexpr auto lshift_signed(T x, int y) noexcept -> decltype(x << y) { return mpt::lshift_signed_standard(x, y); } #endif template constexpr auto arithmetic_shift_right(T x, int y) noexcept -> decltype(x >> y) { return mpt::rshift_signed(x, y); } template constexpr auto arithmetic_shift_right(T x, int y) noexcept -> decltype(x << y) { return mpt::lshift_signed(x, y); } template constexpr auto sar(T x, int y) noexcept -> decltype(x >> y) { return mpt::rshift_signed(x, y); } template constexpr auto sal(T x, int y) noexcept -> decltype(x << y) { return mpt::lshift_signed(x, y); } } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_BASE_ARITHMETIC_SHIFT_HPP libopenmpt-0.6.1+release.autotools/src/mpt/base/array.hpp0000644000175000017500000000355514044173026020415 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_BASE_ARRAY_HPP #define MPT_BASE_ARRAY_HPP #include "mpt/base/detect.hpp" #include "mpt/base/namespace.hpp" #include #include #include namespace mpt { inline namespace MPT_INLINE_NS { template struct stdarray_extent : std::integral_constant { }; template struct stdarray_extent> : std::integral_constant { }; template struct is_stdarray : std::false_type { }; template struct is_stdarray> : std::true_type { }; // mpt::extent is the same as std::extent, // but also works for std::array, // and asserts that the given type is actually an array type instead of returning 0. // use as: // mpt::extent() // mpt::extent() // mpt::extent() // mpt::extent() template constexpr std::size_t extent() noexcept { using Tarray = typename std::remove_cv::type>::type; static_assert(std::is_array::value || mpt::is_stdarray::value); if constexpr (mpt::is_stdarray::value) { return mpt::stdarray_extent(); } else { return std::extent(); } } template struct array_size; template struct array_size> { static constexpr std::size_t size = N; }; template struct array_size { static constexpr std::size_t size = N; }; template constexpr std::array init_array(const Tx & x) { std::array result{}; for (std::size_t i = 0; i < N; ++i) { result[i] = x; } return result; } } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_BASE_ARRAY_HPP libopenmpt-0.6.1+release.autotools/src/mpt/base/bit.hpp0000644000175000017500000002371314163400672020055 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_BASE_BIT_HPP #define MPT_BASE_BIT_HPP #include "mpt/base/detect.hpp" #include "mpt/base/integer.hpp" #include "mpt/base/namespace.hpp" #include "mpt/base/macros.hpp" #if MPT_CXX_AT_LEAST(20) #include #else // !C++20 #include #include #endif // C++20 #include #include #if MPT_CXX_BEFORE(20) #include #endif // !C++20 namespace mpt { inline namespace MPT_INLINE_NS { #if MPT_CXX_AT_LEAST(20) && !MPT_CLANG_BEFORE(14, 0, 0) using std::bit_cast; #else // !C++20 // C++2a compatible bit_cast. // Not implementing constexpr because this is not easily possible pre C++20. template MPT_FORCEINLINE typename std::enable_if<(sizeof(Tdst) == sizeof(Tsrc)) && std::is_trivially_copyable::value && std::is_trivially_copyable::value, Tdst>::type bit_cast(const Tsrc & src) noexcept { Tdst dst{}; std::memcpy(&dst, &src, sizeof(Tdst)); return dst; } #endif // C++20 #if MPT_CXX_AT_LEAST(20) using std::endian; static_assert(mpt::endian::big != mpt::endian::little, "platform with all scalar types having size 1 is not supported"); constexpr mpt::endian get_endian() noexcept { return mpt::endian::native; } constexpr bool endian_is_little() noexcept { return get_endian() == mpt::endian::little; } constexpr bool endian_is_big() noexcept { return get_endian() == mpt::endian::big; } constexpr bool endian_is_weird() noexcept { return !endian_is_little() && !endian_is_big(); } #else // !C++20 #if !MPT_COMPILER_GENERIC #if MPT_COMPILER_MSVC #define MPT_PLATFORM_LITTLE_ENDIAN #elif MPT_COMPILER_GCC || MPT_COMPILER_CLANG #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ #define MPT_PLATFORM_BIG_ENDIAN #elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ #define MPT_PLATFORM_LITTLE_ENDIAN #endif #elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && defined(__ORDER_LITTLE_ENDIAN__) #if __ORDER_BIG_ENDIAN__ != __ORDER_LITTLE_ENDIAN__ #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ #define MPT_PLATFORM_BIG_ENDIAN #elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ #define MPT_PLATFORM_LITTLE_ENDIAN #endif #endif #endif // fallback: #if !defined(MPT_PLATFORM_BIG_ENDIAN) && !defined(MPT_PLATFORM_LITTLE_ENDIAN) #if (defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN)) \ || (defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__)) \ || (defined(_STLP_BIG_ENDIAN) && !defined(_STLP_LITTLE_ENDIAN)) #define MPT_PLATFORM_BIG_ENDIAN #elif (defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN)) \ || (defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__)) \ || (defined(_STLP_LITTLE_ENDIAN) && !defined(_STLP_BIG_ENDIAN)) #define MPT_PLATFORM_LITTLE_ENDIAN #elif defined(__hpux) || defined(__hppa) \ || defined(_MIPSEB) \ || defined(__s390__) #define MPT_PLATFORM_BIG_ENDIAN #elif defined(__i386__) || defined(_M_IX86) \ || defined(__amd64) || defined(__amd64__) || defined(_M_AMD64) || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) \ || defined(__bfin__) #define MPT_PLATFORM_LITTLE_ENDIAN #endif #endif #endif // !MPT_COMPILER_GENERIC enum class endian { little = 0x78563412u, big = 0x12345678u, weird = 1u, #if MPT_COMPILER_GENERIC native = 0u, #elif defined(MPT_PLATFORM_LITTLE_ENDIAN) native = little, #elif defined(MPT_PLATFORM_BIG_ENDIAN) native = big, #else native = 0u, #endif }; static_assert(mpt::endian::big != mpt::endian::little, "platform with all scalar types having size 1 is not supported"); MPT_FORCEINLINE mpt::endian endian_probe() noexcept { using endian_probe_type = uint32; static_assert(sizeof(endian_probe_type) == 4); constexpr endian_probe_type endian_probe_big = 0x12345678u; constexpr endian_probe_type endian_probe_little = 0x78563412u; const std::array probe{{std::byte{0x12}, std::byte{0x34}, std::byte{0x56}, std::byte{0x78}}}; const endian_probe_type test = mpt::bit_cast(probe); mpt::endian result = mpt::endian::native; switch (test) { case endian_probe_big: result = mpt::endian::big; break; case endian_probe_little: result = mpt::endian::little; break; default: result = mpt::endian::weird; break; } return result; } MPT_FORCEINLINE mpt::endian get_endian() noexcept { #if MPT_COMPILER_MSVC #pragma warning(push) #pragma warning(disable : 6285) // false-positive: ( || ) is always a non-zero constant. #endif // MPT_COMPILER_MSVC if constexpr ((mpt::endian::native == mpt::endian::little) || (mpt::endian::native == mpt::endian::big)) { return mpt::endian::native; } else { return mpt::endian_probe(); } #if MPT_COMPILER_MSVC #pragma warning(pop) #endif // MPT_COMPILER_MSVC } MPT_FORCEINLINE bool endian_is_little() noexcept { return get_endian() == mpt::endian::little; } MPT_FORCEINLINE bool endian_is_big() noexcept { return get_endian() == mpt::endian::big; } MPT_FORCEINLINE bool endian_is_weird() noexcept { return !endian_is_little() && !endian_is_big(); } #endif // C++20 #if MPT_CXX_AT_LEAST(20) && !MPT_COMPILER_MSVC && !MPT_CLANG_BEFORE(12, 0, 0) // Disabled for VS2022 for now because of // // / with fix already queued // (). using std::bit_ceil; using std::bit_floor; using std::bit_width; using std::countl_one; using std::countl_zero; using std::countr_one; using std::countr_zero; using std::has_single_bit; using std::popcount; using std::rotl; using std::rotr; #else // !C++20 // C++20 header. // Note that we do not use SFINAE here but instead rely on static_assert. template constexpr int popcount(T val) noexcept { static_assert(std::numeric_limits::is_integer); static_assert(std::is_unsigned::value); int result = 0; while (val > 0) { if (val & 0x1) { result++; } val >>= 1; } return result; } template constexpr bool has_single_bit(T x) noexcept { static_assert(std::numeric_limits::is_integer); static_assert(std::is_unsigned::value); return mpt::popcount(x) == 1; } template constexpr T bit_ceil(T x) noexcept { static_assert(std::numeric_limits::is_integer); static_assert(std::is_unsigned::value); T result = 1; while (result < x) { T newresult = result << 1; if (newresult < result) { return 0; } result = newresult; } return result; } template constexpr T bit_floor(T x) noexcept { static_assert(std::numeric_limits::is_integer); static_assert(std::is_unsigned::value); if (x == 0) { return 0; } T result = 1; do { T newresult = result << 1; if (newresult < result) { return result; } result = newresult; } while (result <= x); return result >> 1; } template constexpr T bit_width(T x) noexcept { static_assert(std::numeric_limits::is_integer); static_assert(std::is_unsigned::value); T result = 0; while (x > 0) { x >>= 1; result += 1; } return result; } template constexpr int countl_zero(T x) noexcept { static_assert(std::numeric_limits::is_integer); static_assert(std::is_unsigned::value); int count = 0; for (int bit = std::numeric_limits::digits - 1; bit >= 0; --bit) { if ((x & (1u << bit)) == 0u) { count++; } else { break; } } return count; } template constexpr int countl_one(T x) noexcept { static_assert(std::numeric_limits::is_integer); static_assert(std::is_unsigned::value); int count = 0; for (int bit = std::numeric_limits::digits - 1; bit >= 0; --bit) { if ((x & (1u << bit)) != 0u) { count++; } else { break; } } return count; } template constexpr int countr_zero(T x) noexcept { static_assert(std::numeric_limits::is_integer); static_assert(std::is_unsigned::value); int count = 0; for (int bit = 0; bit < std::numeric_limits::digits; ++bit) { if ((x & (1u << bit)) == 0u) { count++; } else { break; } } return count; } template constexpr int countr_one(T x) noexcept { static_assert(std::numeric_limits::is_integer); static_assert(std::is_unsigned::value); int count = 0; for (int bit = 0; bit < std::numeric_limits::digits; ++bit) { if ((x & (1u << bit)) != 0u) { count++; } else { break; } } return count; } template constexpr T rotl_impl(T x, int r) noexcept { auto N = std::numeric_limits::digits; return (x >> (N - r)) | (x << r); } template constexpr T rotr_impl(T x, int r) noexcept { auto N = std::numeric_limits::digits; return (x << (N - r)) | (x >> r); } template constexpr T rotl(T x, int s) noexcept { static_assert(std::numeric_limits::is_integer); static_assert(std::is_unsigned::value); auto N = std::numeric_limits::digits; auto r = s % N; return (s < 0) ? mpt::rotr_impl(x, -s) : ((x >> (N - r)) | (x << r)); } template constexpr T rotr(T x, int s) noexcept { static_assert(std::numeric_limits::is_integer); static_assert(std::is_unsigned::value); auto N = std::numeric_limits::digits; auto r = s % N; return (s < 0) ? mpt::rotl_impl(x, -s) : ((x << (N - r)) | (x >> r)); } #endif // C++20 template constexpr int lower_bound_entropy_bits(T x_) { typename std::make_unsigned::type x = static_cast::type>(x_); return mpt::bit_width(x) == static_cast::type>(mpt::popcount(x)) ? mpt::bit_width(x) : mpt::bit_width(x) - 1; } template constexpr bool is_mask(T x) { static_assert(std::is_integral::value); typedef typename std::make_unsigned::type unsigned_T; unsigned_T ux = static_cast(x); unsigned_T mask = 0; for (std::size_t bits = 0; bits <= (sizeof(unsigned_T) * 8); ++bits) { mask = (mask << 1) | 1u; if (ux == mask) { return true; } } return false; } } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_BASE_BIT_HPP libopenmpt-0.6.1+release.autotools/src/mpt/base/check_platform.hpp0000644000175000017500000000132214044173026022246 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_BASE_CHECK_PLATFORM_HPP #define MPT_BASE_CHECK_PLATFORM_HPP #include "mpt/base/detect.hpp" #include "mpt/base/namespace.hpp" #include "mpt/base/pointer.hpp" #include #include namespace mpt { inline namespace MPT_INLINE_NS { static_assert(sizeof(std::uintptr_t) == sizeof(void *)); static_assert(std::numeric_limits::digits == 8); static_assert(sizeof(char) == 1); static_assert(sizeof(std::byte) == 1); static_assert(alignof(std::byte) == 1); static_assert(mpt::arch_bits == static_cast(mpt::pointer_size) * 8); } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_BASE_CHECK_PLATFORM_HPP libopenmpt-0.6.1+release.autotools/src/mpt/base/compiletime_warning.hpp0000644000175000017500000000224014044173026023321 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_BASE_COMPILETIME_WARNING_HPP #define MPT_BASE_COMPILETIME_WARNING_HPP #include "mpt/base/detect.hpp" #include "mpt/base/preprocessor.hpp" #if MPT_COMPILER_MSVC #define MPT_WARNING(text) __pragma(message(__FILE__ "(" MPT_PP_DEFER(MPT_PP_STRINGIFY, __LINE__) "): Warning: " text)) #define MPT_WARNING_STATEMENT(text) __pragma(message(__FILE__ "(" MPT_PP_DEFER(MPT_PP_STRINGIFY, __LINE__) "): Warning: " text)) #elif MPT_COMPILER_GCC || MPT_COMPILER_CLANG #define MPT_WARNING(text) _Pragma(MPT_PP_STRINGIFY(GCC warning text)) #define MPT_WARNING_STATEMENT(text) _Pragma(MPT_PP_STRINGIFY(GCC warning text)) #else // portable #pragma message or #warning replacement #define MPT_WARNING(text) \ static inline int MPT_PP_UNIQUE_IDENTIFIER(MPT_WARNING_NAME)() noexcept { \ int warning [[deprecated("Warning: " text)]] = 0; \ return warning; \ } \ /**/ #define MPT_WARNING_STATEMENT(text) \ int MPT_PP_UNIQUE_IDENTIFIER(MPT_WARNING_NAME) = []() { \ int warning [[deprecated("Warning: " text)]] = 0; \ return warning; \ }() /**/ #endif #endif // MPT_BASE_COMPILETIME_WARNING_HPP libopenmpt-0.6.1+release.autotools/src/mpt/base/constexpr_throw.hpp0000644000175000017500000000242714044173026022544 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_BASE_CONSTEXPR_THROW_HPP #define MPT_BASE_CONSTEXPR_THROW_HPP #include "mpt/base/detect.hpp" #include "mpt/base/namespace.hpp" namespace mpt { inline namespace MPT_INLINE_NS { // Work-around for the requirement of at least 1 non-throwing function argument combination in C++ (17,2a). template constexpr bool constexpr_throw_helper(Exception && e, bool really = true) { //return !really ? really : throw std::forward(e); if (really) { throw std::forward(e); } // cppcheck-suppress identicalConditionAfterEarlyExit return really; } template constexpr bool constexpr_throw(Exception && e) { return mpt::constexpr_throw_helper(std::forward(e)); } template constexpr T constexpr_throw_helper(Exception && e, bool really = true) { //return !really ? really : throw std::forward(e); if (really) { throw std::forward(e); } return T{}; } template constexpr T constexpr_throw(Exception && e) { return mpt::constexpr_throw_helper(std::forward(e)); } } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_BASE_CONSTEXPR_THROW_HPP libopenmpt-0.6.1+release.autotools/src/mpt/base/detect.hpp0000644000175000017500000000051614172545435020552 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_BASE_DETECT_HPP #define MPT_BASE_DETECT_HPP #include "mpt/base/detect_compiler.hpp" #include "mpt/base/detect_libc.hpp" #include "mpt/base/detect_libcxx.hpp" #include "mpt/base/detect_os.hpp" #include "mpt/base/detect_quirks.hpp" #endif // MPT_BASE_DETECT_HPP libopenmpt-0.6.1+release.autotools/src/mpt/base/detect_compiler.hpp0000644000175000017500000001374414172613550022445 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_BASE_DETECT_COMPILER_HPP #define MPT_BASE_DETECT_COMPILER_HPP #define MPT_COMPILER_MAKE_VERSION2(version, sp) ((version)*100 + (sp)) #define MPT_COMPILER_MAKE_VERSION3(major, minor, patch) ((major)*10000 + (minor)*100 + (patch)) #if defined(MPT_COMPILER_GENERIC) #undef MPT_COMPILER_GENERIC #define MPT_COMPILER_GENERIC 1 #elif defined(__clang__) && defined(_MSC_VER) && defined(__c2__) #error "Clang/C2 is not supported. Please use Clang/LLVM for Windows instead." #elif defined(__clang__) #define MPT_COMPILER_CLANG 1 #define MPT_COMPILER_CLANG_VERSION MPT_COMPILER_MAKE_VERSION3(__clang_major__, __clang_minor__, __clang_patchlevel__) #define MPT_CLANG_AT_LEAST(major, minor, patch) (MPT_COMPILER_CLANG_VERSION >= MPT_COMPILER_MAKE_VERSION3((major), (minor), (patch))) #define MPT_CLANG_BEFORE(major, minor, patch) (MPT_COMPILER_CLANG_VERSION < MPT_COMPILER_MAKE_VERSION3((major), (minor), (patch))) #if MPT_CLANG_BEFORE(7, 0, 0) #error "clang version 7 required" #endif #if defined(__clang_analyzer__) #ifndef MPT_BUILD_ANALYZED #define MPT_BUILD_ANALYZED #endif #endif #elif defined(__GNUC__) #define MPT_COMPILER_GCC 1 #define MPT_COMPILER_GCC_VERSION MPT_COMPILER_MAKE_VERSION3(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) #define MPT_GCC_AT_LEAST(major, minor, patch) (MPT_COMPILER_GCC_VERSION >= MPT_COMPILER_MAKE_VERSION3((major), (minor), (patch))) #define MPT_GCC_BEFORE(major, minor, patch) (MPT_COMPILER_GCC_VERSION < MPT_COMPILER_MAKE_VERSION3((major), (minor), (patch))) #if MPT_GCC_BEFORE(8, 1, 0) #error "GCC version 8.1 required" #endif #elif defined(_MSC_VER) #define MPT_COMPILER_MSVC 1 #if (_MSC_VER >= 1930) #define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2022, 0) #elif (_MSC_VER >= 1929) #define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2019, 10) #elif (_MSC_VER >= 1928) #define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2019, 8) #elif (_MSC_VER >= 1927) #define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2019, 7) #elif (_MSC_VER >= 1926) #define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2019, 6) #elif (_MSC_VER >= 1925) #define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2019, 5) #elif (_MSC_VER >= 1924) #define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2019, 4) #elif (_MSC_VER >= 1923) #define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2019, 3) #elif (_MSC_VER >= 1922) #define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2019, 2) #elif (_MSC_VER >= 1921) #define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2019, 1) #elif (_MSC_VER >= 1920) #define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2019, 0) #elif (_MSC_VER >= 1916) #define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2017, 9) #elif (_MSC_VER >= 1915) #define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2017, 8) #elif (_MSC_VER >= 1914) #define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2017, 7) #elif (_MSC_VER >= 1913) #define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2017, 6) #elif (_MSC_VER >= 1912) #define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2017, 5) #elif (_MSC_VER >= 1911) #define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2017, 3) #elif (_MSC_VER >= 1910) #define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2017, 0) #elif (_MSC_VER >= 1900) && defined(_MSVC_LANG) #define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2015, 3) #elif (_MSC_VER >= 1900) #define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2015, 0) #elif (_MSC_VER >= 1800) #define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2013, 0) #elif (_MSC_VER >= 1700) #define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2012, 0) #elif (_MSC_VER >= 1600) #define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2010, 0) #elif (_MSC_VER >= 1500) #define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2008, 0) #else #define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2005, 0) #endif #define MPT_MSVC_AT_LEAST(version, sp) (MPT_COMPILER_MSVC_VERSION >= MPT_COMPILER_MAKE_VERSION2((version), (sp))) #define MPT_MSVC_BEFORE(version, sp) (MPT_COMPILER_MSVC_VERSION < MPT_COMPILER_MAKE_VERSION2((version), (sp))) #if MPT_MSVC_BEFORE(2017, 9) #error "MSVC version 2017 15.9 required" #endif #if defined(_PREFAST_) #ifndef MPT_BUILD_ANALYZED #define MPT_BUILD_ANALYZED #endif #endif #else #define MPT_COMPILER_GENERIC 1 #endif #ifndef MPT_COMPILER_GENERIC #define MPT_COMPILER_GENERIC 0 #endif #ifndef MPT_COMPILER_CLANG #define MPT_COMPILER_CLANG 0 #define MPT_CLANG_AT_LEAST(major, minor, patch) 0 #define MPT_CLANG_BEFORE(major, minor, patch) 0 #endif #ifndef MPT_COMPILER_GCC #define MPT_COMPILER_GCC 0 #define MPT_GCC_AT_LEAST(major, minor, patch) 0 #define MPT_GCC_BEFORE(major, minor, patch) 0 #endif #ifndef MPT_COMPILER_MSVC #define MPT_COMPILER_MSVC 0 #define MPT_MSVC_AT_LEAST(version, sp) 0 #define MPT_MSVC_BEFORE(version, sp) 0 #endif #if MPT_COMPILER_GENERIC || MPT_COMPILER_GCC || MPT_COMPILER_CLANG #if (__cplusplus >= 202002) // Support for C++20 is lacking across all compilers. // Only assume C++17 for non-MSVC, even when in C++20 mode. #define MPT_CXX 17 #elif (__cplusplus >= 201703) #define MPT_CXX 17 #else #define MPT_CXX 17 #endif #elif MPT_COMPILER_MSVC #if MPT_MSVC_AT_LEAST(2019, 10) && (_MSVC_LANG >= 201705) #define MPT_CXX 20 #elif (_MSVC_LANG >= 201703) #define MPT_CXX 17 #else #define MPT_CXX 17 #endif #else #define MPT_CXX 17 #endif // MPT_CXX is stricter than just using __cplusplus directly. // We will only claim a language version as supported IFF all core language and // library fatures that we need are actually supported AND working correctly // (to our needs). #define MPT_CXX_AT_LEAST(version) (MPT_CXX >= (version)) #define MPT_CXX_BEFORE(version) (MPT_CXX < (version)) #endif // MPT_BASE_DETECT_COMPILER_HPP libopenmpt-0.6.1+release.autotools/src/mpt/base/detect_libc.hpp0000644000175000017500000000144514172544705021544 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_BASE_DETECT_LIBC_HPP #define MPT_BASE_DETECT_LIBC_HPP #include "mpt/base/detect_compiler.hpp" #include "mpt/base/detect_os.hpp" #include // order of checks is important! #if MPT_COMPILER_GENERIC #define MPT_LIBC_GENERIC 1 #elif MPT_COMPILER_GCC && (defined(__MINGW32__) || defined(__MINGW64__)) #define MPT_LIBC_MS 1 #elif defined(__GLIBC__) #define MPT_LIBC_GLIBC 1 #elif MPT_COMPILER_MSVC #define MPT_LIBC_MS 1 #elif MPT_COMPILER_CLANG && MPT_OS_WINDOWS #define MPT_LIBC_MS 1 #else #define MPT_LIBC_GENERIC 1 #endif #ifndef MPT_LIBC_GENERIC #define MPT_LIBC_GENERIC 0 #endif #ifndef MPT_LIBC_GLIBC #define MPT_LIBC_GLIBC 0 #endif #ifndef MPT_LIBC_MS #define MPT_LIBC_MS 0 #endif #endif // MPT_BASE_DETECT_LIBC_HPP libopenmpt-0.6.1+release.autotools/src/mpt/base/detect_libcxx.hpp0000644000175000017500000000166214172246710022120 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_BASE_DETECT_LIBCXX_HPP #define MPT_BASE_DETECT_LIBCXX_HPP #include "mpt/base/detect_compiler.hpp" #include "mpt/base/detect_os.hpp" #if MPT_CXX_AT_LEAST(20) #include #else // !C++20 #include #endif // C++20 // order of checks is important! #if MPT_COMPILER_GENERIC #define MPT_LIBCXX_GENERIC 1 #elif defined(_LIBCPP_VERSION) #define MPT_LIBCXX_LLVM 1 #elif defined(__GLIBCXX__) || defined(__GLIBCPP__) #define MPT_LIBCXX_GNU 1 #elif MPT_COMPILER_MSVC #define MPT_LIBCXX_MS 1 #elif MPT_COMPILER_CLANG && MPT_OS_WINDOWS #define MPT_LIBCXX_MS 1 #else #define MPT_LIBCXX_GENERIC 1 #endif #ifndef MPT_LIBCXX_GENERIC #define MPT_LIBCXX_GENERIC 0 #endif #ifndef MPT_LIBCXX_LLVM #define MPT_LIBCXX_LLVM 0 #endif #ifndef MPT_LIBCXX_GNU #define MPT_LIBCXX_GNU 0 #endif #ifndef MPT_LIBCXX_MS #define MPT_LIBCXX_MS 0 #endif #endif // MPT_BASE_DETECT_LIBCXX_HPP libopenmpt-0.6.1+release.autotools/src/mpt/base/detect_os.hpp0000644000175000017500000000454414161041537021250 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_BASE_DETECT_OS_HPP #define MPT_BASE_DETECT_OS_HPP // The order of the checks matters! #if defined(__DJGPP__) #define MPT_OS_DJGPP 1 #elif defined(__EMSCRIPTEN__) #define MPT_OS_EMSCRIPTEN 1 #if defined(__EMSCRIPTEN_major__) && defined(__EMSCRIPTEN_minor__) #if (__EMSCRIPTEN_major__ > 1) // ok #elif (__EMSCRIPTEN_major__ == 1) && (__EMSCRIPTEN_minor__ > 39) // ok #elif (__EMSCRIPTEN_major__ == 1) && (__EMSCRIPTEN_minor__ == 39) && (__EMSCRIPTEN_tiny__ >= 7) // ok #else #error "Emscripten >= 1.39.7 is required." #endif #endif #elif defined(_WIN32) #define MPT_OS_WINDOWS 1 #if defined(WINAPI_FAMILY) #include #if (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP) #define MPT_OS_WINDOWS_WINRT 0 #else #define MPT_OS_WINDOWS_WINRT 1 #endif #else // !WINAPI_FAMILY #define MPT_OS_WINDOWS_WINRT 0 #endif // WINAPI_FAMILY #elif defined(__APPLE__) #define MPT_OS_MACOSX_OR_IOS 1 //#include "TargetConditionals.h" //#if TARGET_IPHONE_SIMULATOR //#elif TARGET_OS_IPHONE //#elif TARGET_OS_MAC //#else //#endif #elif defined(__HAIKU__) #define MPT_OS_HAIKU 1 #elif defined(__ANDROID__) || defined(ANDROID) #define MPT_OS_ANDROID 1 #elif defined(__linux__) #define MPT_OS_LINUX 1 #elif defined(__DragonFly__) #define MPT_OS_DRAGONFLYBSD 1 #elif defined(__FreeBSD__) #define MPT_OS_FREEBSD 1 #elif defined(__OpenBSD__) #define MPT_OS_OPENBSD 1 #elif defined(__NetBSD__) #define MPT_OS_NETBSD 1 #elif defined(__unix__) #define MPT_OS_GENERIC_UNIX 1 #else #define MPT_OS_UNKNOWN 1 #endif #ifndef MPT_OS_DJGPP #define MPT_OS_DJGPP 0 #endif #ifndef MPT_OS_EMSCRIPTEN #define MPT_OS_EMSCRIPTEN 0 #endif #ifndef MPT_OS_WINDOWS #define MPT_OS_WINDOWS 0 #endif #ifndef MPT_OS_WINDOWS_WINRT #define MPT_OS_WINDOWS_WINRT 0 #endif #ifndef MPT_OS_MACOSX_OR_IOS #define MPT_OS_MACOSX_OR_IOS 0 #endif #ifndef MPT_OS_HAIKU #define MPT_OS_HAIKU 0 #endif #ifndef MPT_OS_ANDROID #define MPT_OS_ANDROID 0 #endif #ifndef MPT_OS_LINUX #define MPT_OS_LINUX 0 #endif #ifndef MPT_OS_DRAGONFLYBSD #define MPT_OS_DRAGONFLYBSD 0 #endif #ifndef MPT_OS_FREEBSD #define MPT_OS_FREEBSD 0 #endif #ifndef MPT_OS_OPENBSD #define MPT_OS_OPENBSD 0 #endif #ifndef MPT_OS_NETBSD #define MPT_OS_NETBSD 0 #endif #ifndef MPT_OS_GENERIC_UNIX #define MPT_OS_GENERIC_UNIX 0 #endif #ifndef MPT_OS_UNKNOWN #define MPT_OS_UNKNOWN 0 #endif #endif // MPT_BASE_DETECT_OS_HPP libopenmpt-0.6.1+release.autotools/src/mpt/base/detect_quirks.hpp0000644000175000017500000000560014075537227022151 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_BASE_DETECT_QUIRKS_HPP #define MPT_BASE_DETECT_QUIRKS_HPP #include "mpt/base/detect_compiler.hpp" #include "mpt/base/detect_os.hpp" #if MPT_COMPILER_MSVC // Compiler has multiplication/division semantics when shifting signed integers. #define MPT_COMPILER_SHIFT_SIGNED 1 #endif #ifndef MPT_COMPILER_SHIFT_SIGNED #define MPT_COMPILER_SHIFT_SIGNED 0 #endif // This should really be based on __STDCPP_THREADS__, but that is not defined by // GCC or clang. Stupid. // Just assume multithreaded and disable for platforms we know are // singlethreaded later on. #define MPT_PLATFORM_MULTITHREADED 1 #if MPT_OS_DJGPP #undef MPT_PLATFORM_MULTITHREADED #define MPT_PLATFORM_MULTITHREADED 0 #endif #if (MPT_OS_EMSCRIPTEN && !defined(__EMSCRIPTEN_PTHREADS__)) #undef MPT_PLATFORM_MULTITHREADED #define MPT_PLATFORM_MULTITHREADED 0 #endif #if MPT_OS_WINDOWS && MPT_COMPILER_MSVC #if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0600) // _WIN32_WINNT_VISTA #define MPT_COMPILER_QUIRK_COMPLEX_STD_MUTEX #endif #endif #if MPT_OS_EMSCRIPTEN && defined(MPT_BUILD_AUDIOWORKLETPROCESSOR) #define MPT_COMPILER_QUIRK_CHRONO_NO_HIGH_RESOLUTION_CLOCK #endif #if MPT_OS_EMSCRIPTEN && defined(MPT_BUILD_AUDIOWORKLETPROCESSOR) #define MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE #endif #if MPT_OS_DJGPP #define MPT_COMPILER_QUIRK_NO_WCHAR #endif #if MPT_OS_WINDOWS && MPT_GCC_BEFORE(9, 1, 0) // GCC C++ library has no wchar_t overloads #define MPT_COMPILER_QUIRK_WINDOWS_FSTREAM_NO_WCHAR #endif #if defined(__arm__) #if defined(__SOFTFP__) #define MPT_COMPILER_QUIRK_FLOAT_EMULATED 1 #else #define MPT_COMPILER_QUIRK_FLOAT_EMULATED 0 #endif #if defined(__VFP_FP__) // native-endian IEEE754 #define MPT_COMPILER_QUIRK_FLOAT_NOTNATIVEENDIAN 0 #define MPT_COMPILER_QUIRK_FLOAT_NOTIEEE754 0 #elif defined(__MAVERICK__) // little-endian IEEE754, we assume native-endian though #define MPT_COMPILER_QUIRK_FLOAT_NOTNATIVEENDIAN 1 #define MPT_COMPILER_QUIRK_FLOAT_NOTIEEE754 0 #else // not IEEE754 #define MPT_COMPILER_QUIRK_FLOAT_NOTNATIVEENDIAN 1 #define MPT_COMPILER_QUIRK_FLOAT_NOTIEEE754 1 #endif #elif defined(__mips__) #if defined(__mips_soft_float) #define MPT_COMPILER_QUIRK_FLOAT_EMULATED 1 #else #define MPT_COMPILER_QUIRK_FLOAT_EMULATED 0 #endif #endif #if MPT_OS_EMSCRIPTEN #define MPT_COMPILER_QUIRK_FLOAT_PREFER64 1 #endif #ifndef MPT_COMPILER_QUIRK_FLOAT_PREFER32 #define MPT_COMPILER_QUIRK_FLOAT_PREFER32 0 #endif #ifndef MPT_COMPILER_QUIRK_FLOAT_PREFER64 #define MPT_COMPILER_QUIRK_FLOAT_PREFER64 0 #endif #ifndef MPT_COMPILER_QUIRK_FLOAT_EMULATED #define MPT_COMPILER_QUIRK_FLOAT_EMULATED 0 #endif #ifndef MPT_COMPILER_QUIRK_FLOAT_NOTNATIVEENDIAN #define MPT_COMPILER_QUIRK_FLOAT_NOTNATIVEENDIAN 0 #endif #ifndef MPT_COMPILER_QUIRK_FLOAT_NOTIEEE754 #define MPT_COMPILER_QUIRK_FLOAT_NOTIEEE754 0 #endif #endif // MPT_BASE_DETECT_QUIRKS_HPP libopenmpt-0.6.1+release.autotools/src/mpt/base/floatingpoint.hpp0000644000175000017500000000661414044173026022153 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_BASE_FLOATINGPOINT_HPP #define MPT_BASE_FLOATINGPOINT_HPP #include "mpt/base/detect.hpp" #include "mpt/base/namespace.hpp" #include #include namespace mpt { inline namespace MPT_INLINE_NS { // fp half // n/a // fp single using single = float; namespace float_literals { constexpr single operator"" _fs(long double lit) noexcept { return static_cast(lit); } } // namespace float_literals // fp double namespace float_literals { constexpr double operator"" _fd(long double lit) noexcept { return static_cast(lit); } } // namespace float_literals // fp extended namespace float_literals { constexpr long double operator"" _fe(long double lit) noexcept { return static_cast(lit); } } // namespace float_literals // fp quad // n/a using float32 = std::conditional::type>::type>::type; namespace float_literals { constexpr float32 operator"" _f32(long double lit) noexcept { return static_cast(lit); } } // namespace float_literals using float64 = std::conditional::type>::type>::type; namespace float_literals { constexpr float64 operator"" _f64(long double lit) noexcept { return static_cast(lit); } } // namespace float_literals template struct float_traits { static constexpr bool is_float = !std::numeric_limits::is_integer; static constexpr bool is_hard = is_float && !MPT_COMPILER_QUIRK_FLOAT_EMULATED; static constexpr bool is_soft = is_float && MPT_COMPILER_QUIRK_FLOAT_EMULATED; static constexpr bool is_float32 = is_float && (sizeof(T) == 4); static constexpr bool is_float64 = is_float && (sizeof(T) == 8); static constexpr bool is_native_endian = is_float && !MPT_COMPILER_QUIRK_FLOAT_NOTNATIVEENDIAN; static constexpr bool is_ieee754_binary = is_float && std::numeric_limits::is_iec559 && !MPT_COMPILER_QUIRK_FLOAT_NOTIEEE754; static constexpr bool is_ieee754_binary32 = is_float && is_ieee754_binary && is_float32; static constexpr bool is_ieee754_binary64 = is_float && is_ieee754_binary && is_float64; static constexpr bool is_ieee754_binary32ne = is_float && is_ieee754_binary && is_float32 && is_native_endian; static constexpr bool is_ieee754_binary64ne = is_float && is_ieee754_binary && is_float64 && is_native_endian; static constexpr bool is_preferred = is_float && ((is_float32 && MPT_COMPILER_QUIRK_FLOAT_PREFER32) || (is_float64 && MPT_COMPILER_QUIRK_FLOAT_PREFER64)); }; // prefer smaller floats, but try to use IEEE754 floats using nativefloat = std::conditional::is_preferred, float32, std::conditional::is_preferred, float64, std::conditional::is_iec559, float, std::conditional::is_iec559, double, std::conditional::is_iec559, long double, float>::type>::type>::type>::type>::type; namespace float_literals { constexpr nativefloat operator"" _nf(long double lit) noexcept { return static_cast(lit); } } // namespace float_literals } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_BASE_FLOATINGPOINT_HPP libopenmpt-0.6.1+release.autotools/src/mpt/base/integer.hpp0000644000175000017500000000103114044173026020717 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_BASE_INTEGER_HPP #define MPT_BASE_INTEGER_HPP #include "mpt/base/namespace.hpp" #include namespace mpt { inline namespace MPT_INLINE_NS { using int8 = std::int8_t; using int16 = std::int16_t; using int32 = std::int32_t; using int64 = std::int64_t; using uint8 = std::uint8_t; using uint16 = std::uint16_t; using uint32 = std::uint32_t; using uint64 = std::uint64_t; } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_BASE_INTEGER_HPP libopenmpt-0.6.1+release.autotools/src/mpt/base/macros.hpp0000644000175000017500000000551414044200003020542 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_BASE_MACROS_HPP #define MPT_BASE_MACROS_HPP #include "mpt/base/detect.hpp" #include #if MPT_COMPILER_MSVC && MPT_OS_WINDOWS #include #endif // MPT_COMPILER_MSVC && MPT_OS_WINDOWS // Advanced inline attributes #if MPT_COMPILER_MSVC #define MPT_FORCEINLINE __forceinline #define MPT_NOINLINE __declspec(noinline) #elif MPT_COMPILER_GCC || MPT_COMPILER_CLANG #define MPT_FORCEINLINE __attribute__((always_inline)) inline #define MPT_NOINLINE __attribute__((noinline)) #else #define MPT_FORCEINLINE inline #define MPT_NOINLINE #endif // constexpr #define MPT_CONSTEXPRINLINE constexpr MPT_FORCEINLINE #if MPT_CXX_AT_LEAST(20) #define MPT_CONSTEXPR20_FUN constexpr MPT_FORCEINLINE #define MPT_CONSTEXPR20_VAR constexpr #else // !C++20 #define MPT_CONSTEXPR20_FUN MPT_FORCEINLINE #define MPT_CONSTEXPR20_VAR const #endif // C++20 #define MPT_FORCE_CONSTEXPR(expr) [&]() { \ constexpr auto x = (expr); \ return x; \ }() #if MPT_CXX_AT_LEAST(20) #define MPT_IS_CONSTANT_EVALUATED20() std::is_constant_evaluated() #define MPT_IS_CONSTANT_EVALUATED() std::is_constant_evaluated() #else // !C++20 #define MPT_IS_CONSTANT_EVALUATED20() false // this pessimizes the case for C++17 by always assuming constexpr context, which implies always running constexpr-friendly code #define MPT_IS_CONSTANT_EVALUATED() true #endif // C++20 #if MPT_COMPILER_MSVC #define MPT_MAYBE_CONSTANT_IF(x) \ __pragma(warning(push)) \ __pragma(warning(disable : 4127)) \ if (x) \ __pragma(warning(pop)) \ /**/ #endif #if MPT_COMPILER_GCC #define MPT_MAYBE_CONSTANT_IF(x) \ _Pragma("GCC diagnostic push") \ _Pragma("GCC diagnostic ignored \"-Wtype-limits\"") \ if (x) \ _Pragma("GCC diagnostic pop") \ /**/ #endif #if MPT_COMPILER_CLANG #define MPT_MAYBE_CONSTANT_IF(x) \ _Pragma("clang diagnostic push") \ _Pragma("clang diagnostic ignored \"-Wunknown-pragmas\"") \ _Pragma("clang diagnostic ignored \"-Wtype-limits\"") \ _Pragma("clang diagnostic ignored \"-Wtautological-constant-out-of-range-compare\"") \ if (x) \ _Pragma("clang diagnostic pop") \ /**/ #endif #if !defined(MPT_MAYBE_CONSTANT_IF) // MPT_MAYBE_CONSTANT_IF disables compiler warnings for conditions that may in some case be either always false or always true (this may turn out to be useful in ASSERTions in some cases). #define MPT_MAYBE_CONSTANT_IF(x) if (x) #endif #if MPT_COMPILER_MSVC && MPT_OS_WINDOWS #define MPT_UNUSED(x) UNREFERENCED_PARAMETER(x) #else #define MPT_UNUSED(x) static_cast(x) #endif #define MPT_DISCARD(expr) static_cast(expr) // Use MPT_RESTRICT to indicate that a pointer is guaranteed to not be aliased. #if MPT_COMPILER_MSVC || MPT_COMPILER_GCC || MPT_COMPILER_CLANG #define MPT_RESTRICT __restrict #else #define MPT_RESTRICT #endif #endif // MPT_BASE_MACROS_HPP libopenmpt-0.6.1+release.autotools/src/mpt/base/math.hpp0000644000175000017500000000252414155127757020240 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_BASE_EMPTY_HPP #define MPT_BASE_EMPTY_HPP #include "mpt/base/detect.hpp" #include "mpt/base/namespace.hpp" #include #include #include namespace mpt { inline namespace MPT_INLINE_NS { #if MPT_OS_DJGPP inline long double log2(const long double val) { return static_cast(::log2(static_cast(val))); } inline double log2(const double val) { return ::log2(val); } inline float log2(const float val) { return ::log2f(val); } #else // !MPT_OS_DJGPP // C++11 std::log2 using std::log2; #endif // MPT_OS_DJGPP #if MPT_OS_DJGPP inline long double round(const long double val) { return ::roundl(val); } inline double round(const double val) { return ::round(val); } inline float round(const float val) { return ::roundf(val); } #else // !MPT_OS_DJGPP // C++11 std::round using std::round; #endif // MPT_OS_DJGPP template inline T sanitize_nan(T val) { static_assert(std::is_floating_point::value); if (std::isnan(val)) { return T(0.0); } return val; } template inline T safe_clamp(T v, T lo, T hi) { static_assert(std::is_floating_point::value); return std::clamp(mpt::sanitize_nan(v), lo, hi); } } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_BASE_EMPTY_HPP libopenmpt-0.6.1+release.autotools/src/mpt/base/memory.hpp0000644000175000017500000002152414050167637020613 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_BASE_MEMORY_HPP #define MPT_BASE_MEMORY_HPP #include "mpt/base/integer.hpp" #include "mpt/base/namespace.hpp" #include "mpt/base/macros.hpp" #include "mpt/base/span.hpp" #include #include #include namespace mpt { inline namespace MPT_INLINE_NS { using byte_span = mpt::span; using const_byte_span = mpt::span; // Tell which types are safe for mpt::byte_cast. // signed char is actually not allowed to alias into an object representation, // which means that, if the actual type is not itself signed char but char or // unsigned char instead, dereferencing the signed char pointer is undefined // behaviour. template struct is_byte_castable : public std::false_type { }; template <> struct is_byte_castable : public std::true_type { }; template <> struct is_byte_castable : public std::true_type { }; template <> struct is_byte_castable : public std::true_type { }; template <> struct is_byte_castable : public std::true_type { }; template <> struct is_byte_castable : public std::true_type { }; template <> struct is_byte_castable : public std::true_type { }; template struct is_byte : public std::false_type { }; template <> struct is_byte : public std::true_type { }; template <> struct is_byte : public std::true_type { }; template constexpr bool declare_binary_safe(const T &) noexcept { return false; } constexpr bool declare_binary_safe(const char &) noexcept { return true; } constexpr bool declare_binary_safe(const uint8 &) noexcept { return true; } constexpr bool declare_binary_safe(const int8 &) noexcept { return true; } constexpr bool declare_binary_safe(const std::byte &) noexcept { return true; } // Tell which types are safe to binary write into files. // By default, no types are safe. // When a safe type gets defined, // also specialize this template so that IO functions will work. template struct is_binary_safe : public std::conditional::type { }; // Generic Specialization for arrays. template struct is_binary_safe : public is_binary_safe { }; template struct is_binary_safe : public is_binary_safe { }; template struct is_binary_safe> : public is_binary_safe { }; template struct is_binary_safe> : public is_binary_safe { }; template constexpr bool check_binary_size(std::size_t size) noexcept { return true && (sizeof(T) == size) && (alignof(T) == 1) && std::is_standard_layout::value && std::has_unique_object_representations::value && mpt::is_binary_safe::value; } template struct byte_cast_impl { inline Tdst operator()(Tsrc src) const noexcept { static_assert(sizeof(Tsrc) == sizeof(std::byte)); static_assert(sizeof(Tdst) == sizeof(std::byte)); // not checking is_byte_castable here because we are actually // doing a static_cast and converting the value static_assert(std::is_integral::value || mpt::is_byte::value); static_assert(std::is_integral::value || mpt::is_byte::value); return static_cast(src); } }; template struct byte_cast_impl, mpt::span> { inline mpt::span operator()(mpt::span src) const noexcept { static_assert(sizeof(Tsrc) == sizeof(std::byte)); static_assert(sizeof(Tdst) == sizeof(std::byte)); static_assert(mpt::is_byte_castable::value); static_assert(mpt::is_byte_castable::value); static_assert(std::is_integral::value || mpt::is_byte::value); static_assert(std::is_integral::value || mpt::is_byte::value); return mpt::as_span(mpt::byte_cast_impl()(src.data()), mpt::byte_cast_impl()(src.data() + src.size())); } }; template struct byte_cast_impl { inline Tdst * operator()(Tsrc * src) const noexcept { static_assert(sizeof(Tsrc) == sizeof(std::byte)); static_assert(sizeof(Tdst) == sizeof(std::byte)); static_assert(mpt::is_byte_castable::value); static_assert(mpt::is_byte_castable::value); static_assert(std::is_integral::value || mpt::is_byte::value); static_assert(std::is_integral::value || mpt::is_byte::value); return reinterpret_cast(src); } }; template struct void_cast_impl; template struct void_cast_impl { inline Tdst * operator()(void * src) const noexcept { static_assert(sizeof(Tdst) == sizeof(std::byte)); static_assert(mpt::is_byte_castable::value); static_assert(std::is_integral::value || mpt::is_byte::value); return reinterpret_cast(src); } }; template struct void_cast_impl { inline Tdst * operator()(const void * src) const noexcept { static_assert(sizeof(Tdst) == sizeof(std::byte)); static_assert(mpt::is_byte_castable::value); static_assert(std::is_integral::value || mpt::is_byte::value); return reinterpret_cast(src); } }; template struct void_cast_impl { inline void * operator()(Tsrc * src) const noexcept { static_assert(sizeof(Tsrc) == sizeof(std::byte)); static_assert(mpt::is_byte_castable::value); static_assert(std::is_integral::value || mpt::is_byte::value); return reinterpret_cast(src); } }; template struct void_cast_impl { inline const void * operator()(Tsrc * src) const noexcept { static_assert(sizeof(Tsrc) == sizeof(std::byte)); static_assert(mpt::is_byte_castable::value); static_assert(std::is_integral::value || mpt::is_byte::value); return reinterpret_cast(src); } }; // casts between different byte (char) types or pointers to these types template inline Tdst byte_cast(Tsrc src) noexcept { return byte_cast_impl()(src); } // casts between pointers to void and pointers to byte template inline Tdst void_cast(Tsrc src) noexcept { return void_cast_impl()(src); } template MPT_CONSTEXPRINLINE std::byte as_byte(T src) noexcept { static_assert(std::is_integral::value); return static_cast(static_cast(src)); } template struct as_raw_memory_impl { inline mpt::const_byte_span operator()(const T & v) const { static_assert(mpt::is_binary_safe::type>::value); return mpt::as_span(reinterpret_cast(&v), sizeof(T)); } inline mpt::byte_span operator()(T & v) const { static_assert(mpt::is_binary_safe::type>::value); return mpt::as_span(reinterpret_cast(&v), sizeof(T)); } }; template struct as_raw_memory_impl { inline mpt::const_byte_span operator()(const T (&v)[N]) const { static_assert(mpt::is_binary_safe::type>::value); return mpt::as_span(reinterpret_cast(v), N * sizeof(T)); } inline mpt::byte_span operator()(T (&v)[N]) const { static_assert(mpt::is_binary_safe::type>::value); return mpt::as_span(reinterpret_cast(v), N * sizeof(T)); } }; template struct as_raw_memory_impl { inline mpt::const_byte_span operator()(const T (&v)[N]) const { static_assert(mpt::is_binary_safe::type>::value); return mpt::as_span(reinterpret_cast(v), N * sizeof(T)); } }; // In order to be able to partially specialize it, // as_raw_memory is implemented via a class template. // Do not overload or specialize as_raw_memory directly. // Using a wrapper (by default just around a cast to const std::byte *), // allows for implementing raw memory access // via on-demand generating a cached serialized representation. template inline mpt::const_byte_span as_raw_memory(const T & v) { return mpt::as_raw_memory_impl()(v); } template inline mpt::byte_span as_raw_memory(T & v) { return mpt::as_raw_memory_impl()(v); } template inline void memclear(T & x) { static_assert(std::is_standard_layout::value); static_assert((std::is_trivially_default_constructible::value && std::is_trivially_copyable::value) || mpt::is_binary_safe::value); std::memset(&x, 0, sizeof(T)); } } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_BASE_MEMORY_HPP libopenmpt-0.6.1+release.autotools/src/mpt/base/namespace.hpp0000644000175000017500000000361414044173026021227 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_BASE_NAMESPACE_HPP #define MPT_BASE_NAMESPACE_HPP #include "mpt/base/detect.hpp" #include "mpt/base/version.hpp" #include "mpt/base/compiletime_warning.hpp" #if !defined(MPT_INLINE_NS) #define MPT_BUILD_VERSION_NAMESPACE_IMPL(a, b, c, d) v##a##_##b##_##c##_##d #define MPT_BUILD_VERSION_NAMESPACE(a, b, c, d) MPT_BUILD_VERSION_NAMESPACE_IMPL(a, b, c, d) #define MPT_VERSION_NAMESPACE MPT_BUILD_VERSION_NAMESPACE(MPT_VERSION_MAJOR, MPT_VERSION_MINOR, MPT_VERSION_PATCH, MPT_VERSION_BUILD) #if MPT_OS_WINDOWS #ifdef UNICODE #define MPT_VERSION_ABI_OS u #else #define MPT_VERSION_ABI_OS 8 #endif #else #define MPT_VERSION_ABI_OS _ #endif #if MPT_LIBC_GENERIC #define MPT_VERSION_ABI_LIBC _ #elif MPT_LIBC_MS #ifdef _DLL #ifdef _DEBUG #define MPT_VERSION_ABI_LIBC MDd #else #define MPT_VERSION_ABI_LIBC MDr #endif #else #ifdef _DEBUG #define MPT_VERSION_ABI_LIBC MTd #else #define MPT_VERSION_ABI_LIBC MTr #endif #endif #elif MPT_LIBC_GLIBC #define MPT_VERSION_ABI_LIBC G #else #define MPT_VERSION_ABI_LIBC _ #endif #define MPT_BUILD_ABI_NAMESPACE_IMPL(a, b) ABI_##a##_##b #define MPT_BUILD_ABI_NAMESPACE(a, b) MPT_BUILD_ABI_NAMESPACE_IMPL(a, b) #define MPT_ABI_NAMESPACE MPT_BUILD_ABI_NAMESPACE(MPT_VERSION_ABI_OS, MPT_VERSION_ABI_LIBC) #if !defined(MPT_PROJECT_NAMESPACE) MPT_WARNING("Please #define MPT_PROJECT_NAMESPACE or #define MPT_INLINE_NS in build configuration.") #define MPT_PROJECT_NAMESPACE x #endif // !MPT_PROJECT_NAMESPACE #define MPT_BUILD_INLINE_NS_IMPL(a, b, c) a##_##b##_##c #define MPT_BUILD_INLINE_NS(a, b, c) MPT_BUILD_INLINE_NS_IMPL(a, b, c) #define MPT_INLINE_NS MPT_BUILD_INLINE_NS(MPT_VERSION_NAMESPACE, MPT_ABI_NAMESPACE, MPT_PROJECT_NAMESPACE) #endif // !MPT_INLINE_NS namespace mpt { inline namespace MPT_INLINE_NS { } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_BASE_NAMESPACE_HPP libopenmpt-0.6.1+release.autotools/src/mpt/base/numbers.hpp0000644000175000017500000001460314047741753020761 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_BASE_NUMBRES_HPP #define MPT_BASE_NUMBRES_HPP #include "mpt/base/detect_compiler.hpp" #include "mpt/base/namespace.hpp" #if MPT_CXX_AT_LEAST(20) #include #else #include #include #include #endif namespace mpt { inline namespace MPT_INLINE_NS { namespace numbers { #if MPT_CXX_AT_LEAST(20) template inline constexpr T e_v = std::numbers::e_v; template inline constexpr T log2e_v = std::numbers::log2e_v; template inline constexpr T log10e_v = std::numbers::log10e_v; template inline constexpr T pi_v = std::numbers::pi_v; template inline constexpr T inv_pi_v = std::numbers::inv_pi_v; template inline constexpr T inv_sqrtpi_v = std::numbers::inv_sqrtpi_v; template inline constexpr T ln2_v = std::numbers::ln2_v; template inline constexpr T ln10_v = std::numbers::ln10_v; template inline constexpr T sqrt2_v = std::numbers::sqrt2_v; template inline constexpr T sqrt3_v = std::numbers::sqrt3_v; template inline constexpr T inv_sqrt3_v = std::numbers::inv_sqrt3_v; template inline constexpr T egamma_v = std::numbers::egamma_v; template inline constexpr T phi_v = std::numbers::phi_v; inline constexpr double e = e_v; inline constexpr double log2e = log2e_v; inline constexpr double log10e = log10e_v; inline constexpr double pi = pi_v; inline constexpr double inv_pi = inv_pi_v; inline constexpr double inv_sqrtpi = inv_sqrtpi_v; inline constexpr double ln2 = ln2_v; inline constexpr double ln10 = ln10_v; inline constexpr double sqrt2 = sqrt2_v; inline constexpr double sqrt3 = sqrt3_v; inline constexpr double inv_sqrt3 = inv_sqrt3_v; inline constexpr double egamma = egamma_v; inline constexpr double phi = phi_v; #else #ifdef M_E template ::value, bool>::type = true> inline constexpr T e_v = static_cast(M_E); #else template ::value, bool>::type = true> inline constexpr T e_v = static_cast(2.71828182845904523536); #endif #ifdef M_LOG2E template ::value, bool>::type = true> inline constexpr T log2e_v = static_cast(M_LOG2E); #else template ::value, bool>::type = true> inline constexpr T log2e_v = static_cast(1.44269504088896340736); #endif #ifdef M_LOG10E template ::value, bool>::type = true> inline constexpr T log10e_v = static_cast(M_LOG10E); #else template ::value, bool>::type = true> inline constexpr T log10e_v = static_cast(0.434294481903251827651); #endif #ifdef M_PI template ::value, bool>::type = true> inline constexpr T pi_v = static_cast(M_PI); #else template ::value, bool>::type = true> inline constexpr T pi_v = static_cast(3.14159265358979323846); #endif #ifdef M_1_PI template ::value, bool>::type = true> inline constexpr T inv_pi_v = static_cast(M_1_PI); #else template ::value, bool>::type = true> inline constexpr T inv_pi_v = static_cast(0.318309886183790671538); #endif template ::value, bool>::type = true> inline constexpr T inv_sqrtpi_v = static_cast(0.564189583547756286948079451560772586); #ifdef M_LN2 template ::value, bool>::type = true> inline constexpr T ln2_v = static_cast(M_LN2); #else template ::value, bool>::type = true> inline constexpr T ln2_v = static_cast(0.693147180559945309417); #endif #ifdef M_LN10 template ::value, bool>::type = true> inline constexpr T ln10_v = static_cast(M_LN10); #else template ::value, bool>::type = true> inline constexpr T ln10_v = static_cast(2.30258509299404568402); #endif #ifdef M_SQRT2 template ::value, bool>::type = true> inline constexpr T sqrt2_v = static_cast(M_SQRT2); #else template ::value, bool>::type = true> inline constexpr T sqrt2_v = static_cast(1.41421356237309504880); #endif template ::value, bool>::type = true> inline constexpr T sqrt3_v = static_cast(1.732050807568877293527446341505872367); template ::value, bool>::type = true> inline constexpr T inv_sqrt3_v = static_cast(0.577350269189625764509148780501957456); template ::value, bool>::type = true> inline constexpr T egamma_v = static_cast(0.577215664901532860606512090082402431); template ::value, bool>::type = true> inline constexpr T phi_v = static_cast(1.618033988749894848204586834365638118); inline constexpr double e = e_v; inline constexpr double log2e = log2e_v; inline constexpr double log10e = log10e_v; inline constexpr double pi = pi_v; inline constexpr double inv_pi = inv_pi_v; inline constexpr double inv_sqrtpi = inv_sqrtpi_v; inline constexpr double ln2 = ln2_v; inline constexpr double ln10 = ln10_v; inline constexpr double sqrt2 = sqrt2_v; inline constexpr double sqrt3 = sqrt3_v; inline constexpr double inv_sqrt3 = inv_sqrt3_v; inline constexpr double egamma = egamma_v; inline constexpr double phi = phi_v; #endif } // namespace numbers } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_BASE_NUMBRES_HPP libopenmpt-0.6.1+release.autotools/src/mpt/base/numeric.hpp0000644000175000017500000000403714056707252020744 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_BASE_NUMERIC_HPP #define MPT_BASE_NUMERIC_HPP #include "mpt/base/detect_compiler.hpp" #include "mpt/base/namespace.hpp" #include "mpt/base/bit.hpp" #include "mpt/base/saturate_cast.hpp" #include #include #include namespace mpt { inline namespace MPT_INLINE_NS { template struct ModIfNotZeroImpl { template constexpr Tval mod(Tval x) { static_assert(std::numeric_limits::is_integer); static_assert(!std::numeric_limits::is_signed); static_assert(std::numeric_limits::is_integer); static_assert(!std::numeric_limits::is_signed); return static_cast(x % m); } }; template <> struct ModIfNotZeroImpl { template constexpr Tval mod(Tval x) { return x; } }; template <> struct ModIfNotZeroImpl { template constexpr Tval mod(Tval x) { return x; } }; template <> struct ModIfNotZeroImpl { template constexpr Tval mod(Tval x) { return x; } }; template <> struct ModIfNotZeroImpl { template constexpr Tval mod(Tval x) { return x; } }; // Returns x % m if m != 0, x otherwise. // i.e. "return (m == 0) ? x : (x % m);", but without causing a warning with stupid older compilers template constexpr Tval modulo_if_not_zero(Tval x) { return ModIfNotZeroImpl().mod(x); } // rounds x up to multiples of target template constexpr T align_up(T x, T target) { return ((x + (target - 1)) / target) * target; } // rounds x down to multiples of target template constexpr T align_down(T x, T target) { return (x / target) * target; } // Returns sign of a number (-1 for negative numbers, 1 for positive numbers, 0 for 0) template constexpr int signum(T value) { return (value > T(0)) - (value < T(0)); } } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_BASE_ALGORITHM_HPP libopenmpt-0.6.1+release.autotools/src/mpt/base/pointer.hpp0000644000175000017500000000214614044173026020752 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_BASE_POINTER_HPP #define MPT_BASE_POINTER_HPP #include "mpt/base/namespace.hpp" #include namespace mpt { inline namespace MPT_INLINE_NS { inline constexpr int arch_bits = sizeof(void *) * 8; inline constexpr std::size_t pointer_size = sizeof(void *); template struct pointer_cast_helper { static constexpr Tdst cast(const Tsrc & src) noexcept { return src; } }; template struct pointer_cast_helper { static constexpr Tdst cast(const Tptr * const & src) noexcept { return reinterpret_cast(src); } }; template struct pointer_cast_helper { static constexpr Tdst cast(const Tptr * const & src) noexcept { return reinterpret_cast(src); } }; template constexpr Tdst pointer_cast(const Tsrc & src) noexcept { return pointer_cast_helper::cast(src); } } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_BASE_POINTER_HPP libopenmpt-0.6.1+release.autotools/src/mpt/base/preprocessor.hpp0000644000175000017500000000063314044173026022017 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_BASE_PREPROCESSOR_HPP #define MPT_BASE_PREPROCESSOR_HPP #define MPT_PP_DEFER(m, ...) m(__VA_ARGS__) #define MPT_PP_STRINGIFY(x) #x #define MPT_PP_JOIN_HELPER(a, b) a##b #define MPT_PP_JOIN(a, b) MPT_PP_JOIN_HELPER(a, b) #define MPT_PP_UNIQUE_IDENTIFIER(prefix) MPT_PP_JOIN(prefix, __LINE__) #endif // MPT_BASE_PREPROCESSOR_HPP libopenmpt-0.6.1+release.autotools/src/mpt/base/saturate_cast.hpp0000644000175000017500000000542614044173026022140 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_BASE_SATURATE_CAST_HPP #define MPT_BASE_SATURATE_CAST_HPP #include "mpt/base/namespace.hpp" #include namespace mpt { inline namespace MPT_INLINE_NS { // Saturate the value of src to the domain of Tdst template constexpr Tdst saturate_cast(Tsrc src) noexcept { // This code tries not only to obviously avoid overflows but also to avoid signed/unsigned comparison warnings and type truncation warnings (which in fact would be safe here) by explicit casting. static_assert(std::numeric_limits::is_integer); static_assert(std::numeric_limits::is_integer); if constexpr (std::numeric_limits::is_signed && std::numeric_limits::is_signed) { if constexpr (sizeof(Tdst) >= sizeof(Tsrc)) { return static_cast(src); } else { return static_cast(std::max(static_cast(std::numeric_limits::min()), std::min(src, static_cast(std::numeric_limits::max())))); } } else if constexpr (!std::numeric_limits::is_signed && !std::numeric_limits::is_signed) { if constexpr (sizeof(Tdst) >= sizeof(Tsrc)) { return static_cast(src); } else { return static_cast(std::min(src, static_cast(std::numeric_limits::max()))); } } else if constexpr (std::numeric_limits::is_signed && !std::numeric_limits::is_signed) { if constexpr (sizeof(Tdst) > sizeof(Tsrc)) { return static_cast(src); } else if constexpr (sizeof(Tdst) == sizeof(Tsrc)) { return static_cast(std::min(src, static_cast(std::numeric_limits::max()))); } else { return static_cast(std::min(src, static_cast(std::numeric_limits::max()))); } } else { // Tdst unsigned, Tsrc signed if constexpr (sizeof(Tdst) >= sizeof(Tsrc)) { return static_cast(std::max(static_cast(0), src)); } else { return static_cast(std::max(static_cast(0), std::min(src, static_cast(std::numeric_limits::max())))); } } } template constexpr Tdst saturate_cast(double src) { if (src >= static_cast(std::numeric_limits::max())) { return std::numeric_limits::max(); } if (src <= static_cast(std::numeric_limits::min())) { return std::numeric_limits::min(); } return static_cast(src); } template constexpr Tdst saturate_cast(float src) { if (src >= static_cast(std::numeric_limits::max())) { return std::numeric_limits::max(); } if (src <= static_cast(std::numeric_limits::min())) { return std::numeric_limits::min(); } return static_cast(src); } } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_BASE_SATURATE_CAST_HPP libopenmpt-0.6.1+release.autotools/src/mpt/base/saturate_round.hpp0000644000175000017500000000201514044173026022324 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_BASE_SATURATE_ROUND_HPP #define MPT_BASE_SATURATE_ROUND_HPP #include "mpt/base/namespace.hpp" #include "mpt/base/math.hpp" #include "mpt/base/saturate_cast.hpp" #include namespace mpt { inline namespace MPT_INLINE_NS { // Rounds given double value to nearest integer value of type T. // Out-of-range values are saturated to the specified integer type's limits. template inline T saturate_round(float val) { static_assert(std::numeric_limits::is_integer); return mpt::saturate_cast(mpt::round(val)); } template inline T saturate_round(double val) { static_assert(std::numeric_limits::is_integer); return mpt::saturate_cast(mpt::round(val)); } template inline T saturate_round(long double val) { static_assert(std::numeric_limits::is_integer); return mpt::saturate_cast(mpt::round(val)); } } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_BASE_SATURATE_ROUND_HPP libopenmpt-0.6.1+release.autotools/src/mpt/base/secure.hpp0000644000175000017500000001040714044173026020557 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_BASE_SECURE_HPP #define MPT_BASE_SECURE_HPP #include "mpt/base/integer.hpp" #include "mpt/base/macros.hpp" #include "mpt/base/namespace.hpp" #include #include #include #include namespace mpt { inline namespace MPT_INLINE_NS { namespace secure { inline MPT_NOINLINE void memzero(std::byte * const dst, std::size_t const len) noexcept { std::atomic_thread_fence(std::memory_order_seq_cst); volatile std::byte * volatile p = static_cast(dst); std::atomic_thread_fence(std::memory_order_seq_cst); for (volatile std::size_t i = 0; i < len; ++i) { p[i] = std::byte{0}; } std::atomic_thread_fence(std::memory_order_seq_cst); } inline MPT_NOINLINE void memzero(void * const dst, std::size_t const len) noexcept { std::atomic_thread_fence(std::memory_order_seq_cst); volatile std::byte * volatile p = static_cast(dst); std::atomic_thread_fence(std::memory_order_seq_cst); for (volatile std::size_t i = 0; i < len; ++i) { p[i] = std::byte{0}; } std::atomic_thread_fence(std::memory_order_seq_cst); } inline MPT_NOINLINE void memzero(char * const dst, std::size_t const len) noexcept { std::atomic_thread_fence(std::memory_order_seq_cst); volatile std::byte * volatile p = reinterpret_cast(dst); std::atomic_thread_fence(std::memory_order_seq_cst); for (volatile std::size_t i = 0; i < len; ++i) { p[i] = std::byte{0}; } std::atomic_thread_fence(std::memory_order_seq_cst); } inline MPT_NOINLINE void memzero(uint8 * const dst, std::size_t const len) noexcept { std::atomic_thread_fence(std::memory_order_seq_cst); volatile std::byte * volatile p = reinterpret_cast(dst); std::atomic_thread_fence(std::memory_order_seq_cst); for (volatile std::size_t i = 0; i < len; ++i) { p[i] = std::byte{0}; } std::atomic_thread_fence(std::memory_order_seq_cst); } template inline MPT_NOINLINE void clear(T & val) { std::atomic_signal_fence(std::memory_order_seq_cst); volatile T * volatile v = &val; std::atomic_thread_fence(std::memory_order_seq_cst); *v = T{}; std::atomic_signal_fence(std::memory_order_seq_cst); } class byte { private: std::byte value; public: byte() noexcept : value(std::byte{0}) { return; } explicit byte(std::byte value) noexcept : value(value) { return; } byte(const byte & other) noexcept : value(other.value) { return; } byte(byte && other) noexcept : value(std::move(other.value)) { mpt::secure::clear(other.value); } byte & operator=(const byte & other) noexcept { if (&other == this) { return *this; } value = other.value; return *this; } byte & operator==(byte && other) noexcept { if (&other == this) { return *this; } value = std::move(other.value); mpt::secure::clear(other.value); return *this; } explicit operator std::byte() const noexcept { return value; } ~byte() { mpt::secure::clear(value); } }; class buffer { private: std::vector m_data; public: buffer() : m_data(0) { return; } explicit buffer(const std::vector & data) : m_data(data) { return; } explicit buffer(const std::byte * beg, const std::byte * end) : m_data(beg, end) { return; } buffer(const buffer & other) : m_data(other.m_data) { return; } buffer(buffer && other) noexcept : m_data(std::move(other.m_data)) { mpt::secure::memzero(other.m_data.data(), other.m_data.size()); } buffer & operator=(const buffer & other) { if (&other == this) { return *this; } m_data = other.m_data; return *this; } buffer & operator=(buffer && other) noexcept { if (&other == this) { return *this; } m_data = std::move(other.m_data); mpt::secure::memzero(other.m_data.data(), other.m_data.size()); return *this; } ~buffer() { mpt::secure::memzero(m_data.data(), m_data.size()); m_data.resize(0); m_data.shrink_to_fit(); } explicit operator std::vector() const { return m_data; } const std::byte * data() const { return m_data.data(); } std::byte * data() { return m_data.data(); } std::size_t size() const { return m_data.size(); } }; } // namespace secure } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_BASE_SECURE_HPP libopenmpt-0.6.1+release.autotools/src/mpt/base/semantic_version.hpp0000644000175000017500000000554014044173026022643 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_BASE_SEMANTIC_VERSION_HPP #define MPT_BASE_SEMANTIC_VERSION_HPP #include "mpt/base/detect.hpp" #include "mpt/base/namespace.hpp" #include "mpt/base/version.hpp" #include namespace mpt { inline namespace MPT_INLINE_NS { struct semantic_version { unsigned long long major = 0; unsigned long long minor = 0; unsigned long long patch = 0; constexpr std::tuple as_tuple() const noexcept { return std::make_tuple(major, minor, patch); } }; constexpr bool operator==(semantic_version const a, semantic_version const b) noexcept { return a.as_tuple() == b.as_tuple(); } constexpr bool operator!=(semantic_version const a, semantic_version const b) noexcept { return a.as_tuple() != b.as_tuple(); } constexpr bool operator<(semantic_version const a, semantic_version const b) noexcept { return a.as_tuple() < b.as_tuple(); } constexpr bool operator>(semantic_version const a, semantic_version const b) noexcept { return a.as_tuple() > b.as_tuple(); } constexpr bool operator<=(semantic_version const a, semantic_version const b) noexcept { return a.as_tuple() <= b.as_tuple(); } constexpr bool operator>=(semantic_version const a, semantic_version const b) noexcept { return a.as_tuple() >= b.as_tuple(); } struct version_info { semantic_version semver{}; unsigned long long build = 0; constexpr std::tuple, unsigned long long> as_tuple() const noexcept { return std::make_tuple(semver.as_tuple(), build); } template friend Tostream & operator<<(Tostream & os, version_info const vi) { if (vi.build > 0) { os << vi.semver.major << "." << vi.semver.minor << "." << vi.semver.patch << "+build." << vi.build; } else { os << vi.semver.major << "." << vi.semver.minor << "." << vi.semver.patch; } return os; } }; constexpr bool operator==(version_info const a, version_info const b) noexcept { return a.as_tuple() == b.as_tuple(); } constexpr bool operator!=(version_info const a, version_info const b) noexcept { return a.as_tuple() != b.as_tuple(); } constexpr bool operator<(version_info const a, version_info const b) noexcept { return a.as_tuple() < b.as_tuple(); } constexpr bool operator>(version_info const a, version_info const b) noexcept { return a.as_tuple() > b.as_tuple(); } constexpr bool operator<=(version_info const a, version_info const b) noexcept { return a.as_tuple() <= b.as_tuple(); } constexpr bool operator>=(version_info const a, version_info const b) noexcept { return a.as_tuple() >= b.as_tuple(); } constexpr inline version_info Version = {{MPT_VERSION_MAJOR, MPT_VERSION_MINOR, MPT_VERSION_PATCH}, MPT_VERSION_BUILD}; } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_BASE_SEMANTIC_VERSION_HPP libopenmpt-0.6.1+release.autotools/src/mpt/base/source_location.hpp0000644000175000017500000000616414161645744022501 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_BASE_SOURCE_LOCATION_HPP #define MPT_BASE_SOURCE_LOCATION_HPP #include "mpt/base/detect.hpp" #include "mpt/base/integer.hpp" #include "mpt/base/namespace.hpp" #if MPT_CXX_AT_LEAST(20) && !MPT_MSVC_BEFORE(2022, 0) && !MPT_COMPILER_CLANG #include #endif // C++20 namespace mpt { inline namespace MPT_INLINE_NS { #if MPT_CXX_AT_LEAST(20) && !MPT_MSVC_BEFORE(2022, 0) && !MPT_COMPILER_CLANG using std::source_location; #define MPT_SOURCE_LOCATION_CURRENT() std::source_location::current() #else // !C++20 #if MPT_COMPILER_MSVC && MPT_MSVC_AT_LEAST(2019, 6) #define MPT_SOURCE_LOCATION_FILE __builtin_FILE() #define MPT_SOURCE_LOCATION_FUNCTION __builtin_FUNCTION() #define MPT_SOURCE_LOCATION_LINE __builtin_LINE() #define MPT_SOURCE_LOCATION_COLUMN __builtin_COLUMN() #elif MPT_COMPILER_GCC #define MPT_SOURCE_LOCATION_FILE __builtin_FILE() #define MPT_SOURCE_LOCATION_FUNCTION __builtin_FUNCTION() #define MPT_SOURCE_LOCATION_LINE __builtin_LINE() #define MPT_SOURCE_LOCATION_COLUMN 0 #elif MPT_COMPILER_CLANG && MPT_CLANG_AT_LEAST(9, 0, 0) #define MPT_SOURCE_LOCATION_FILE __builtin_FILE() #define MPT_SOURCE_LOCATION_FUNCTION __builtin_FUNCTION() #define MPT_SOURCE_LOCATION_LINE __builtin_LINE() #define MPT_SOURCE_LOCATION_COLUMN __builtin_COLUMN() #else #define MPT_SOURCE_LOCATION_FILE __FILE__ #define MPT_SOURCE_LOCATION_FUNCTION "" #define MPT_SOURCE_LOCATION_LINE __LINE__ #define MPT_SOURCE_LOCATION_COLUMN 0 #endif // compatible with C++20 std::source_location struct source_location { private: const char * m_file_name; const char * m_function_name; uint32 m_line; uint32 m_column; public: constexpr source_location() noexcept : m_file_name("") , m_function_name("") , m_line(0) , m_column(0) { } constexpr source_location(const char * file, const char * function, uint32 line, uint32 column) noexcept : m_file_name(file) , m_function_name(function) , m_line(line) , m_column(column) { } source_location(const source_location &) = default; source_location(source_location &&) = default; static constexpr source_location current(const char * file = MPT_SOURCE_LOCATION_FILE, const char * function = MPT_SOURCE_LOCATION_FUNCTION, uint32 line = MPT_SOURCE_LOCATION_LINE, uint32 column = MPT_SOURCE_LOCATION_COLUMN) noexcept { return source_location(file, function, line, column); } constexpr uint32 line() const noexcept { return m_line; } constexpr uint32 column() const noexcept { return m_column; } constexpr const char * file_name() const noexcept { return m_file_name; } constexpr const char * function_name() const noexcept { return m_function_name; } }; #if (MPT_COMPILER_MSVC && MPT_MSVC_AT_LEAST(2019, 6)) || MPT_COMPILER_GCC || (MPT_COMPILER_CLANG && MPT_CLANG_AT_LEAST(9, 0, 0)) #define MPT_SOURCE_LOCATION_CURRENT() mpt::source_location::current() #else #define MPT_SOURCE_LOCATION_CURRENT() mpt::source_location::current(__FILE__, __func__, __LINE__, 0) #endif #endif // C++20 } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_BASE_SOURCE_LOCATION_HPP libopenmpt-0.6.1+release.autotools/src/mpt/base/span.hpp0000644000175000017500000000742514144166425020246 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_BASE_SPAN_HPP #define MPT_BASE_SPAN_HPP #include "mpt/base/detect.hpp" #include "mpt/base/namespace.hpp" #include #if MPT_CXX_AT_LEAST(20) #include #else // !C++20 #include #include #include #endif // C++20 #if MPT_CXX_BEFORE(20) #include #endif // !C++20 namespace mpt { inline namespace MPT_INLINE_NS { #if MPT_CXX_AT_LEAST(20) using std::dynamic_extent; using std::span; #else // !C++20 // Simplified version of gsl::span. // Non-owning read-only or read-write view into a contiguous block of T // objects, i.e. equivalent to a (beg,end) or (data,size) tuple. // Can eventually be replaced without further modifications with a full C++20 // std::span. inline constexpr std::size_t dynamic_extent = std::numeric_limits::max(); template class span { public: using element_type = T; using value_type = typename std::remove_cv::type; using index_type = std::size_t; using pointer = T *; using const_pointer = const T *; using reference = T &; using const_reference = const T &; using iterator = pointer; using difference_type = typename std::iterator_traits::difference_type; private: T * m_data; std::size_t m_size; public: span() noexcept : m_data(nullptr) , m_size(0) { } span(pointer beg, pointer end) : m_data(beg) , m_size(end - beg) { } span(pointer data, index_type size) : m_data(data) , m_size(size) { } template span(element_type (&arr)[N]) : m_data(arr) , m_size(N) { } template span(std::array & arr) : m_data(arr.data()) , m_size(arr.size()) { } template span(const std::array & arr) : m_data(arr.data()) , m_size(arr.size()) { } span(const span & other) noexcept = default; template span(const span & other) : m_data(other.data()) , m_size(other.size()) { } span & operator=(const span & other) noexcept = default; iterator begin() const { return iterator(m_data); } iterator end() const { return iterator(m_data + m_size); } reference operator[](index_type index) { return m_data[index]; } const_reference operator[](index_type index) const { return m_data[index]; } bool operator==(span const & other) const noexcept { return size() == other.size() && (m_data == other.m_data || std::equal(begin(), end(), other.begin())); } bool operator!=(span const & other) const noexcept { return !(*this == other); } pointer data() const noexcept { return m_data; } bool empty() const noexcept { return size() == 0; } index_type size() const noexcept { return m_size; } index_type length() const noexcept { return size(); } span subspan(std::size_t offset, std::size_t count = mpt::dynamic_extent) const { return span(data() + offset, (count == mpt::dynamic_extent) ? (size() - offset) : count); } span first(std::size_t count) const { return span(data(), count); } span last(std::size_t count) const { return span(data() + (size() - count), count); } }; // class span #endif // C++20 template inline span as_span(T * beg, T * end) { return span(beg, end); } template inline span as_span(T * data, std::size_t size) { return span(data, size); } template inline span as_span(T (&arr)[N]) { return span(std::begin(arr), std::end(arr)); } template inline span as_span(std::array & cont) { return span(cont); } template inline span as_span(const std::array & cont) { return span(cont); } } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_BASE_SPAN_HPP libopenmpt-0.6.1+release.autotools/src/mpt/base/utility.hpp0000644000175000017500000001033714161645744021011 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_BASE_UTILITY_HPP #define MPT_BASE_UTILITY_HPP #include "mpt/base/detect_compiler.hpp" #include "mpt/base/namespace.hpp" #if MPT_CXX_BEFORE(20) #include "mpt/base/saturate_cast.hpp" #endif #include #include #include namespace mpt { inline namespace MPT_INLINE_NS { #if MPT_CXX_AT_LEAST(20) && !MPT_CLANG_BEFORE(13, 0, 0) using std::in_range; #else // Returns true iff Tdst can represent the value val. // Use as if(mpt::in_range(-1)). template constexpr bool in_range(Tsrc val) { return (static_cast(mpt::saturate_cast(val)) == val); } #endif #if MPT_CXX_AT_LEAST(23) using std::to_underlying; #else template constexpr std::underlying_type_t to_underlying(T value) noexcept { return static_cast::type>(value); } #endif template struct value_initializer { inline void operator()(T & x) { x = T{}; } }; template struct value_initializer { inline void operator()(T (&a)[N]) { for (auto & e : a) { value_initializer{}(e); } } }; template inline void reset(T & x) { value_initializer{}(x); } #if MPT_CXX_AT_LEAST(20) && !MPT_CLANG_BEFORE(13, 0, 0) using std::cmp_equal; using std::cmp_greater; using std::cmp_greater_equal; using std::cmp_less; using std::cmp_less_equal; using std::cmp_not_equal; #else template constexpr bool cmp_equal(Ta a, Tb b) noexcept { using UTa = typename std::make_unsigned::type; using UTb = typename std::make_unsigned::type; if constexpr (std::is_signed::value == std::is_signed::value) { return a == b; } else if constexpr (std::is_signed::value) { return (a < 0) ? false : static_cast(a) == b; } else { return (b < 0) ? false : a == static_cast(b); } } template constexpr bool cmp_not_equal(Ta a, Tb b) noexcept { using UTa = typename std::make_unsigned::type; using UTb = typename std::make_unsigned::type; if constexpr (std::is_signed::value == std::is_signed::value) { return a != b; } else if constexpr (std::is_signed::value) { return (a < 0) ? true : static_cast(a) != b; } else { return (b < 0) ? true : a != static_cast(b); } } template constexpr bool cmp_less(Ta a, Tb b) noexcept { using UTa = typename std::make_unsigned::type; using UTb = typename std::make_unsigned::type; if constexpr (std::is_signed::value == std::is_signed::value) { return a < b; } else if constexpr (std::is_signed::value) { return (a < 0) ? true : static_cast(a) < b; } else { return (b < 0) ? false : a < static_cast(b); } } template constexpr bool cmp_greater(Ta a, Tb b) noexcept { using UTa = typename std::make_unsigned::type; using UTb = typename std::make_unsigned::type; if constexpr (std::is_signed::value == std::is_signed::value) { return a > b; } else if constexpr (std::is_signed::value) { return (a < 0) ? false : static_cast(a) > b; } else { return (b < 0) ? true : a > static_cast(b); } } template constexpr bool cmp_less_equal(Ta a, Tb b) noexcept { using UTa = typename std::make_unsigned::type; using UTb = typename std::make_unsigned::type; if constexpr (std::is_signed::value == std::is_signed::value) { return a <= b; } else if constexpr (std::is_signed::value) { return (a < 0) ? true : static_cast(a) <= b; } else { return (b < 0) ? false : a <= static_cast(b); } } template constexpr bool cmp_greater_equal(Ta a, Tb b) noexcept { using UTa = typename std::make_unsigned::type; using UTb = typename std::make_unsigned::type; if constexpr (std::is_signed::value == std::is_signed::value) { return a >= b; } else if constexpr (std::is_signed::value) { return (a < 0) ? false : static_cast(a) >= b; } else { return (b < 0) ? true : a >= static_cast(b); } } #endif } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_BASE_UTILITY_HPP libopenmpt-0.6.1+release.autotools/src/mpt/base/version.hpp0000644000175000017500000000040714044173026020755 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_BASE_VERSION_HPP #define MPT_BASE_VERSION_HPP #define MPT_VERSION_MAJOR 0 #define MPT_VERSION_MINOR 0 #define MPT_VERSION_PATCH 0 #define MPT_VERSION_BUILD 0 #endif // MPT_BASE_VERSION_HPP libopenmpt-0.6.1+release.autotools/src/mpt/base/wrapping_divide.hpp0000644000175000017500000000154614044173026022450 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_BASE_WRAPPING_DIVIDE_HPP #define MPT_BASE_WRAPPING_DIVIDE_HPP #include "mpt/base/namespace.hpp" namespace mpt { inline namespace MPT_INLINE_NS { // Modulo with more intuitive behaviour for some contexts: // Instead of being symmetrical around 0, the pattern for positive numbers is repeated in the negative range. // For example, wrapping_modulo(-1, m) == (m - 1). // Behaviour is undefined if m<=0. template constexpr auto wrapping_modulo(T x, M m) -> decltype(x % m) { return (x >= 0) ? (x % m) : (m - 1 - ((-1 - x) % m)); } template constexpr auto wrapping_divide(T x, D d) -> decltype(x / d) { return (x >= 0) ? (x / d) : (((x + 1) / d) - 1); } } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_BASE_WRAPPING_DIVIDE_HPP libopenmpt-0.6.1+release.autotools/src/mpt/binary/0000755000175000017500000000000014175541574017224 500000000000000libopenmpt-0.6.1+release.autotools/src/mpt/binary/tests/0000755000175000017500000000000014175541575020367 500000000000000libopenmpt-0.6.1+release.autotools/src/mpt/binary/tests/tests_binary.hpp0000644000175000017500000001304414044173026023513 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_BASE_TESTS_BINARY_HPP #define MPT_BASE_TESTS_BINARY_HPP #include "mpt/base/detect_compiler.hpp" #include "mpt/base/memory.hpp" #include "mpt/base/namespace.hpp" #include "mpt/base/span.hpp" #include "mpt/binary/base64.hpp" #include "mpt/binary/base64url.hpp" #include "mpt/string/types.hpp" #include "mpt/test/test.hpp" #include "mpt/test/test_macros.hpp" #include #include #include namespace mpt { inline namespace MPT_INLINE_NS { namespace tests { namespace binary { #if MPT_COMPILER_CLANG #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wglobal-constructors" #endif MPT_TEST_GROUP_INLINE("mpt/binary") #if MPT_COMPILER_CLANG #pragma clang diagnostic pop #endif { { std::string expecteds = std::string("pleasure."); std::vector expected(mpt::byte_cast(mpt::as_span(expecteds)).data(), mpt::byte_cast(mpt::as_span(expecteds)).data() + mpt::byte_cast(mpt::as_span(expecteds)).size()); MPT_TEST_EXPECT_EQUAL(mpt::encode_base64(mpt::as_span(expected)), MPT_USTRING("cGxlYXN1cmUu")); } { std::string expecteds = std::string("leasure."); std::vector expected(mpt::byte_cast(mpt::as_span(expecteds)).data(), mpt::byte_cast(mpt::as_span(expecteds)).data() + mpt::byte_cast(mpt::as_span(expecteds)).size()); MPT_TEST_EXPECT_EQUAL(mpt::encode_base64(mpt::as_span(expected)), MPT_USTRING("bGVhc3VyZS4=")); } { std::string expecteds = std::string("easure."); std::vector expected(mpt::byte_cast(mpt::as_span(expecteds)).data(), mpt::byte_cast(mpt::as_span(expecteds)).data() + mpt::byte_cast(mpt::as_span(expecteds)).size()); MPT_TEST_EXPECT_EQUAL(mpt::encode_base64(mpt::as_span(expected)), MPT_USTRING("ZWFzdXJlLg==")); } { std::string expecteds = std::string("pleasure."); std::vector expected(mpt::byte_cast(mpt::as_span(expecteds)).data(), mpt::byte_cast(mpt::as_span(expecteds)).data() + mpt::byte_cast(mpt::as_span(expecteds)).size()); MPT_TEST_EXPECT_EQUAL(expected, mpt::decode_base64(MPT_USTRING("cGxlYXN1cmUu"))); } { std::string expecteds = std::string("leasure."); std::vector expected(mpt::byte_cast(mpt::as_span(expecteds)).data(), mpt::byte_cast(mpt::as_span(expecteds)).data() + mpt::byte_cast(mpt::as_span(expecteds)).size()); MPT_TEST_EXPECT_EQUAL(expected, mpt::decode_base64(MPT_USTRING("bGVhc3VyZS4="))); } { std::string expecteds = std::string("easure."); std::vector expected(mpt::byte_cast(mpt::as_span(expecteds)).data(), mpt::byte_cast(mpt::as_span(expecteds)).data() + mpt::byte_cast(mpt::as_span(expecteds)).size()); MPT_TEST_EXPECT_EQUAL(expected, mpt::decode_base64(MPT_USTRING("ZWFzdXJlLg=="))); } { std::string expecteds = std::string("pleasure."); std::vector expected(mpt::byte_cast(mpt::as_span(expecteds)).data(), mpt::byte_cast(mpt::as_span(expecteds)).data() + mpt::byte_cast(mpt::as_span(expecteds)).size()); MPT_TEST_EXPECT_EQUAL(mpt::encode_base64url(mpt::as_span(expected)), MPT_USTRING("cGxlYXN1cmUu")); } { std::string expecteds = std::string("leasure."); std::vector expected(mpt::byte_cast(mpt::as_span(expecteds)).data(), mpt::byte_cast(mpt::as_span(expecteds)).data() + mpt::byte_cast(mpt::as_span(expecteds)).size()); MPT_TEST_EXPECT_EQUAL(mpt::encode_base64url(mpt::as_span(expected)), MPT_USTRING("bGVhc3VyZS4")); } { std::string expecteds = std::string("easure."); std::vector expected(mpt::byte_cast(mpt::as_span(expecteds)).data(), mpt::byte_cast(mpt::as_span(expecteds)).data() + mpt::byte_cast(mpt::as_span(expecteds)).size()); MPT_TEST_EXPECT_EQUAL(mpt::encode_base64url(mpt::as_span(expected)), MPT_USTRING("ZWFzdXJlLg")); } { std::string expecteds = std::string("pleasure."); std::vector expected(mpt::byte_cast(mpt::as_span(expecteds)).data(), mpt::byte_cast(mpt::as_span(expecteds)).data() + mpt::byte_cast(mpt::as_span(expecteds)).size()); MPT_TEST_EXPECT_EQUAL(expected, mpt::decode_base64url(MPT_USTRING("cGxlYXN1cmUu"))); } { std::string expecteds = std::string("leasure."); std::vector expected(mpt::byte_cast(mpt::as_span(expecteds)).data(), mpt::byte_cast(mpt::as_span(expecteds)).data() + mpt::byte_cast(mpt::as_span(expecteds)).size()); MPT_TEST_EXPECT_EQUAL(expected, mpt::decode_base64url(MPT_USTRING("bGVhc3VyZS4"))); } { std::string expecteds = std::string("easure."); std::vector expected(mpt::byte_cast(mpt::as_span(expecteds)).data(), mpt::byte_cast(mpt::as_span(expecteds)).data() + mpt::byte_cast(mpt::as_span(expecteds)).size()); MPT_TEST_EXPECT_EQUAL(expected, mpt::decode_base64url(MPT_USTRING("ZWFzdXJlLg"))); } } } // namespace binary } // namespace tests } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_BASE_TESTS_BINARY_HPP libopenmpt-0.6.1+release.autotools/src/mpt/binary/base64.hpp0000644000175000017500000000733514044173026020735 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_BINARY_BASE64_HPP #define MPT_BINARY_BASE64_HPP #include "mpt/base/integer.hpp" #include "mpt/base/memory.hpp" #include "mpt/base/namespace.hpp" #include "mpt/string/types.hpp" #include #include #include #include namespace mpt { inline namespace MPT_INLINE_NS { class base64_parse_error : public std::runtime_error { public: base64_parse_error() : std::runtime_error("invalid Base64 encoding") { } }; inline constexpr std::array base64 = { {MPT_UCHAR('A'), MPT_UCHAR('B'), MPT_UCHAR('C'), MPT_UCHAR('D'), MPT_UCHAR('E'), MPT_UCHAR('F'), MPT_UCHAR('G'), MPT_UCHAR('H'), MPT_UCHAR('I'), MPT_UCHAR('J'), MPT_UCHAR('K'), MPT_UCHAR('L'), MPT_UCHAR('M'), MPT_UCHAR('N'), MPT_UCHAR('O'), MPT_UCHAR('P'), MPT_UCHAR('Q'), MPT_UCHAR('R'), MPT_UCHAR('S'), MPT_UCHAR('T'), MPT_UCHAR('U'), MPT_UCHAR('V'), MPT_UCHAR('W'), MPT_UCHAR('X'), MPT_UCHAR('Y'), MPT_UCHAR('Z'), MPT_UCHAR('a'), MPT_UCHAR('b'), MPT_UCHAR('c'), MPT_UCHAR('d'), MPT_UCHAR('e'), MPT_UCHAR('f'), MPT_UCHAR('g'), MPT_UCHAR('h'), MPT_UCHAR('i'), MPT_UCHAR('j'), MPT_UCHAR('k'), MPT_UCHAR('l'), MPT_UCHAR('m'), MPT_UCHAR('n'), MPT_UCHAR('o'), MPT_UCHAR('p'), MPT_UCHAR('q'), MPT_UCHAR('r'), MPT_UCHAR('s'), MPT_UCHAR('t'), MPT_UCHAR('u'), MPT_UCHAR('v'), MPT_UCHAR('w'), MPT_UCHAR('x'), MPT_UCHAR('y'), MPT_UCHAR('z'), MPT_UCHAR('0'), MPT_UCHAR('1'), MPT_UCHAR('2'), MPT_UCHAR('3'), MPT_UCHAR('4'), MPT_UCHAR('5'), MPT_UCHAR('6'), MPT_UCHAR('7'), MPT_UCHAR('8'), MPT_UCHAR('9'), MPT_UCHAR('+'), MPT_UCHAR('/')}}; template inline mpt::ustring encode_base64(mpt::span src_) { mpt::const_byte_span src = mpt::byte_cast(src_); mpt::ustring result; result.reserve(4 * ((src.size() + 2) / 3)); uint32 bits = 0; std::size_t bytes = 0; for (std::byte byte : src) { bits <<= 8; bits |= mpt::byte_cast(byte); bytes++; if (bytes == 3) { result.push_back(base64[(bits >> 18) & 0x3f]); result.push_back(base64[(bits >> 12) & 0x3f]); result.push_back(base64[(bits >> 6) & 0x3f]); result.push_back(base64[(bits >> 0) & 0x3f]); bits = 0; bytes = 0; } } std::size_t padding = 0; while (bytes != 0) { bits <<= 8; padding++; bytes++; if (bytes == 3) { result.push_back(base64[(bits >> 18) & 0x3f]); result.push_back(base64[(bits >> 12) & 0x3f]); if (padding > 1) { result.push_back(MPT_UCHAR('=')); } else { result.push_back(base64[(bits >> 6) & 0x3f]); } if (padding > 0) { result.push_back(MPT_UCHAR('=')); } else { result.push_back(base64[(bits >> 0) & 0x3f]); } bits = 0; bytes = 0; } } return result; } inline uint8 decode_base64_bits(mpt::uchar c) { for (uint8 i = 0; i < 64; ++i) { if (base64[i] == c) { return i; } } throw base64_parse_error(); } inline std::vector decode_base64(const mpt::ustring & src) { std::vector result; result.reserve(3 * (src.length() / 4)); uint32 bits = 0; std::size_t chars = 0; std::size_t padding = 0; for (mpt::uchar c : src) { bits <<= 6; if (c == MPT_UCHAR('=')) { padding++; } else { bits |= decode_base64_bits(c); } chars++; if (chars == 4) { result.push_back(mpt::byte_cast(static_cast((bits >> 16) & 0xff))); if (padding < 2) { result.push_back(mpt::byte_cast(static_cast((bits >> 8) & 0xff))); } if (padding < 1) { result.push_back(mpt::byte_cast(static_cast((bits >> 0) & 0xff))); } bits = 0; chars = 0; padding = 0; } } if (chars != 0) { throw base64_parse_error(); } return result; } } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_BINARY_BASE64_HPP libopenmpt-0.6.1+release.autotools/src/mpt/binary/base64url.hpp0000644000175000017500000001001314044173026021443 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_BINARY_BASE64URL_HPP #define MPT_BINARY_BASE64URL_HPP #include "mpt/base/integer.hpp" #include "mpt/base/memory.hpp" #include "mpt/base/namespace.hpp" #include "mpt/string/types.hpp" #include #include #include #include namespace mpt { inline namespace MPT_INLINE_NS { class base64url_parse_error : public std::runtime_error { public: base64url_parse_error() : std::runtime_error("invalid Base64URL encoding") { } }; inline constexpr std::array base64url = { {MPT_UCHAR('A'), MPT_UCHAR('B'), MPT_UCHAR('C'), MPT_UCHAR('D'), MPT_UCHAR('E'), MPT_UCHAR('F'), MPT_UCHAR('G'), MPT_UCHAR('H'), MPT_UCHAR('I'), MPT_UCHAR('J'), MPT_UCHAR('K'), MPT_UCHAR('L'), MPT_UCHAR('M'), MPT_UCHAR('N'), MPT_UCHAR('O'), MPT_UCHAR('P'), MPT_UCHAR('Q'), MPT_UCHAR('R'), MPT_UCHAR('S'), MPT_UCHAR('T'), MPT_UCHAR('U'), MPT_UCHAR('V'), MPT_UCHAR('W'), MPT_UCHAR('X'), MPT_UCHAR('Y'), MPT_UCHAR('Z'), MPT_UCHAR('a'), MPT_UCHAR('b'), MPT_UCHAR('c'), MPT_UCHAR('d'), MPT_UCHAR('e'), MPT_UCHAR('f'), MPT_UCHAR('g'), MPT_UCHAR('h'), MPT_UCHAR('i'), MPT_UCHAR('j'), MPT_UCHAR('k'), MPT_UCHAR('l'), MPT_UCHAR('m'), MPT_UCHAR('n'), MPT_UCHAR('o'), MPT_UCHAR('p'), MPT_UCHAR('q'), MPT_UCHAR('r'), MPT_UCHAR('s'), MPT_UCHAR('t'), MPT_UCHAR('u'), MPT_UCHAR('v'), MPT_UCHAR('w'), MPT_UCHAR('x'), MPT_UCHAR('y'), MPT_UCHAR('z'), MPT_UCHAR('0'), MPT_UCHAR('1'), MPT_UCHAR('2'), MPT_UCHAR('3'), MPT_UCHAR('4'), MPT_UCHAR('5'), MPT_UCHAR('6'), MPT_UCHAR('7'), MPT_UCHAR('8'), MPT_UCHAR('9'), MPT_UCHAR('-'), MPT_UCHAR('_')}}; template inline mpt::ustring encode_base64url(mpt::span src_) { mpt::const_byte_span src = mpt::byte_cast(src_); mpt::ustring result; result.reserve(4 * ((src.size() + 2) / 3)); uint32 bits = 0; std::size_t bytes = 0; for (std::byte byte : src) { bits <<= 8; bits |= mpt::byte_cast(byte); bytes++; if (bytes == 3) { result.push_back(base64url[(bits >> 18) & 0x3f]); result.push_back(base64url[(bits >> 12) & 0x3f]); result.push_back(base64url[(bits >> 6) & 0x3f]); result.push_back(base64url[(bits >> 0) & 0x3f]); bits = 0; bytes = 0; } } std::size_t padding = 0; while (bytes != 0) { bits <<= 8; padding++; bytes++; if (bytes == 3) { result.push_back(base64url[(bits >> 18) & 0x3f]); result.push_back(base64url[(bits >> 12) & 0x3f]); if (padding <= 1) { result.push_back(base64url[(bits >> 6) & 0x3f]); } if (padding <= 0) { result.push_back(base64url[(bits >> 0) & 0x3f]); } bits = 0; bytes = 0; } } return result; } inline uint8 decode_base64url_bits(mpt::uchar c) { for (uint8 i = 0; i < 64; ++i) { if (base64url[i] == c) { return i; } } throw base64url_parse_error(); } inline std::vector decode_base64url(const mpt::ustring & src) { std::vector result; result.reserve(3 * ((src.length() + 2) / 4)); uint32 bits = 0; std::size_t chars = 0; for (mpt::uchar c : src) { bits <<= 6; bits |= decode_base64url_bits(c); chars++; if (chars == 4) { result.push_back(mpt::byte_cast(static_cast((bits >> 16) & 0xff))); result.push_back(mpt::byte_cast(static_cast((bits >> 8) & 0xff))); result.push_back(mpt::byte_cast(static_cast((bits >> 0) & 0xff))); bits = 0; chars = 0; } } uint32 padding = 0; if (chars != 0 && chars < 2) { throw base64url_parse_error(); } while (chars != 0) { bits <<= 6; padding++; chars++; if (chars == 4) { result.push_back(mpt::byte_cast(static_cast((bits >> 16) & 0xff))); if (padding < 2) { result.push_back(mpt::byte_cast(static_cast((bits >> 8) & 0xff))); } if (padding < 1) { result.push_back(mpt::byte_cast(static_cast((bits >> 0) & 0xff))); } bits = 0; chars = 0; padding = 0; } } return result; } } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_BINARY_BASE64URL_HPP libopenmpt-0.6.1+release.autotools/src/mpt/binary/hex.hpp0000644000175000017500000000451314044173026020430 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_BINARY_HEX_HPP #define MPT_BINARY_HEX_HPP #include "mpt/base/integer.hpp" #include "mpt/base/memory.hpp" #include "mpt/base/namespace.hpp" #include "mpt/string/types.hpp" #include #include #include namespace mpt { inline namespace MPT_INLINE_NS { inline constexpr std::array encode_nibble = { {MPT_UCHAR('0'), MPT_UCHAR('1'), MPT_UCHAR('2'), MPT_UCHAR('3'), MPT_UCHAR('4'), MPT_UCHAR('5'), MPT_UCHAR('6'), MPT_UCHAR('7'), MPT_UCHAR('8'), MPT_UCHAR('9'), MPT_UCHAR('A'), MPT_UCHAR('B'), MPT_UCHAR('C'), MPT_UCHAR('D'), MPT_UCHAR('E'), MPT_UCHAR('F')}}; inline bool decode_byte(uint8 & byte, mpt::uchar c1, mpt::uchar c2) { byte = 0; if (MPT_UCHAR('0') <= c1 && c1 <= MPT_UCHAR('9')) { byte += static_cast((c1 - MPT_UCHAR('0')) << 4); } else if (MPT_UCHAR('A') <= c1 && c1 <= MPT_UCHAR('F')) { byte += static_cast((c1 - MPT_UCHAR('A') + 10) << 4); } else if (MPT_UCHAR('a') <= c1 && c1 <= MPT_UCHAR('f')) { byte += static_cast((c1 - MPT_UCHAR('a') + 10) << 4); } else { return false; } if (MPT_UCHAR('0') <= c2 && c2 <= MPT_UCHAR('9')) { byte += static_cast(c2 - MPT_UCHAR('0')); } else if (MPT_UCHAR('A') <= c2 && c2 <= MPT_UCHAR('F')) { byte += static_cast(c2 - MPT_UCHAR('A') + 10); } else if (MPT_UCHAR('a') <= c2 && c2 <= MPT_UCHAR('f')) { byte += static_cast(c2 - MPT_UCHAR('a') + 10); } else { return false; } return true; } template inline mpt::ustring encode_hex(mpt::span src_) { mpt::const_byte_span src = mpt::byte_cast(src_); mpt::ustring result; result.reserve(src.size() * 2); for (std::byte byte : src) { result.push_back(encode_nibble[(mpt::byte_cast(byte) & 0xf0) >> 4]); result.push_back(encode_nibble[mpt::byte_cast(byte) & 0x0f]); } return result; } inline std::vector decode_hex(const mpt::ustring & src) { std::vector result; result.reserve(src.size() / 2); for (std::size_t i = 0; (i + 1) < src.size(); i += 2) { uint8 byte = 0; if (!decode_byte(byte, src[i], src[i + 1])) { return result; } result.push_back(mpt::byte_cast(byte)); } return result; } } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_BINARY_HEX_HPP libopenmpt-0.6.1+release.autotools/src/mpt/check/0000755000175000017500000000000014175541574017015 500000000000000libopenmpt-0.6.1+release.autotools/src/mpt/check/libc.hpp0000644000175000017500000000232314163324560020346 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_CHECK_LIBC_HPP #define MPT_CHECK_LIBC_HPP #include "mpt/base/detect_os.hpp" #include "mpt/base/compiletime_warning.hpp" #ifndef __STDC_CONSTANT_MACROS #ifndef MPT_CHECK_LIBC_IGNORE_WARNING_NO_STDC_CONSTANT_MACROS MPT_WARNING("C stdlib does not provide constant macros. Please #define __STDC_CONSTANT_MACROS.") #endif #endif #ifndef __STDC_FORMAT_MACROS #ifndef MPT_CHECK_LIBC_IGNORE_WARNING_NO_STDC_FORMAT_MACROS MPT_WARNING("C stdlib does not provide limit macros. Please #define __STDC_FORMAT_MACROS.") #endif #endif #ifndef __STDC_LIMIT_MACROS #ifndef MPT_CHECK_LIBC_IGNORE_WARNING_NO_STDC_LIMIT_MACROS MPT_WARNING("C stdlib does not provide limit macros. Please #define __STDC_LIMIT_MACROS.") #endif #endif #ifndef _USE_MATH_DEFINES #ifndef MPT_CHECK_LIBC_IGNORE_WARNING_NO_USE_MATH_DEFINES MPT_WARNING("C stdlib does not provide math constants. Please #define _USE_MATH_DEFINES.") #endif #endif #if MPT_LIBC_GLIBC #if !defined(_FILE_OFFSET_BITS) #ifndef MPT_CHECK_LIBC_IGNORE_WARNING_NO_FILE_OFFSET_BITS MPT_WARNING("C stdlib may not provide 64bit std::FILE access. Please #define _FILE_OFFSET_BITS=64.") #endif #endif #endif #endif // MPT_CHECK_LIBC_HPP libopenmpt-0.6.1+release.autotools/src/mpt/check/mfc.hpp0000644000175000017500000000102114044173026020171 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_CHECK_MFC_HPP #define MPT_CHECK_MFC_HPP #include "mpt/base/compiletime_warning.hpp" #include "mpt/detect/mfc.hpp" #if MPT_DETECTED_MFC #ifndef _CSTRING_DISABLE_NARROW_WIDE_CONVERSION #ifndef MPT_CHECK_MFC_IGNORE_WARNING_NO_CSTRING_DISABLE_NARROW_WIDE_CONVERSION MPT_WARNING("MFC uses CString with automatic encoding conversions. Please #define _CSTRING_DISABLE_NARROW_WIDE_CONVERSION.") #endif #endif #endif // MPT_DETECTED_MFC #endif // MPT_CHECK_MFC_HPP libopenmpt-0.6.1+release.autotools/src/mpt/check/windows.hpp0000644000175000017500000000113414044173026021123 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_CHECK_WINDOWS_HPP #define MPT_CHECK_WINDOWS_HPP #include "mpt/base/detect_os.hpp" #include "mpt/base/compiletime_warning.hpp" #if MPT_OS_WINDOWS #ifndef UNICODE #ifndef MPT_CHECK_WINDOWS_IGNORE_WARNING_NO_UNICODE MPT_WARNING("windows.h uses MBCS TCHAR. Please #define UNICODE.") #endif #endif #ifndef NOMINMAX #ifndef MPT_CHECK_WINDOWS_IGNORE_WARNING_NO_NOMINMAX MPT_WARNING("windows.h defines min and max which conflicts with C++. Please #define NOMINMAX.") #endif #endif #endif // MPT_OS_WINDOWS #endif // MPT_CHECK_WINDOWS_HPP libopenmpt-0.6.1+release.autotools/src/mpt/crc/0000755000175000017500000000000014175541574016507 500000000000000libopenmpt-0.6.1+release.autotools/src/mpt/crc/tests/0000755000175000017500000000000014175541575017652 500000000000000libopenmpt-0.6.1+release.autotools/src/mpt/crc/tests/tests_crc.hpp0000644000175000017500000000157314044173026022265 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_BASE_TESTS_CRC_HPP #define MPT_BASE_TESTS_CRC_HPP #include "mpt/base/detect_compiler.hpp" #include "mpt/base/namespace.hpp" #include "mpt/crc/crc.hpp" #include "mpt/test/test.hpp" #include "mpt/test/test_macros.hpp" #include namespace mpt { inline namespace MPT_INLINE_NS { namespace tests { namespace crc { #if MPT_COMPILER_CLANG #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wglobal-constructors" #endif MPT_TEST_GROUP_INLINE("mpt/crc") #if MPT_COMPILER_CLANG #pragma clang diagnostic pop #endif { MPT_TEST_EXPECT_EQUAL(mpt::crc32(std::string("123456789")), 0xCBF43926u); MPT_TEST_EXPECT_EQUAL(mpt::crc32_ogg(std::string("123456789")), 0x89a1897fu); } } // namespace crc } // namespace tests } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_BASE_TESTS_CRC_HPP libopenmpt-0.6.1+release.autotools/src/mpt/crc/crc.hpp0000644000175000017500000001141214044173026017672 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_CRC_CRC_HPP #define MPT_CRC_CRC_HPP #include "mpt/base/array.hpp" #include "mpt/base/detect.hpp" #include "mpt/base/namespace.hpp" #include "mpt/base/integer.hpp" #include "mpt/base/memory.hpp" #include #include namespace mpt { inline namespace MPT_INLINE_NS { template class crc { public: using self_type = crc; using value_type = T; using byte_type = uint8; static constexpr std::size_t size_bytes = sizeof(value_type); static constexpr std::size_t size_bits = sizeof(value_type) * 8; static constexpr value_type top_bit = static_cast(1) << ((sizeof(value_type) * 8) - 1); private: template static constexpr Tint reverse(Tint value) noexcept { const std::size_t bits = sizeof(Tint) * 8; Tint result = 0; for (std::size_t i = 0; i < bits; ++i) { result <<= 1; result |= static_cast(value & 0x1); value >>= 1; } return result; } static constexpr value_type calculate_table_entry(byte_type pos) noexcept { value_type value = 0; value = (static_cast(reverseData ? reverse(pos) : pos) << (size_bits - 8)); for (std::size_t bit = 0; bit < 8; ++bit) { if (value & top_bit) { value = (value << 1) ^ polynomial; } else { value = (value << 1); } } value = (reverseData ? reverse(value) : value); return value; } private: static constexpr std::array calculate_table() noexcept { std::array t = mpt::init_array(value_type{}); for (std::size_t i = 0; i < 256; ++i) { t[i] = calculate_table_entry(static_cast(i)); } return t; } static constexpr std::array table = calculate_table(); private: constexpr value_type read_table(byte_type pos) const noexcept { return table[pos]; } private: value_type value; public: constexpr crc() noexcept : value(initial) { return; } constexpr void processByte(byte_type byte) noexcept { if constexpr (reverseData) { value = (value >> 8) ^ read_table(static_cast((value & 0xff) ^ byte)); } else { value = (value << 8) ^ read_table(static_cast(((value >> (size_bits - 8)) & 0xff) ^ byte)); } } constexpr value_type result() const noexcept { return (value ^ resultXOR); } public: constexpr operator value_type() const noexcept { return result(); } inline crc & process(char c) noexcept { processByte(mpt::byte_cast(c)); return *this; } inline crc & process(signed char c) noexcept { processByte(static_cast(c)); return *this; } inline crc & process(unsigned char c) noexcept { processByte(mpt::byte_cast(c)); return *this; } inline crc & process(std::byte c) noexcept { processByte(mpt::byte_cast(c)); return *this; } template inline crc & process(InputIt beg, InputIt end) { for (InputIt it = beg; it != end; ++it) { static_assert(sizeof(*it) == 1, "1 byte type required"); process(*it); } return *this; } template inline crc & process(const Container & data) { operator()(data.begin(), data.end()); return *this; } inline crc & operator()(char c) noexcept { processByte(mpt::byte_cast(c)); return *this; } inline crc & operator()(signed char c) noexcept { processByte(static_cast(c)); return *this; } inline crc & operator()(unsigned char c) noexcept { processByte(mpt::byte_cast(c)); return *this; } inline crc & operator()(std::byte c) noexcept { processByte(mpt::byte_cast(c)); return *this; } template crc & operator()(InputIt beg, InputIt end) { for (InputIt it = beg; it != end; ++it) { static_assert(sizeof(*it) == 1, "1 byte type required"); operator()(*it); } return *this; } template inline crc & operator()(const Container & data) { operator()(data.begin(), data.end()); return *this; } template crc(InputIt beg, InputIt end) : value(initial) { for (InputIt it = beg; it != end; ++it) { static_assert(sizeof(*it) == 1, "1 byte type required"); process(*it); } } template inline crc(const Container & data) : value(initial) { process(data.begin(), data.end()); } }; using crc16 = crc; using crc32 = crc; using crc32_ogg = crc; using crc32c = crc; using crc64_jones = crc; } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_CRC_CRC_HPP libopenmpt-0.6.1+release.autotools/src/mpt/detect/0000755000175000017500000000000014175541574017210 500000000000000libopenmpt-0.6.1+release.autotools/src/mpt/detect/dl.hpp0000644000175000017500000000101314047510526020222 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_DETECT_DL_HPP #define MPT_DETECT_DL_HPP #include "mpt/base/compiletime_warning.hpp" #if defined(MPT_WITH_DL) #if !defined(CPPCHECK) #if !__has_include() #error "MPT_WITH_DL defined but not found." #endif #endif #define MPT_DETECTED_DL 1 #else #if defined(CPPCHECK) #define MPT_DETECTED_DL 1 #else #if __has_include() #define MPT_DETECTED_DL 1 #else #define MPT_DETECTED_DL 0 #endif #endif #endif #endif // MPT_DETECT_DL_HPP libopenmpt-0.6.1+release.autotools/src/mpt/detect/ltdl.hpp0000644000175000017500000000103214047510526020563 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_DETECT_LTDL_HPP #define MPT_DETECT_LTDL_HPP #include "mpt/base/compiletime_warning.hpp" #if defined(MPT_WITH_LTDL) #if !defined(CPPCHECK) #if !__has_include() #error "MPT_WITH_LTDL defined but not found." #endif #endif #define MPT_DETECTED_LTDL 1 #else #if defined(CPPCHECK) #define MPT_DETECTED_LTDL 1 #else #if __has_include() #define MPT_DETECTED_LTDL 1 #else #define MPT_DETECTED_LTDL 0 #endif #endif #endif #endif // MPT_DETECT_LTDL_HPP libopenmpt-0.6.1+release.autotools/src/mpt/detect/mfc.hpp0000644000175000017500000000101714044247307020375 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_DETECT_MFC_HPP #define MPT_DETECT_MFC_HPP #include "mpt/base/compiletime_warning.hpp" #if defined(MPT_WITH_MFC) #if !defined(CPPCHECK) #if !__has_include() #error "MPT_WITH_MFC defined but not found." #endif #endif #if !MPT_COMPILER_GENERIC && !MPT_COMPILER_MSVC && !MPT_COMPILER_CLANG MPT_WARNING("Using MFC with unsupported compiler.") #endif #define MPT_DETECTED_MFC 1 #else #define MPT_DETECTED_MFC 0 #endif #endif // MPT_DETECT_MFC_HPP libopenmpt-0.6.1+release.autotools/src/mpt/detect/nlohmann_json.hpp0000644000175000017500000000113714044247307022476 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_DETECT_NLOHMANN_JSON_HPP #define MPT_DETECT_NLOHMANN_JSON_HPP #if defined(MPT_WITH_NLOHMANN_JSON) #if !defined(CPPCHECK) #if !__has_include() #error "MPT_WITH_NLOHMANN_JSON defined but not found." #endif #endif #define MPT_DETECTED_NLOHMANN_JSON 1 #else #if defined(CPPCHECK) #define MPT_DETECTED_NLOHMANN_JSON 1 #else #if __has_include() #define MPT_DETECTED_NLOHMANN_JSON 1 #else #define MPT_DETECTED_NLOHMANN_JSON 0 #endif #endif #endif #endif // MPT_DETECT_NLOHMANN_JSON_HPP libopenmpt-0.6.1+release.autotools/src/mpt/endian/0000755000175000017500000000000014175541574017176 500000000000000libopenmpt-0.6.1+release.autotools/src/mpt/endian/tests/0000755000175000017500000000000014175541575020341 500000000000000libopenmpt-0.6.1+release.autotools/src/mpt/endian/tests/tests_endian_floatingpoint.hpp0000644000175000017500000001011214044173026026365 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_ENDIAN_TESTS_ENDIAN_FLOATINGPOINT_HPP #define MPT_ENDIAN_TESTS_ENDIAN_FLOATINGPOINT_HPP #include "mpt/base/detect_compiler.hpp" #include "mpt/base/memory.hpp" #include "mpt/base/namespace.hpp" #include "mpt/endian/floatingpoint.hpp" #include "mpt/test/test.hpp" #include "mpt/test/test_macros.hpp" namespace mpt { inline namespace MPT_INLINE_NS { namespace tests { namespace endian { namespace floatingpoint { #if MPT_COMPILER_CLANG #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wglobal-constructors" #endif MPT_TEST_GROUP_INLINE("mpt/endian/floatingpoint") #if MPT_COMPILER_CLANG #pragma clang diagnostic pop #endif { MPT_TEST_EXPECT_EQUAL(mpt::EncodeIEEE754binary32(1.0f), 0x3f800000u); MPT_TEST_EXPECT_EQUAL(mpt::EncodeIEEE754binary32(-1.0f), 0xbf800000u); MPT_TEST_EXPECT_EQUAL(mpt::DecodeIEEE754binary32(0x00000000u), 0.0f); MPT_TEST_EXPECT_EQUAL(mpt::DecodeIEEE754binary32(0x41840000u), 16.5f); MPT_TEST_EXPECT_EQUAL(mpt::DecodeIEEE754binary32(0x3faa0000u), 1.328125f); MPT_TEST_EXPECT_EQUAL(mpt::DecodeIEEE754binary32(0xbfaa0000u), -1.328125f); MPT_TEST_EXPECT_EQUAL(mpt::DecodeIEEE754binary32(0x3f800000u), 1.0f); MPT_TEST_EXPECT_EQUAL(mpt::DecodeIEEE754binary32(0x00000000u), 0.0f); MPT_TEST_EXPECT_EQUAL(mpt::DecodeIEEE754binary32(0xbf800000u), -1.0f); MPT_TEST_EXPECT_EQUAL(mpt::DecodeIEEE754binary32(0x3f800000u), 1.0f); MPT_TEST_EXPECT_EQUAL(IEEE754binary32LE(1.0f).GetInt32(), 0x3f800000u); MPT_TEST_EXPECT_EQUAL(IEEE754binary32BE(1.0f).GetInt32(), 0x3f800000u); MPT_TEST_EXPECT_EQUAL(IEEE754binary32LE(mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x80), mpt::as_byte(0x3f)), 1.0f); MPT_TEST_EXPECT_EQUAL(IEEE754binary32BE(mpt::as_byte(0x3f), mpt::as_byte(0x80), mpt::as_byte(0x00), mpt::as_byte(0x00)), 1.0f); MPT_TEST_EXPECT_EQUAL(IEEE754binary32LE(1.0f), IEEE754binary32LE(mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x80), mpt::as_byte(0x3f))); MPT_TEST_EXPECT_EQUAL(IEEE754binary32BE(1.0f), IEEE754binary32BE(mpt::as_byte(0x3f), mpt::as_byte(0x80), mpt::as_byte(0x00), mpt::as_byte(0x00))); MPT_TEST_EXPECT_EQUAL(mpt::EncodeIEEE754binary64(1.0), 0x3ff0000000000000ull); MPT_TEST_EXPECT_EQUAL(mpt::EncodeIEEE754binary64(-1.0), 0xbff0000000000000ull); MPT_TEST_EXPECT_EQUAL(mpt::DecodeIEEE754binary64(0x0000000000000000ull), 0.0); MPT_TEST_EXPECT_EQUAL(mpt::DecodeIEEE754binary64(0x4030800000000000ull), 16.5); MPT_TEST_EXPECT_EQUAL(mpt::DecodeIEEE754binary64(0x3FF5400000000000ull), 1.328125); MPT_TEST_EXPECT_EQUAL(mpt::DecodeIEEE754binary64(0xBFF5400000000000ull), -1.328125); MPT_TEST_EXPECT_EQUAL(mpt::DecodeIEEE754binary64(0x3ff0000000000000ull), 1.0); MPT_TEST_EXPECT_EQUAL(mpt::DecodeIEEE754binary64(0x0000000000000000ull), 0.0); MPT_TEST_EXPECT_EQUAL(mpt::DecodeIEEE754binary64(0xbff0000000000000ull), -1.0); MPT_TEST_EXPECT_EQUAL(mpt::DecodeIEEE754binary64(0x3ff0000000000000ull), 1.0); MPT_TEST_EXPECT_EQUAL(IEEE754binary64LE(1.0).GetInt64(), 0x3ff0000000000000ull); MPT_TEST_EXPECT_EQUAL(IEEE754binary64BE(1.0).GetInt64(), 0x3ff0000000000000ull); MPT_TEST_EXPECT_EQUAL(IEEE754binary64LE(mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0xf0), mpt::as_byte(0x3f)), 1.0); MPT_TEST_EXPECT_EQUAL(IEEE754binary64BE(mpt::as_byte(0x3f), mpt::as_byte(0xf0), mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x00)), 1.0); MPT_TEST_EXPECT_EQUAL(IEEE754binary64LE(1.0), IEEE754binary64LE(mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0xf0), mpt::as_byte(0x3f))); MPT_TEST_EXPECT_EQUAL(IEEE754binary64BE(1.0), IEEE754binary64BE(mpt::as_byte(0x3f), mpt::as_byte(0xf0), mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x00))); } } // namespace floatingpoint } // namespace endian } // namespace tests } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_ENDIAN_TESTS_ENDIAN_FLOATINGPOINT_HPP libopenmpt-0.6.1+release.autotools/src/mpt/endian/tests/tests_endian_integer.hpp0000644000175000017500000001101014044173026025143 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_ENDIAN_TESTS_ENDIAN_INTEGER_HPP #define MPT_ENDIAN_TESTS_ENDIAN_INTEGER_HPP #include "mpt/base/detect_compiler.hpp" #include "mpt/base/integer.hpp" #include "mpt/base/macros.hpp" #include "mpt/base/namespace.hpp" #include "mpt/endian/integer.hpp" #include "mpt/test/test.hpp" #include "mpt/test/test_macros.hpp" #include #include namespace mpt { inline namespace MPT_INLINE_NS { namespace tests { namespace endian { namespace integer { #if MPT_COMPILER_CLANG #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wglobal-constructors" #endif MPT_TEST_GROUP_INLINE("mpt/endian/integer") #if MPT_COMPILER_CLANG #pragma clang diagnostic pop #endif { static_assert(std::numeric_limits::min() == std::numeric_limits::min()); static_assert(std::numeric_limits::min() == std::numeric_limits::min()); static_assert(std::numeric_limits::min() == std::numeric_limits::min()); static_assert(std::numeric_limits::min() == std::numeric_limits::min()); static_assert(std::numeric_limits::min() == std::numeric_limits::min()); static_assert(std::numeric_limits::min() == std::numeric_limits::min()); static_assert(std::numeric_limits::min() == std::numeric_limits::min()); static_assert(std::numeric_limits::min() == std::numeric_limits::min()); static_assert(std::numeric_limits::max() == std::numeric_limits::max()); static_assert(std::numeric_limits::max() == std::numeric_limits::max()); static_assert(std::numeric_limits::max() == std::numeric_limits::max()); static_assert(std::numeric_limits::max() == std::numeric_limits::max()); static_assert(std::numeric_limits::max() == std::numeric_limits::max()); static_assert(std::numeric_limits::max() == std::numeric_limits::max()); static_assert(std::numeric_limits::max() == std::numeric_limits::max()); static_assert(std::numeric_limits::max() == std::numeric_limits::max()); struct test_endian_constexpr { static MPT_CONSTEXPR20_FUN int32le test(uint32 x) { int32le foo{}; foo = x; return foo; } }; MPT_CONSTEXPR20_VAR int32le foo = test_endian_constexpr::test(23); static_cast(foo); MPT_TEST_EXPECT_EQUAL(mpt::SwapBytesImpl(uint8(0x12)), 0x12); MPT_TEST_EXPECT_EQUAL(mpt::SwapBytesImpl(uint16(0x1234)), 0x3412); MPT_TEST_EXPECT_EQUAL(mpt::SwapBytesImpl(uint32(0x12345678u)), 0x78563412u); MPT_TEST_EXPECT_EQUAL(mpt::SwapBytesImpl(uint64(0x123456789abcdef0ull)), 0xf0debc9a78563412ull); MPT_TEST_EXPECT_EQUAL(mpt::SwapBytesImpl(int8(std::numeric_limits::min())), std::numeric_limits::min()); MPT_TEST_EXPECT_EQUAL(mpt::SwapBytesImpl(int16(std::numeric_limits::min())), int16(0x80)); MPT_TEST_EXPECT_EQUAL(mpt::SwapBytesImpl(int32(std::numeric_limits::min())), int32(0x80)); MPT_TEST_EXPECT_EQUAL(mpt::SwapBytesImpl(int64(std::numeric_limits::min())), int64(0x80)); // Packed integers with defined endianness { int8le le8; le8.set(-128); int8be be8; be8.set(-128); MPT_TEST_EXPECT_EQUAL(le8, -128); MPT_TEST_EXPECT_EQUAL(be8, -128); MPT_TEST_EXPECT_EQUAL(std::memcmp(&le8, "\x80", 1), 0); MPT_TEST_EXPECT_EQUAL(std::memcmp(&be8, "\x80", 1), 0); int16le le16; le16.set(0x1234); int16be be16; be16.set(0x1234); MPT_TEST_EXPECT_EQUAL(le16, 0x1234); MPT_TEST_EXPECT_EQUAL(be16, 0x1234); MPT_TEST_EXPECT_EQUAL(std::memcmp(&le16, "\x34\x12", 2), 0); MPT_TEST_EXPECT_EQUAL(std::memcmp(&be16, "\x12\x34", 2), 0); uint32le le32; le32.set(0xFFEEDDCCu); uint32be be32; be32.set(0xFFEEDDCCu); MPT_TEST_EXPECT_EQUAL(le32, 0xFFEEDDCCu); MPT_TEST_EXPECT_EQUAL(be32, 0xFFEEDDCCu); MPT_TEST_EXPECT_EQUAL(std::memcmp(&le32, "\xCC\xDD\xEE\xFF", 4), 0); MPT_TEST_EXPECT_EQUAL(std::memcmp(&be32, "\xFF\xEE\xDD\xCC", 4), 0); uint64le le64; le64.set(0xDEADC0DE15C0FFEEull); uint64be be64; be64.set(0xDEADC0DE15C0FFEEull); MPT_TEST_EXPECT_EQUAL(le64, 0xDEADC0DE15C0FFEEull); MPT_TEST_EXPECT_EQUAL(be64, 0xDEADC0DE15C0FFEEull); MPT_TEST_EXPECT_EQUAL(std::memcmp(&le64, "\xEE\xFF\xC0\x15\xDE\xC0\xAD\xDE", 8), 0); MPT_TEST_EXPECT_EQUAL(std::memcmp(&be64, "\xDE\xAD\xC0\xDE\x15\xC0\xFF\xEE", 8), 0); } } } // namespace integer } // namespace endian } // namespace tests } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_ENDIAN_TESTS_ENDIAN_INTEGER_HPP libopenmpt-0.6.1+release.autotools/src/mpt/endian/floatingpoint.hpp0000644000175000017500000003762114044765055022512 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_ENDIAN_FLOATINGPOINT_HPP #define MPT_ENDIAN_FLOATINGPOINT_HPP #include "mpt/base/bit.hpp" #include "mpt/base/floatingpoint.hpp" #include "mpt/base/macros.hpp" #include "mpt/base/memory.hpp" #include "mpt/base/namespace.hpp" #include #include #include #include #include namespace mpt { inline namespace MPT_INLINE_NS { // 1.0f --> 0x3f800000u MPT_FORCEINLINE uint32 EncodeIEEE754binary32(float32 f) { if constexpr (mpt::float_traits::is_ieee754_binary32ne) { return mpt::bit_cast(f); } else { int e = 0; float m = std::frexp(f, &e); if (e == 0 && std::fabs(m) == 0.0f) { uint32 expo = 0u; uint32 sign = std::signbit(m) ? 0x01u : 0x00u; uint32 mant = 0u; uint32 i = 0u; i |= (mant << 0) & 0x007fffffu; i |= (expo << 23) & 0x7f800000u; i |= (sign << 31) & 0x80000000u; return i; } else { uint32 expo = e + 127 - 1; uint32 sign = std::signbit(m) ? 0x01u : 0x00u; uint32 mant = static_cast(std::fabs(std::ldexp(m, 24))); uint32 i = 0u; i |= (mant << 0) & 0x007fffffu; i |= (expo << 23) & 0x7f800000u; i |= (sign << 31) & 0x80000000u; return i; } } } MPT_FORCEINLINE uint64 EncodeIEEE754binary64(float64 f) { if constexpr (mpt::float_traits::is_ieee754_binary64ne) { return mpt::bit_cast(f); } else { int e = 0; double m = std::frexp(f, &e); if (e == 0 && std::fabs(m) == 0.0) { uint64 expo = 0u; uint64 sign = std::signbit(m) ? 0x01u : 0x00u; uint64 mant = 0u; uint64 i = 0u; i |= (mant << 0) & 0x000fffffffffffffull; i |= (expo << 52) & 0x7ff0000000000000ull; i |= (sign << 63) & 0x8000000000000000ull; return i; } else { uint64 expo = static_cast(e) + 1023 - 1; uint64 sign = std::signbit(m) ? 0x01u : 0x00u; uint64 mant = static_cast(std::fabs(std::ldexp(m, 53))); uint64 i = 0u; i |= (mant << 0) & 0x000fffffffffffffull; i |= (expo << 52) & 0x7ff0000000000000ull; i |= (sign << 63) & 0x8000000000000000ull; return i; } } } // 0x3f800000u --> 1.0f MPT_FORCEINLINE float32 DecodeIEEE754binary32(uint32 i) { if constexpr (mpt::float_traits::is_ieee754_binary32ne) { return mpt::bit_cast(i); } else { uint32 mant = (i & 0x007fffffu) >> 0; uint32 expo = (i & 0x7f800000u) >> 23; uint32 sign = (i & 0x80000000u) >> 31; if (expo == 0) { float m = sign ? -static_cast(mant) : static_cast(mant); int e = static_cast(expo) - 127 + 1 - 24; float f = std::ldexp(m, e); return static_cast(f); } else { mant |= 0x00800000u; float m = sign ? -static_cast(mant) : static_cast(mant); int e = static_cast(expo) - 127 + 1 - 24; float f = std::ldexp(m, e); return static_cast(f); } } } MPT_FORCEINLINE float64 DecodeIEEE754binary64(uint64 i) { if constexpr (mpt::float_traits::is_ieee754_binary64ne) { return mpt::bit_cast(i); } else { uint64 mant = (i & 0x000fffffffffffffull) >> 0; uint64 expo = (i & 0x7ff0000000000000ull) >> 52; uint64 sign = (i & 0x8000000000000000ull) >> 63; if (expo == 0) { double m = sign ? -static_cast(mant) : static_cast(mant); int e = static_cast(expo) - 1023 + 1 - 53; double f = std::ldexp(m, e); return static_cast(f); } else { mant |= 0x0010000000000000ull; double m = sign ? -static_cast(mant) : static_cast(mant); int e = static_cast(expo) - 1023 + 1 - 53; double f = std::ldexp(m, e); return static_cast(f); } } } // template parameters are byte indices corresponding to the individual bytes of iee754 in memory template struct IEEE754binary32Emulated { public: using self_t = IEEE754binary32Emulated; std::byte bytes[4]; public: MPT_FORCEINLINE std::byte GetByte(std::size_t i) const { return bytes[i]; } IEEE754binary32Emulated() = default; MPT_FORCEINLINE explicit IEEE754binary32Emulated(float32 f) { SetInt32(EncodeIEEE754binary32(f)); } // b0...b3 are in memory order, i.e. depend on the endianness of this type // little endian: (0x00,0x00,0x80,0x3f) // big endian: (0x3f,0x80,0x00,0x00) MPT_FORCEINLINE explicit IEEE754binary32Emulated(std::byte b0, std::byte b1, std::byte b2, std::byte b3) { bytes[0] = b0; bytes[1] = b1; bytes[2] = b2; bytes[3] = b3; } MPT_FORCEINLINE operator float32() const { return DecodeIEEE754binary32(GetInt32()); } MPT_FORCEINLINE self_t & SetInt32(uint32 i) { bytes[hihi] = static_cast(i >> 24); bytes[hilo] = static_cast(i >> 16); bytes[lohi] = static_cast(i >> 8); bytes[lolo] = static_cast(i >> 0); return *this; } MPT_FORCEINLINE uint32 GetInt32() const { return 0u | (static_cast(bytes[hihi]) << 24) | (static_cast(bytes[hilo]) << 16) | (static_cast(bytes[lohi]) << 8) | (static_cast(bytes[lolo]) << 0); } MPT_FORCEINLINE bool operator==(const self_t & cmp) const { return true && bytes[0] == cmp.bytes[0] && bytes[1] == cmp.bytes[1] && bytes[2] == cmp.bytes[2] && bytes[3] == cmp.bytes[3]; } MPT_FORCEINLINE bool operator!=(const self_t & cmp) const { return !(*this == cmp); } }; template struct IEEE754binary64Emulated { public: using self_t = IEEE754binary64Emulated; std::byte bytes[8]; public: MPT_FORCEINLINE std::byte GetByte(std::size_t i) const { return bytes[i]; } IEEE754binary64Emulated() = default; MPT_FORCEINLINE explicit IEEE754binary64Emulated(float64 f) { SetInt64(EncodeIEEE754binary64(f)); } MPT_FORCEINLINE explicit IEEE754binary64Emulated(std::byte b0, std::byte b1, std::byte b2, std::byte b3, std::byte b4, std::byte b5, std::byte b6, std::byte b7) { bytes[0] = b0; bytes[1] = b1; bytes[2] = b2; bytes[3] = b3; bytes[4] = b4; bytes[5] = b5; bytes[6] = b6; bytes[7] = b7; } MPT_FORCEINLINE operator float64() const { return DecodeIEEE754binary64(GetInt64()); } MPT_FORCEINLINE self_t & SetInt64(uint64 i) { bytes[hihihi] = static_cast(i >> 56); bytes[hihilo] = static_cast(i >> 48); bytes[hilohi] = static_cast(i >> 40); bytes[hilolo] = static_cast(i >> 32); bytes[lohihi] = static_cast(i >> 24); bytes[lohilo] = static_cast(i >> 16); bytes[lolohi] = static_cast(i >> 8); bytes[lololo] = static_cast(i >> 0); return *this; } MPT_FORCEINLINE uint64 GetInt64() const { return 0u | (static_cast(bytes[hihihi]) << 56) | (static_cast(bytes[hihilo]) << 48) | (static_cast(bytes[hilohi]) << 40) | (static_cast(bytes[hilolo]) << 32) | (static_cast(bytes[lohihi]) << 24) | (static_cast(bytes[lohilo]) << 16) | (static_cast(bytes[lolohi]) << 8) | (static_cast(bytes[lololo]) << 0); } MPT_FORCEINLINE bool operator==(const self_t & cmp) const { return true && bytes[0] == cmp.bytes[0] && bytes[1] == cmp.bytes[1] && bytes[2] == cmp.bytes[2] && bytes[3] == cmp.bytes[3] && bytes[4] == cmp.bytes[4] && bytes[5] == cmp.bytes[5] && bytes[6] == cmp.bytes[6] && bytes[7] == cmp.bytes[7]; } MPT_FORCEINLINE bool operator!=(const self_t & cmp) const { return !(*this == cmp); } }; using IEEE754binary32EmulatedBE = IEEE754binary32Emulated<0, 1, 2, 3>; using IEEE754binary32EmulatedLE = IEEE754binary32Emulated<3, 2, 1, 0>; using IEEE754binary64EmulatedBE = IEEE754binary64Emulated<0, 1, 2, 3, 4, 5, 6, 7>; using IEEE754binary64EmulatedLE = IEEE754binary64Emulated<7, 6, 5, 4, 3, 2, 1, 0>; constexpr bool declare_binary_safe(const IEEE754binary32EmulatedBE &) { return true; } constexpr bool declare_binary_safe(const IEEE754binary32EmulatedLE &) { return true; } constexpr bool declare_binary_safe(const IEEE754binary64EmulatedBE &) { return true; } constexpr bool declare_binary_safe(const IEEE754binary64EmulatedLE &) { return true; } static_assert(mpt::check_binary_size(4)); static_assert(mpt::check_binary_size(4)); static_assert(mpt::check_binary_size(8)); static_assert(mpt::check_binary_size(8)); template struct IEEE754binary32Native { public: float32 value; public: MPT_FORCEINLINE std::byte GetByte(std::size_t i) const { static_assert(endian == mpt::endian::little || endian == mpt::endian::big); if constexpr (endian == mpt::endian::little) { return static_cast(EncodeIEEE754binary32(value) >> (i * 8)); } if constexpr (endian == mpt::endian::big) { return static_cast(EncodeIEEE754binary32(value) >> ((4 - 1 - i) * 8)); } } IEEE754binary32Native() = default; MPT_FORCEINLINE explicit IEEE754binary32Native(float32 f) { value = f; } // b0...b3 are in memory order, i.e. depend on the endianness of this type // little endian: (0x00,0x00,0x80,0x3f) // big endian: (0x3f,0x80,0x00,0x00) MPT_FORCEINLINE explicit IEEE754binary32Native(std::byte b0, std::byte b1, std::byte b2, std::byte b3) { static_assert(endian == mpt::endian::little || endian == mpt::endian::big); if constexpr (endian == mpt::endian::little) { value = DecodeIEEE754binary32(0u | (static_cast(b0) << 0) | (static_cast(b1) << 8) | (static_cast(b2) << 16) | (static_cast(b3) << 24)); } if constexpr (endian == mpt::endian::big) { value = DecodeIEEE754binary32(0u | (static_cast(b0) << 24) | (static_cast(b1) << 16) | (static_cast(b2) << 8) | (static_cast(b3) << 0)); } } MPT_FORCEINLINE operator float32() const { return value; } MPT_FORCEINLINE IEEE754binary32Native & SetInt32(uint32 i) { value = DecodeIEEE754binary32(i); return *this; } MPT_FORCEINLINE uint32 GetInt32() const { return EncodeIEEE754binary32(value); } MPT_FORCEINLINE bool operator==(const IEEE754binary32Native & cmp) const { return value == cmp.value; } MPT_FORCEINLINE bool operator!=(const IEEE754binary32Native & cmp) const { return value != cmp.value; } }; template struct IEEE754binary64Native { public: float64 value; public: MPT_FORCEINLINE std::byte GetByte(std::size_t i) const { static_assert(endian == mpt::endian::little || endian == mpt::endian::big); if constexpr (endian == mpt::endian::little) { return mpt::byte_cast(static_cast(EncodeIEEE754binary64(value) >> (i * 8))); } if constexpr (endian == mpt::endian::big) { return mpt::byte_cast(static_cast(EncodeIEEE754binary64(value) >> ((8 - 1 - i) * 8))); } } IEEE754binary64Native() = default; MPT_FORCEINLINE explicit IEEE754binary64Native(float64 f) { value = f; } MPT_FORCEINLINE explicit IEEE754binary64Native(std::byte b0, std::byte b1, std::byte b2, std::byte b3, std::byte b4, std::byte b5, std::byte b6, std::byte b7) { static_assert(endian == mpt::endian::little || endian == mpt::endian::big); if constexpr (endian == mpt::endian::little) { value = DecodeIEEE754binary64(0ull | (static_cast(b0) << 0) | (static_cast(b1) << 8) | (static_cast(b2) << 16) | (static_cast(b3) << 24) | (static_cast(b4) << 32) | (static_cast(b5) << 40) | (static_cast(b6) << 48) | (static_cast(b7) << 56)); } if constexpr (endian == mpt::endian::big) { value = DecodeIEEE754binary64(0ull | (static_cast(b0) << 56) | (static_cast(b1) << 48) | (static_cast(b2) << 40) | (static_cast(b3) << 32) | (static_cast(b4) << 24) | (static_cast(b5) << 16) | (static_cast(b6) << 8) | (static_cast(b7) << 0)); } } MPT_FORCEINLINE operator float64() const { return value; } MPT_FORCEINLINE IEEE754binary64Native & SetInt64(uint64 i) { value = DecodeIEEE754binary64(i); return *this; } MPT_FORCEINLINE uint64 GetInt64() const { return EncodeIEEE754binary64(value); } MPT_FORCEINLINE bool operator==(const IEEE754binary64Native & cmp) const { return value == cmp.value; } MPT_FORCEINLINE bool operator!=(const IEEE754binary64Native & cmp) const { return value != cmp.value; } }; static_assert((sizeof(IEEE754binary32Native<>) == 4)); static_assert((sizeof(IEEE754binary64Native<>) == 8)); constexpr bool declare_binary_safe(const IEEE754binary32Native<> &) noexcept { return true; } constexpr bool declare_binary_safe(const IEEE754binary64Native<> &) noexcept { return true; } template struct IEEE754binary_types { using IEEE754binary32LE = IEEE754binary32EmulatedLE; using IEEE754binary32BE = IEEE754binary32EmulatedBE; using IEEE754binary64LE = IEEE754binary64EmulatedLE; using IEEE754binary64BE = IEEE754binary64EmulatedBE; }; template <> struct IEEE754binary_types { using IEEE754binary32LE = IEEE754binary32Native<>; using IEEE754binary32BE = IEEE754binary32EmulatedBE; using IEEE754binary64LE = IEEE754binary64Native<>; using IEEE754binary64BE = IEEE754binary64EmulatedBE; }; template <> struct IEEE754binary_types { using IEEE754binary32LE = IEEE754binary32EmulatedLE; using IEEE754binary32BE = IEEE754binary32Native<>; using IEEE754binary64LE = IEEE754binary64EmulatedLE; using IEEE754binary64BE = IEEE754binary64Native<>; }; using IEEE754binary32LE = IEEE754binary_types::is_ieee754_binary32ne, mpt::endian::native>::IEEE754binary32LE; using IEEE754binary32BE = IEEE754binary_types::is_ieee754_binary32ne, mpt::endian::native>::IEEE754binary32BE; using IEEE754binary64LE = IEEE754binary_types::is_ieee754_binary64ne, mpt::endian::native>::IEEE754binary64LE; using IEEE754binary64BE = IEEE754binary_types::is_ieee754_binary64ne, mpt::endian::native>::IEEE754binary64BE; static_assert(sizeof(IEEE754binary32LE) == 4); static_assert(sizeof(IEEE754binary32BE) == 4); static_assert(sizeof(IEEE754binary64LE) == 8); static_assert(sizeof(IEEE754binary64BE) == 8); // unaligned using float32le = IEEE754binary32EmulatedLE; using float32be = IEEE754binary32EmulatedBE; using float64le = IEEE754binary64EmulatedLE; using float64be = IEEE754binary64EmulatedBE; static_assert(sizeof(float32le) == 4); static_assert(sizeof(float32be) == 4); static_assert(sizeof(float64le) == 8); static_assert(sizeof(float64be) == 8); // potentially aligned using float32le_fast = IEEE754binary32LE; using float32be_fast = IEEE754binary32BE; using float64le_fast = IEEE754binary64LE; using float64be_fast = IEEE754binary64BE; static_assert(sizeof(float32le_fast) == 4); static_assert(sizeof(float32be_fast) == 4); static_assert(sizeof(float64le_fast) == 8); static_assert(sizeof(float64be_fast) == 8); template struct make_float_be { }; template <> struct make_float_be { using type = IEEE754binary64BE; }; template <> struct make_float_be { using type = IEEE754binary32BE; }; template struct make_float_le { }; template <> struct make_float_le { using type = IEEE754binary64LE; }; template <> struct make_float_le { using type = IEEE754binary32LE; }; template struct make_float_endian { }; template struct make_float_endian { using type = typename make_float_le::type>::type; }; template struct make_float_endian { using type = typename make_float_be::type>::type; }; } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_ENDIAN_FLOATINGPOINT_HPP libopenmpt-0.6.1+release.autotools/src/mpt/endian/int24.hpp0000644000175000017500000000760114051474621020562 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_ENDIAN_INT24_HPP #define MPT_ENDIAN_INT24_HPP #include "mpt/base/bit.hpp" #include "mpt/base/macros.hpp" #include "mpt/base/memory.hpp" #include "mpt/base/namespace.hpp" #include #include #include #include namespace mpt { inline namespace MPT_INLINE_NS { struct uint24 { std::array bytes; uint24() = default; template ::value, bool>::type = true> explicit uint24(T other) noexcept { using Tunsigned = typename std::make_unsigned::type; MPT_MAYBE_CONSTANT_IF(mpt::endian_is_big()) { bytes[0] = mpt::byte_cast(static_cast((static_cast(other) >> 16) & 0xff)); bytes[1] = mpt::byte_cast(static_cast((static_cast(other) >> 8) & 0xff)); bytes[2] = mpt::byte_cast(static_cast((static_cast(other) >> 0) & 0xff)); } else { bytes[0] = mpt::byte_cast(static_cast((static_cast(other) >> 0) & 0xff)); bytes[1] = mpt::byte_cast(static_cast((static_cast(other) >> 8) & 0xff)); bytes[2] = mpt::byte_cast(static_cast((static_cast(other) >> 16) & 0xff)); } } operator int() const noexcept { MPT_MAYBE_CONSTANT_IF(mpt::endian_is_big()) { return (mpt::byte_cast(bytes[0]) * 65536) + (mpt::byte_cast(bytes[1]) * 256) + mpt::byte_cast(bytes[2]); } else { return (mpt::byte_cast(bytes[2]) * 65536) + (mpt::byte_cast(bytes[1]) * 256) + mpt::byte_cast(bytes[0]); } } }; static_assert(sizeof(uint24) == 3); struct int24 { std::array bytes; int24() = default; template ::value, bool>::type = true> explicit int24(T other) noexcept { using Tunsigned = typename std::make_unsigned::type; MPT_MAYBE_CONSTANT_IF(mpt::endian_is_big()) { bytes[0] = mpt::byte_cast(static_cast((static_cast(other) >> 16) & 0xff)); bytes[1] = mpt::byte_cast(static_cast((static_cast(other) >> 8) & 0xff)); bytes[2] = mpt::byte_cast(static_cast((static_cast(other) >> 0) & 0xff)); } else { bytes[0] = mpt::byte_cast(static_cast((static_cast(other) >> 0) & 0xff)); bytes[1] = mpt::byte_cast(static_cast((static_cast(other) >> 8) & 0xff)); bytes[2] = mpt::byte_cast(static_cast((static_cast(other) >> 16) & 0xff)); } } operator int() const noexcept { MPT_MAYBE_CONSTANT_IF(mpt::endian_is_big()) { return (static_cast(mpt::byte_cast(bytes[0])) * 65536) + (mpt::byte_cast(bytes[1]) * 256) + mpt::byte_cast(bytes[2]); } else { return (static_cast(mpt::byte_cast(bytes[2])) * 65536) + (mpt::byte_cast(bytes[1]) * 256) + mpt::byte_cast(bytes[0]); } } }; static_assert(sizeof(int24) == 3); } // namespace MPT_INLINE_NS } // namespace mpt #if !defined(CPPCHECK) // work-around crash in cppcheck 2.4.1 namespace std { template <> class numeric_limits : public std::numeric_limits { public: static constexpr mpt::uint32 min() noexcept { return 0; } static constexpr mpt::uint32 lowest() noexcept { return 0; } static constexpr mpt::uint32 max() noexcept { return 0x00ffffff; } }; template <> class numeric_limits : public std::numeric_limits { public: static constexpr mpt::int32 min() noexcept { return 0 - 0x00800000; } static constexpr mpt::int32 lowest() noexcept { return 0 - 0x00800000; } static constexpr mpt::int32 max() noexcept { return 0 + 0x007fffff; } }; } // namespace std #endif // !CPPCHECK #endif // MPT_ENDIAN_INT24_HPP libopenmpt-0.6.1+release.autotools/src/mpt/endian/integer.hpp0000644000175000017500000003402214044765004021254 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_ENDIAN_INTEGER_HPP #define MPT_ENDIAN_INTEGER_HPP #include "mpt/base/detect.hpp" #include "mpt/base/bit.hpp" #include "mpt/base/integer.hpp" #include "mpt/base/macros.hpp" #include "mpt/base/memory.hpp" #include "mpt/base/namespace.hpp" #include #include #include #include #include #include #if MPT_COMPILER_MSVC #include #endif namespace mpt { inline namespace MPT_INLINE_NS { struct BigEndian_tag { static constexpr mpt::endian endian = mpt::endian::big; }; struct LittleEndian_tag { static constexpr mpt::endian endian = mpt::endian::little; }; constexpr inline uint16 constexpr_bswap16(uint16 x) noexcept { return uint16(0) | ((x >> 8) & 0x00FFu) | ((x << 8) & 0xFF00u); } constexpr inline uint32 constexpr_bswap32(uint32 x) noexcept { return uint32(0) | ((x & 0x000000FFu) << 24) | ((x & 0x0000FF00u) << 8) | ((x & 0x00FF0000u) >> 8) | ((x & 0xFF000000u) >> 24); } constexpr inline uint64 constexpr_bswap64(uint64 x) noexcept { return uint64(0) | (((x >> 0) & 0xffull) << 56) | (((x >> 8) & 0xffull) << 48) | (((x >> 16) & 0xffull) << 40) | (((x >> 24) & 0xffull) << 32) | (((x >> 32) & 0xffull) << 24) | (((x >> 40) & 0xffull) << 16) | (((x >> 48) & 0xffull) << 8) | (((x >> 56) & 0xffull) << 0); } #if MPT_COMPILER_GCC #define MPT_bswap16 __builtin_bswap16 #define MPT_bswap32 __builtin_bswap32 #define MPT_bswap64 __builtin_bswap64 #elif MPT_COMPILER_MSVC #define MPT_bswap16 _byteswap_ushort #define MPT_bswap32 _byteswap_ulong #define MPT_bswap64 _byteswap_uint64 #endif // No intrinsics available #ifndef MPT_bswap16 #define MPT_bswap16(x) mpt::constexpr_bswap16(x) #endif #ifndef MPT_bswap32 #define MPT_bswap32(x) mpt::constexpr_bswap32(x) #endif #ifndef MPT_bswap64 #define MPT_bswap64(x) mpt::constexpr_bswap64(x) #endif template MPT_CONSTEXPRINLINE std::array EndianEncode(T val) noexcept { static_assert(Tendian::endian == mpt::endian::little || Tendian::endian == mpt::endian::big); static_assert(std::numeric_limits::is_integer); static_assert(!std::numeric_limits::is_signed); static_assert(sizeof(T) == size); using base_type = T; using unsigned_base_type = typename std::make_unsigned::type; using endian_type = Tendian; unsigned_base_type uval = static_cast(val); std::array data{}; if constexpr (endian_type::endian == mpt::endian::little) { for (std::size_t i = 0; i < sizeof(base_type); ++i) { data[i] = static_cast(static_cast((uval >> (i * 8)) & 0xffu)); } } else { for (std::size_t i = 0; i < sizeof(base_type); ++i) { data[(sizeof(base_type) - 1) - i] = static_cast(static_cast((uval >> (i * 8)) & 0xffu)); } } return data; } template MPT_CONSTEXPRINLINE T EndianDecode(std::array data) noexcept { static_assert(Tendian::endian == mpt::endian::little || Tendian::endian == mpt::endian::big); static_assert(std::numeric_limits::is_integer); static_assert(!std::numeric_limits::is_signed); static_assert(sizeof(T) == size); using base_type = T; using unsigned_base_type = typename std::make_unsigned::type; using endian_type = Tendian; base_type val = base_type(); unsigned_base_type uval = unsigned_base_type(); if constexpr (endian_type::endian == mpt::endian::little) { for (std::size_t i = 0; i < sizeof(base_type); ++i) { uval |= static_cast(static_cast(data[i])) << (i * 8); } } else { for (std::size_t i = 0; i < sizeof(base_type); ++i) { uval |= static_cast(static_cast(data[(sizeof(base_type) - 1) - i])) << (i * 8); } } val = static_cast(uval); return val; } MPT_CONSTEXPR20_FUN uint64 SwapBytesImpl(uint64 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) { return mpt::constexpr_bswap64(value); } else { return MPT_bswap64(value); } } MPT_CONSTEXPR20_FUN uint32 SwapBytesImpl(uint32 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) { return mpt::constexpr_bswap32(value); } else { return MPT_bswap32(value); } } MPT_CONSTEXPR20_FUN uint16 SwapBytesImpl(uint16 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) { return mpt::constexpr_bswap16(value); } else { return MPT_bswap16(value); } } MPT_CONSTEXPR20_FUN int64 SwapBytesImpl(int64 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) { return mpt::constexpr_bswap64(value); } else { return MPT_bswap64(value); } } MPT_CONSTEXPR20_FUN int32 SwapBytesImpl(int32 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) { return mpt::constexpr_bswap32(value); } else { return MPT_bswap32(value); } } MPT_CONSTEXPR20_FUN int16 SwapBytesImpl(int16 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) { return mpt::constexpr_bswap16(value); } else { return MPT_bswap16(value); } } // Do NOT remove these overloads, even if they seem useless. // We do not want risking to extend 8bit integers to int and then // endian-converting and casting back to int. // Thus these overloads. MPT_CONSTEXPR20_FUN uint8 SwapBytesImpl(uint8 value) noexcept { return value; } MPT_CONSTEXPR20_FUN int8 SwapBytesImpl(int8 value) noexcept { return value; } MPT_CONSTEXPR20_FUN char SwapBytesImpl(char value) noexcept { return value; } #undef MPT_bswap16 #undef MPT_bswap32 #undef MPT_bswap64 // On-disk integer types with defined endianness and no alignemnt requirements // Note: To easily debug module loaders (and anything else that uses this // wrapper struct), you can use the Debugger Visualizers available in // build/vs/debug/ to conveniently view the wrapped contents. template struct packed { public: using base_type = T; using endian_type = Tendian; public: std::array data; public: MPT_CONSTEXPR20_FUN void set(base_type val) noexcept { static_assert(std::numeric_limits::is_integer); MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) { if constexpr (endian_type::endian == mpt::endian::big) { typename std::make_unsigned::type uval = val; for (std::size_t i = 0; i < sizeof(base_type); ++i) { data[i] = static_cast((uval >> (8 * (sizeof(base_type) - 1 - i))) & 0xffu); } } else { typename std::make_unsigned::type uval = val; for (std::size_t i = 0; i < sizeof(base_type); ++i) { data[i] = static_cast((uval >> (8 * i)) & 0xffu); } } } else { if constexpr (mpt::endian::native == mpt::endian::little || mpt::endian::native == mpt::endian::big) { if constexpr (mpt::endian::native != endian_type::endian) { val = mpt::SwapBytesImpl(val); } std::memcpy(data.data(), &val, sizeof(val)); } else { using unsigned_base_type = typename std::make_unsigned::type; data = EndianEncode(val); } } } MPT_CONSTEXPR20_FUN base_type get() const noexcept { static_assert(std::numeric_limits::is_integer); MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) { if constexpr (endian_type::endian == mpt::endian::big) { typename std::make_unsigned::type uval = 0; for (std::size_t i = 0; i < sizeof(base_type); ++i) { uval |= static_cast::type>(data[i]) << (8 * (sizeof(base_type) - 1 - i)); } return static_cast(uval); } else { typename std::make_unsigned::type uval = 0; for (std::size_t i = 0; i < sizeof(base_type); ++i) { uval |= static_cast::type>(data[i]) << (8 * i); } return static_cast(uval); } } else { if constexpr (mpt::endian::native == mpt::endian::little || mpt::endian::native == mpt::endian::big) { base_type val = base_type(); std::memcpy(&val, data.data(), sizeof(val)); if constexpr (mpt::endian::native != endian_type::endian) { val = mpt::SwapBytesImpl(val); } return val; } else { using unsigned_base_type = typename std::make_unsigned::type; return EndianDecode(data); } } } MPT_CONSTEXPR20_FUN packed & operator=(const base_type & val) noexcept { set(val); return *this; } MPT_CONSTEXPR20_FUN operator base_type() const noexcept { return get(); } public: MPT_CONSTEXPR20_FUN packed & operator&=(base_type val) noexcept { set(get() & val); return *this; } MPT_CONSTEXPR20_FUN packed & operator|=(base_type val) noexcept { set(get() | val); return *this; } MPT_CONSTEXPR20_FUN packed & operator^=(base_type val) noexcept { set(get() ^ val); return *this; } MPT_CONSTEXPR20_FUN packed & operator+=(base_type val) noexcept { set(get() + val); return *this; } MPT_CONSTEXPR20_FUN packed & operator-=(base_type val) noexcept { set(get() - val); return *this; } MPT_CONSTEXPR20_FUN packed & operator*=(base_type val) noexcept { set(get() * val); return *this; } MPT_CONSTEXPR20_FUN packed & operator/=(base_type val) noexcept { set(get() / val); return *this; } MPT_CONSTEXPR20_FUN packed & operator%=(base_type val) noexcept { set(get() % val); return *this; } MPT_CONSTEXPR20_FUN packed & operator++() noexcept { // prefix set(get() + 1); return *this; } MPT_CONSTEXPR20_FUN packed & operator--() noexcept { // prefix set(get() - 1); return *this; } MPT_CONSTEXPR20_FUN base_type operator++(int) noexcept { // postfix base_type old = get(); set(old + 1); return old; } MPT_CONSTEXPR20_FUN base_type operator--(int) noexcept { // postfix base_type old = get(); set(old - 1); return old; } }; using int64le = packed; using int32le = packed; using int16le = packed; using int8le = packed; using uint64le = packed; using uint32le = packed; using uint16le = packed; using uint8le = packed; using int64be = packed; using int32be = packed; using int16be = packed; using int8be = packed; using uint64be = packed; using uint32be = packed; using uint16be = packed; using uint8be = packed; constexpr bool declare_binary_safe(const int64le &) { return true; } constexpr bool declare_binary_safe(const int32le &) { return true; } constexpr bool declare_binary_safe(const int16le &) { return true; } constexpr bool declare_binary_safe(const int8le &) { return true; } constexpr bool declare_binary_safe(const uint64le &) { return true; } constexpr bool declare_binary_safe(const uint32le &) { return true; } constexpr bool declare_binary_safe(const uint16le &) { return true; } constexpr bool declare_binary_safe(const uint8le &) { return true; } constexpr bool declare_binary_safe(const int64be &) { return true; } constexpr bool declare_binary_safe(const int32be &) { return true; } constexpr bool declare_binary_safe(const int16be &) { return true; } constexpr bool declare_binary_safe(const int8be &) { return true; } constexpr bool declare_binary_safe(const uint64be &) { return true; } constexpr bool declare_binary_safe(const uint32be &) { return true; } constexpr bool declare_binary_safe(const uint16be &) { return true; } constexpr bool declare_binary_safe(const uint8be &) { return true; } static_assert(mpt::check_binary_size(8)); static_assert(mpt::check_binary_size(4)); static_assert(mpt::check_binary_size(2)); static_assert(mpt::check_binary_size(1)); static_assert(mpt::check_binary_size(8)); static_assert(mpt::check_binary_size(4)); static_assert(mpt::check_binary_size(2)); static_assert(mpt::check_binary_size(1)); static_assert(mpt::check_binary_size(8)); static_assert(mpt::check_binary_size(4)); static_assert(mpt::check_binary_size(2)); static_assert(mpt::check_binary_size(1)); static_assert(mpt::check_binary_size(8)); static_assert(mpt::check_binary_size(4)); static_assert(mpt::check_binary_size(2)); static_assert(mpt::check_binary_size(1)); template struct make_le { using type = packed::type, LittleEndian_tag>; }; template struct make_be { using type = packed::type, BigEndian_tag>; }; template struct make_endian { }; template struct make_endian { using type = packed::type, LittleEndian_tag>; }; template struct make_endian { using type = packed::type, BigEndian_tag>; }; template MPT_CONSTEXPR20_FUN auto as_le(T v) noexcept -> typename mpt::make_le::type>::type { typename mpt::make_le::type>::type res{}; res = v; return res; } template MPT_CONSTEXPR20_FUN auto as_be(T v) noexcept -> typename mpt::make_be::type>::type { typename mpt::make_be::type>::type res{}; res = v; return res; } template MPT_CONSTEXPR20_FUN Tpacked as_endian(typename Tpacked::base_type v) noexcept { Tpacked res{}; res = v; return res; } } // namespace MPT_INLINE_NS } // namespace mpt namespace std { template class numeric_limits> : public std::numeric_limits { }; template class numeric_limits> : public std::numeric_limits { }; } // namespace std #endif // MPT_ENDIAN_INTEGER_HPP libopenmpt-0.6.1+release.autotools/src/mpt/environment/0000755000175000017500000000000014175541574020304 500000000000000libopenmpt-0.6.1+release.autotools/src/mpt/environment/environment.hpp0000644000175000017500000000267514107230757023304 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_ENVIRONMENT_ENVIRONMENT_HPP #define MPT_ENVIRONMENT_ENVIRONMENT_HPP #include "mpt/base/detect.hpp" #include "mpt/base/macros.hpp" #include "mpt/base/namespace.hpp" #include "mpt/string/types.hpp" #include "mpt/string_transcode/transcode.hpp" #include "mpt/system_error/system_error.hpp" #include #if MPT_OS_WINDOWS #if defined(UNICODE) && !MPT_OS_WINDOWS_WINRT #include #endif // !MPT_OS_WINDOWS_WINRT #endif // MPT_OS_WINDOWS #include #if MPT_OS_WINDOWS #include #endif // MPT_OS_WINDOWS namespace mpt { inline namespace MPT_INLINE_NS { inline std::optional getenv(const mpt::ustring & env_var) { #if MPT_OS_WINDOWS && MPT_OS_WINDOWS_WINRT MPT_UNUSED(env_var); return std::nullopt; #elif MPT_OS_WINDOWS && defined(UNICODE) std::vector buf(32767); DWORD size = GetEnvironmentVariable(mpt::transcode(env_var).c_str(), buf.data(), 32767); if (size == 0) { mpt::windows::ExpectError(ERROR_ENVVAR_NOT_FOUND); return std::nullopt; } return mpt::transcode(buf.data()); #else const char * val = std::getenv(mpt::transcode(mpt::environment_encoding, env_var).c_str()); if (!val) { return std::nullopt; } return mpt::transcode(mpt::environment_encoding, val); #endif } } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_ENVIRONMENT_ENVIRONMENT_HPP libopenmpt-0.6.1+release.autotools/src/mpt/exception_text/0000755000175000017500000000000014175541574021002 500000000000000libopenmpt-0.6.1+release.autotools/src/mpt/exception_text/exception_text.hpp0000644000175000017500000000335014107230757024467 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_EXCEPTION_TEXT_EXCEPTION_TEXT_HPP #define MPT_EXCEPTION_TEXT_EXCEPTION_TEXT_HPP #include "mpt/base/namespace.hpp" #include "mpt/string/types.hpp" #include "mpt/string_transcode/transcode.hpp" #include #include namespace mpt { inline namespace MPT_INLINE_NS { template inline Tstring get_exception_text(const std::exception & e) { if (e.what() && (std::strlen(e.what()) > 0)) { return mpt::transcode(mpt::exception_string{e.what()}); } else if (typeid(e).name() && (std::strlen(typeid(e).name()) > 0)) { return mpt::transcode(mpt::source_string{typeid(e).name()}); } else { return mpt::transcode(mpt::source_string{"unknown exception name"}); } } template <> inline std::string get_exception_text(const std::exception & e) { if (e.what() && (std::strlen(e.what()) > 0)) { return std::string{e.what()}; } else if (typeid(e).name() && (std::strlen(typeid(e).name()) > 0)) { return std::string{typeid(e).name()}; } else { return std::string{"unknown exception name"}; } } template inline Tstring get_current_exception_text() { try { throw; } catch (const std::exception & e) { return mpt::get_exception_text(e); } catch (...) { return mpt::transcode(mpt::source_string{"unknown exception"}); } } template <> inline std::string get_current_exception_text() { try { throw; } catch (const std::exception & e) { return mpt::get_exception_text(e); } catch (...) { return std::string{"unknown exception"}; } } } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_EXCEPTION_TEXT_EXCEPTION_TEXT_HPP libopenmpt-0.6.1+release.autotools/src/mpt/format/0000755000175000017500000000000014175541574017230 500000000000000libopenmpt-0.6.1+release.autotools/src/mpt/format/tests/0000755000175000017500000000000014175541575020373 500000000000000libopenmpt-0.6.1+release.autotools/src/mpt/format/tests/tests_format_message.hpp0000644000175000017500000000464614053176735025251 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_FORMAT_TESTS_FORMAT_MESSAGE_HPP #define MPT_FORMAT_TESTS_FORMAT_MESSAGE_HPP #include "mpt/base/detect.hpp" #include "mpt/base/namespace.hpp" #include "mpt/format/message.hpp" #include "mpt/format/message_macros.hpp" #include "mpt/test/test.hpp" #include "mpt/test/test_macros.hpp" namespace mpt { inline namespace MPT_INLINE_NS { namespace tests { namespace format { namespace message { #if MPT_COMPILER_CLANG #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wglobal-constructors" #endif MPT_TEST_GROUP_INLINE("mpt/format/message") #if MPT_COMPILER_CLANG #pragma clang diagnostic pop #endif { static_assert(mpt::parse_format_string_argument_count("") == 0); static_assert(mpt::parse_format_string_argument_count("{{") == 0); static_assert(mpt::parse_format_string_argument_count("}}") == 0); static_assert(mpt::parse_format_string_argument_count("{}") == 1); static_assert(mpt::parse_format_string_argument_count("{}{}") == 2); static_assert(mpt::parse_format_string_argument_count("{0}{1}") == 2); // basic MPT_TEST_EXPECT_EQUAL(MPT_AFORMAT_MESSAGE("{}{}{}")(1, 2, 3), "123"); MPT_TEST_EXPECT_EQUAL(MPT_AFORMAT_MESSAGE("{2}{1}{0}")(1, 2, 3), "321"); MPT_TEST_EXPECT_EQUAL(MPT_AFORMAT_MESSAGE("{2}{1}{0}{4}{3}{6}{5}{7}{10}{9}{8}")(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "a"), "21043657a98"); #if !defined(MPT_COMPILER_QUIRK_NO_WCHAR) MPT_TEST_EXPECT_EQUAL(MPT_AFORMAT_MESSAGE(L"{}{}{}")(1, 2, 3), L"123"); #endif // !MPT_COMPILER_QUIRK_NO_WCHAR // escaping behviour MPT_TEST_EXPECT_EQUAL(MPT_AFORMAT_MESSAGE("%")(), "%"); MPT_TEST_EXPECT_EQUAL(MPT_AFORMAT_MESSAGE("%")(), "%"); MPT_TEST_EXPECT_EQUAL(MPT_AFORMAT_MESSAGE("%%")(), "%%"); MPT_TEST_EXPECT_EQUAL(MPT_AFORMAT_MESSAGE("{}")("a"), "a"); MPT_TEST_EXPECT_EQUAL(MPT_AFORMAT_MESSAGE("{}%")("a"), "a%"); MPT_TEST_EXPECT_EQUAL(MPT_AFORMAT_MESSAGE("{}%")("a"), "a%"); MPT_TEST_EXPECT_EQUAL(MPT_AFORMAT_MESSAGE("{}%%")("a"), "a%%"); MPT_TEST_EXPECT_EQUAL(MPT_AFORMAT_MESSAGE("%1")(), "%1"); MPT_TEST_EXPECT_EQUAL(MPT_AFORMAT_MESSAGE("%{}")("a"), "%a"); MPT_TEST_EXPECT_EQUAL(MPT_AFORMAT_MESSAGE("%b")(), "%b"); MPT_TEST_EXPECT_EQUAL(MPT_AFORMAT_MESSAGE("{{}}")(), "{}"); MPT_TEST_EXPECT_EQUAL(MPT_AFORMAT_MESSAGE("{{{}}}")("a"), "{a}"); } } // namespace message } // namespace format } // namespace tests } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_FORMAT_TESTS_FORMAT_MESSAGE_HPP libopenmpt-0.6.1+release.autotools/src/mpt/format/tests/tests_format_simple.hpp0000644000175000017500000002060014044173026025070 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_FORMAT_TESTS_FORMAT_SIMPLE_HPP #define MPT_FORMAT_TESTS_FORMAT_SIMPLE_HPP #include "mpt/base/detect.hpp" #include "mpt/base/namespace.hpp" #include "mpt/format/simple.hpp" #include "mpt/string/types.hpp" #include "mpt/test/test.hpp" #include "mpt/test/test_macros.hpp" #include namespace mpt { inline namespace MPT_INLINE_NS { namespace tests { namespace format { namespace simple { #if MPT_COMPILER_CLANG #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wglobal-constructors" #endif MPT_TEST_GROUP_INLINE("mpt/format/simple") #if MPT_COMPILER_CLANG #pragma clang diagnostic pop #endif { MPT_TEST_EXPECT_EQUAL(mpt::format::val(1.5f), "1.5"); MPT_TEST_EXPECT_EQUAL(mpt::format::val(true), "1"); MPT_TEST_EXPECT_EQUAL(mpt::format::val(false), "0"); MPT_TEST_EXPECT_EQUAL(mpt::format::val(0), "0"); MPT_TEST_EXPECT_EQUAL(mpt::format::val(-23), "-23"); MPT_TEST_EXPECT_EQUAL(mpt::format::val(42), "42"); MPT_TEST_EXPECT_EQUAL(mpt::format::hex0<3>((int32)-1), "-001"); MPT_TEST_EXPECT_EQUAL(mpt::format::hex((int32)-1), "-1"); MPT_TEST_EXPECT_EQUAL(mpt::format::hex(-0xabcde), "-abcde"); MPT_TEST_EXPECT_EQUAL(mpt::format::hex(std::numeric_limits::min()), "-80000000"); MPT_TEST_EXPECT_EQUAL(mpt::format::hex(std::numeric_limits::min() + 1), "-7fffffff"); MPT_TEST_EXPECT_EQUAL(mpt::format::hex(0x123e), "123e"); MPT_TEST_EXPECT_EQUAL(mpt::format::hex0<6>(0x123e), "00123e"); MPT_TEST_EXPECT_EQUAL(mpt::format::hex0<2>(0x123e), "123e"); MPT_TEST_EXPECT_EQUAL(mpt::format::dec0<0>(1), "1"); MPT_TEST_EXPECT_EQUAL(mpt::format::dec0<1>(1), "1"); MPT_TEST_EXPECT_EQUAL(mpt::format::dec0<2>(1), "01"); MPT_TEST_EXPECT_EQUAL(mpt::format::dec0<3>(1), "001"); MPT_TEST_EXPECT_EQUAL(mpt::format::dec0<0>(11), "11"); MPT_TEST_EXPECT_EQUAL(mpt::format::dec0<1>(11), "11"); MPT_TEST_EXPECT_EQUAL(mpt::format::dec0<2>(11), "11"); MPT_TEST_EXPECT_EQUAL(mpt::format::dec0<3>(11), "011"); MPT_TEST_EXPECT_EQUAL(mpt::format::dec0<0>(-1), "-1"); MPT_TEST_EXPECT_EQUAL(mpt::format::dec0<1>(-1), "-1"); MPT_TEST_EXPECT_EQUAL(mpt::format::dec0<2>(-1), "-01"); MPT_TEST_EXPECT_EQUAL(mpt::format::dec0<3>(-1), "-001"); MPT_TEST_EXPECT_EQUAL(mpt::format::HEX0<7>(0xa2345678), MPT_USTRING("A2345678")); MPT_TEST_EXPECT_EQUAL(mpt::format::HEX0<8>(0xa2345678), MPT_USTRING("A2345678")); MPT_TEST_EXPECT_EQUAL(mpt::format::HEX0<9>(0xa2345678), MPT_USTRING("0A2345678")); MPT_TEST_EXPECT_EQUAL(mpt::format::HEX0<10>(0xa2345678), MPT_USTRING("00A2345678")); #if !defined(MPT_COMPILER_QUIRK_NO_WCHAR) MPT_TEST_EXPECT_EQUAL(mpt::format::hex(0x123e), L"123e"); MPT_TEST_EXPECT_EQUAL(mpt::format::hex0<6>(0x123e), L"00123e"); MPT_TEST_EXPECT_EQUAL(mpt::format::hex0<2>(0x123e), L"123e"); #endif // !MPT_COMPILER_QUIRK_NO_WCHAR MPT_TEST_EXPECT_EQUAL(mpt::format::val(-87.0f), "-87"); if (mpt::format::val(-0.5e-6) != "-5e-007" && mpt::format::val(-0.5e-6) != "-5e-07" && mpt::format::val(-0.5e-6) != "-5e-7" && mpt::format::val(-0.5e-6) != "-4.9999999999999998e-7" && mpt::format::val(-0.5e-6) != "-4.9999999999999998e-07" && mpt::format::val(-0.5e-6) != "-4.9999999999999998e-007") { MPT_TEST_EXPECT_EQUAL(true, false); } if (mpt::format::val(-1.0 / 65536.0) != "-1.52587890625e-005" && mpt::format::val(-1.0 / 65536.0) != "-1.52587890625e-05" && mpt::format::val(-1.0 / 65536.0) != "-1.52587890625e-5") { MPT_TEST_EXPECT_EQUAL(true, false); } if (mpt::format::val(-1.0f / 65536.0f) != "-1.52587891e-005" && mpt::format::val(-1.0f / 65536.0f) != "-1.52587891e-05" && mpt::format::val(-1.0f / 65536.0f) != "-1.52587891e-5" && mpt::format::val(-1.0f / 65536.0f) != "-1.5258789e-005" && mpt::format::val(-1.0f / 65536.0f) != "-1.5258789e-05" && mpt::format::val(-1.0f / 65536.0f) != "-1.5258789e-5") { MPT_TEST_EXPECT_EQUAL(true, false); } if (mpt::format::val(58.65403492763) != "58.654034927630001" && mpt::format::val(58.65403492763) != "58.65403492763") { MPT_TEST_EXPECT_EQUAL(true, false); } MPT_TEST_EXPECT_EQUAL(mpt::format::flt(58.65403492763, 6), "58.654"); MPT_TEST_EXPECT_EQUAL(mpt::format::fix(23.42, 1), "23.4"); MPT_TEST_EXPECT_EQUAL(mpt::format::fix(234.2, 1), "234.2"); MPT_TEST_EXPECT_EQUAL(mpt::format::fix(2342.0, 1), "2342.0"); MPT_TEST_EXPECT_EQUAL(mpt::format::dec(2, ';', 2345678), std::string("2;34;56;78")); MPT_TEST_EXPECT_EQUAL(mpt::format::dec(2, ';', 12345678), std::string("12;34;56;78")); MPT_TEST_EXPECT_EQUAL(mpt::format::hex(3, ':', 0xa2345678), std::string("a2:345:678")); MPT_TEST_EXPECT_EQUAL(mpt::format::dec(2, ';', 12345678), MPT_USTRING("12;34;56;78")); MPT_TEST_EXPECT_EQUAL(mpt::format::hex(3, ':', 0xa2345678), MPT_USTRING("a2:345:678")); MPT_TEST_EXPECT_EQUAL(mpt::format::HEX0<7>(3, ':', 0xa2345678), MPT_USTRING("A2:345:678")); MPT_TEST_EXPECT_EQUAL(mpt::format::HEX0<8>(3, ':', 0xa2345678), MPT_USTRING("A2:345:678")); MPT_TEST_EXPECT_EQUAL(mpt::format::HEX0<9>(3, ':', 0xa2345678), MPT_USTRING("0A2:345:678")); MPT_TEST_EXPECT_EQUAL(mpt::format::HEX0<10>(3, ':', 0xa2345678), MPT_USTRING("0:0A2:345:678")); MPT_TEST_EXPECT_EQUAL(mpt::format::HEX0<11>(3, ':', 0xa2345678), MPT_USTRING("00:0A2:345:678")); MPT_TEST_EXPECT_EQUAL(mpt::format::HEX0<12>(3, ':', 0xa2345678), MPT_USTRING("000:0A2:345:678")); MPT_TEST_EXPECT_EQUAL(mpt::format::HEX0<7>(3, ':', -0x12345678), MPT_USTRING("-12:345:678")); MPT_TEST_EXPECT_EQUAL(mpt::format::HEX0<8>(3, ':', -0x12345678), MPT_USTRING("-12:345:678")); MPT_TEST_EXPECT_EQUAL(mpt::format::HEX0<9>(3, ':', -0x12345678), MPT_USTRING("-012:345:678")); MPT_TEST_EXPECT_EQUAL(mpt::format::HEX0<10>(3, ':', -0x12345678), MPT_USTRING("-0:012:345:678")); MPT_TEST_EXPECT_EQUAL(mpt::format::HEX0<11>(3, ':', -0x12345678), MPT_USTRING("-00:012:345:678")); MPT_TEST_EXPECT_EQUAL(mpt::format::HEX0<12>(3, ':', -0x12345678), MPT_USTRING("-000:012:345:678")); MPT_TEST_EXPECT_EQUAL(mpt::format::HEX0<5>(3, ':', 0x345678), MPT_USTRING("345:678")); MPT_TEST_EXPECT_EQUAL(mpt::format::HEX0<6>(3, ':', 0x345678), MPT_USTRING("345:678")); MPT_TEST_EXPECT_EQUAL(mpt::format::HEX0<7>(3, ':', 0x345678), MPT_USTRING("0:345:678")); MPT_TEST_EXPECT_EQUAL(mpt::format::HEX0<5>(3, ':', -0x345678), MPT_USTRING("-345:678")); MPT_TEST_EXPECT_EQUAL(mpt::format::HEX0<6>(3, ':', -0x345678), MPT_USTRING("-345:678")); MPT_TEST_EXPECT_EQUAL(mpt::format::HEX0<7>(3, ':', -0x345678), MPT_USTRING("-0:345:678")); MPT_TEST_EXPECT_EQUAL(mpt::format::left(3, "a"), "a "); MPT_TEST_EXPECT_EQUAL(mpt::format::right(3, "a"), " a"); MPT_TEST_EXPECT_EQUAL(mpt::format::center(3, "a"), " a "); MPT_TEST_EXPECT_EQUAL(mpt::format::center(4, "a"), " a "); MPT_TEST_EXPECT_EQUAL(mpt::format::flt(6.12345, 3), "6.12"); MPT_TEST_EXPECT_EQUAL(mpt::format::fix(6.12345, 3), "6.123"); MPT_TEST_EXPECT_EQUAL(mpt::format::flt(6.12345, 4), "6.123"); MPT_TEST_EXPECT_EQUAL(mpt::format::fix(6.12345, 4), "6.1235"); #if !defined(MPT_COMPILER_QUIRK_NO_WCHAR) MPT_TEST_EXPECT_EQUAL(mpt::format::flt(6.12345, 3), L"6.12"); MPT_TEST_EXPECT_EQUAL(mpt::format::fix(6.12345, 3), L"6.123"); MPT_TEST_EXPECT_EQUAL(mpt::format::flt(6.12345, 4), L"6.123"); #endif // !MPT_COMPILER_QUIRK_NO_WCHAR } } // namespace simple } // namespace format } // namespace tests } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_FORMAT_TESTS_FORMAT_SIMPLE_HPP libopenmpt-0.6.1+release.autotools/src/mpt/format/default_floatingpoint.hpp0000644000175000017500000000514014174570175024240 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_FORMAT_DETAULT_FLOATINGPOINT_HPP #define MPT_FORMAT_DETAULT_FLOATINGPOINT_HPP #include "mpt/base/detect.hpp" #if MPT_MSVC_AT_LEAST(2019, 4) || MPT_GCC_AT_LEAST(11, 1, 0) #define MPT_FORMAT_FORMAT_DEFAULT_FLOAT_CXX17 1 #else #define MPT_FORMAT_FORMAT_DEFAULT_FLOAT_CXX17 0 #endif #if MPT_FORMAT_FORMAT_DEFAULT_FLOAT_CXX17 #include "mpt/base/algorithm.hpp" #endif #include "mpt/base/namespace.hpp" #include "mpt/format/helpers.hpp" #include "mpt/string_transcode/transcode.hpp" #if MPT_FORMAT_FORMAT_DEFAULT_FLOAT_CXX17 #include #endif #if !MPT_FORMAT_FORMAT_DEFAULT_FLOAT_CXX17 #include #include #include #include #include #endif #include #if MPT_FORMAT_FORMAT_DEFAULT_FLOAT_CXX17 #include #endif #include namespace mpt { inline namespace MPT_INLINE_NS { #if MPT_FORMAT_FORMAT_DEFAULT_FLOAT_CXX17 template ::value, bool> = true> inline Tstring to_chars_string(const T & x) { std::string str(1, '\0'); bool done = false; while (!done) { std::to_chars_result result = std::to_chars(str.data(), str.data() + str.size(), x); if (result.ec != std::errc{}) { str.resize(mpt::exponential_grow(str.size()), '\0'); } else { str.resize(result.ptr - str.data()); done = true; } } return mpt::convert_formatted_simple(str); } template ::value, bool> = true> inline Tstring format_value_default(const T & x) { return mpt::transcode(mpt::to_chars_string::type>(x)); } #endif #if !MPT_FORMAT_FORMAT_DEFAULT_FLOAT_CXX17 template ::value, bool> = true> inline Tstring to_stream_string(const T & x) { using stream_char_type = typename mpt::select_format_char_type::type; std::basic_ostringstream s; s.imbue(std::locale::classic()); s << std::setprecision(std::numeric_limits::max_digits10) << x; return mpt::convert_formatted_simple(s.str()); } template ::value, bool> = true> inline Tstring format_value_default(const T & x) { return mpt::transcode(mpt::to_stream_string::type>(x)); } #endif } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_FORMAT_DETAULT_FLOATINGPOINT_HPP libopenmpt-0.6.1+release.autotools/src/mpt/format/default_formatter.hpp0000644000175000017500000000124314044173026023354 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_FORMAT_DEFAULT_FORMATTER_HPP #define MPT_FORMAT_DEFAULT_FORMATTER_HPP #include "mpt/base/namespace.hpp" #include "mpt/format/default_floatingpoint.hpp" #include "mpt/format/default_integer.hpp" #include "mpt/format/default_string.hpp" #include namespace mpt { inline namespace MPT_INLINE_NS { struct default_formatter { template static inline Tstring format(const T & value) { using namespace mpt; return format_value_default(value); } }; } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_FORMAT_DEFAULT_FORMATTER_HPP libopenmpt-0.6.1+release.autotools/src/mpt/format/default_integer.hpp0000644000175000017500000000661014174570175023023 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_FORMAT_DEFAULT_INTEGER_HPP #define MPT_FORMAT_DEFAULT_INTEGER_HPP #include "mpt/base/detect.hpp" #if 1 #define MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17 1 #else #define MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17 0 #endif #if MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17 #include "mpt/base/algorithm.hpp" #endif // MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17 #include "mpt/base/namespace.hpp" #include "mpt/base/utility.hpp" #include "mpt/format/helpers.hpp" #include "mpt/string_transcode/transcode.hpp" #if MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17 #include #endif // MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17 #if !MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17 #include #include #include #endif // !MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17 #include #if MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17 #include #endif // MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17 #include namespace mpt { inline namespace MPT_INLINE_NS { #if MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17 template ::value, bool> = true> inline Tstring to_chars_string(const T & x) { std::string str(1, '\0'); bool done = false; while (!done) { if constexpr (std::is_same::value) { std::to_chars_result result = std::to_chars(str.data(), str.data() + str.size(), static_cast(x)); if (result.ec != std::errc{}) { str.resize(mpt::exponential_grow(str.size()), '\0'); } else { str.resize(result.ptr - str.data()); done = true; } } else { std::to_chars_result result = std::to_chars(str.data(), str.data() + str.size(), x); if (result.ec != std::errc{}) { str.resize(mpt::exponential_grow(str.size()), '\0'); } else { str.resize(result.ptr - str.data()); done = true; } } } return mpt::convert_formatted_simple(str); } template ::value, bool> = true> inline Tstring format_value_default(const T & x) { return mpt::transcode(mpt::to_chars_string::type>(x)); } #endif // MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17 #if !MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17 template ::value, bool> = true> inline Tstring to_stream_string(const T & x) { using stream_char_type = typename mpt::select_format_char_type::type; std::basic_ostringstream s; s.imbue(std::locale::classic()); if constexpr (std::is_same::value) { s << static_cast(x); } else if constexpr (mpt::is_character::value) { s << (x + 0); // force integral promotion } else { s << x; } return mpt::convert_formatted_simple(s.str()); } template ::value, bool> = true> inline Tstring format_value_default(const T & x) { return mpt::transcode(mpt::to_stream_string::type>(x)); } #endif // !MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17 template ::value, bool> = true> inline Tstring format_value_default(const T & x) { return mpt::format_value_default(mpt::to_underlying(x)); } } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_FORMAT_DEFAULT_INTEGER_HPP libopenmpt-0.6.1+release.autotools/src/mpt/format/default_string.hpp0000644000175000017500000000102014107230757022655 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_FORMAT_DEFAULT_STRING_HPP #define MPT_FORMAT_DEFAULT_STRING_HPP #include "mpt/base/namespace.hpp" #include "mpt/string_transcode/transcode.hpp" namespace mpt { inline namespace MPT_INLINE_NS { template inline auto format_value_default(const T & x) -> decltype(mpt::transcode(x)) { return mpt::transcode(x); } } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_FORMAT_DEFAULT_STRING_HPP libopenmpt-0.6.1+release.autotools/src/mpt/format/helpers.hpp0000644000175000017500000000413114107230757021313 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_FORMAT_HELPERS_HPP #define MPT_FORMAT_HELPERS_HPP #include "mpt/base/detect.hpp" #include "mpt/base/namespace.hpp" #include "mpt/string/types.hpp" #include "mpt/string_transcode/transcode.hpp" #include #include namespace mpt { inline namespace MPT_INLINE_NS { template inline Tdststring convert_formatted_simple(const Tsrcstring & src) { if constexpr (std::is_same::value) { return src; } else { Tdststring dst; dst.reserve(src.length()); for (std::size_t i = 0; i < src.length(); ++i) { dst.push_back(mpt::unsafe_char_convert(src[i])); } return dst; } } template struct select_format_char_type { using type = char; }; #if !defined(MPT_COMPILER_QUIRK_NO_WCHAR) template <> struct select_format_char_type { using type = wchar_t; }; #if MPT_USTRING_MODE_WIDE #if MPT_CXX_AT_LEAST(20) template <> struct select_format_char_type { using type = wchar_t; }; #endif // C++20 template <> struct select_format_char_type { using type = wchar_t; }; template <> struct select_format_char_type { using type = wchar_t; }; #endif // MPT_USTRING_MODE_WIDE #endif // !MPT_COMPILER_QUIRK_NO_WCHAR template struct select_format_string_type { using type = mpt::ustring; }; template <> struct select_format_string_type { using type = std::string; }; #if !defined(MPT_COMPILER_QUIRK_NO_WCHAR) template <> struct select_format_string_type { using type = std::wstring; }; #endif // !MPT_COMPILER_QUIRK_NO_WCHAR #if MPT_CXX_AT_LEAST(20) template <> struct select_format_string_type { using type = std::u8string; }; #endif // C++20 template <> struct select_format_string_type { using type = std::u16string; }; template <> struct select_format_string_type { using type = std::u32string; }; } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_FORMAT_HELPERS_HPP libopenmpt-0.6.1+release.autotools/src/mpt/format/message.hpp0000644000175000017500000002005014060603570021266 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_FORMAT_MESSAGE_HPP #define MPT_FORMAT_MESSAGE_HPP #include "mpt/base/macros.hpp" #include "mpt/base/namespace.hpp" #include "mpt/base/span.hpp" #include "mpt/string/types.hpp" #include "mpt/string/utility.hpp" #include #include #include #include namespace mpt { inline namespace MPT_INLINE_NS { class format_message_syntax_error : public std::domain_error { public: format_message_syntax_error() : std::domain_error("format string syntax error") { return; } }; template class message_formatter { public: using Tstring = typename mpt::make_string_type::type; private: Tstring format; private: MPT_NOINLINE Tstring do_format(const mpt::span vals) const { using traits = typename mpt::string_traits; using char_type = typename traits::char_type; using size_type = typename traits::size_type; Tstring result; const size_type len = traits::length(format); traits::reserve(result, len); std::size_t max_arg = 0; //std::size_t args = 0; bool success = true; enum class state : int { error = -1, text = 0, open_seen = 1, number_seen = 2, close_seen = 3, }; state state = state::text; bool numbered_args = false; bool unnumbered_args = false; std::size_t last_arg = 0; std::size_t this_arg = 0; std::size_t current_arg = 0; for (size_type pos = 0; pos != len; ++pos) { char_type c = format[pos]; switch (state) { case state::text: if (c == char_type('{')) { state = state::open_seen; } else if (c == char_type('}')) { state = state::close_seen; } else { state = state::text; traits::append(result, 1, c); // output c here } break; case state::open_seen: if (c == char_type('{')) { state = state::text; traits::append(result, 1, char_type('{')); // output { here } else if (c == char_type('}')) { state = state::text; unnumbered_args = true; last_arg++; this_arg = last_arg; { // output this_arg here const std::size_t n = this_arg - 1; if (n < std::size(vals)) { traits::append(result, vals[n]); } } if (this_arg > max_arg) { max_arg = this_arg; } //args += 1; } else if (char_type('0') <= c && c <= char_type('9')) { state = state::number_seen; numbered_args = true; current_arg = c - char_type('0'); } else { state = state::error; } break; case state::number_seen: if (c == char_type('{')) { state = state::error; } else if (c == char_type('}')) { state = state::text; this_arg = current_arg + 1; { // output this_arg here const std::size_t n = this_arg - 1; if (n < std::size(vals)) { traits::append(result, vals[n]); } } if (this_arg > max_arg) { max_arg = this_arg; } //args += 1; } else if (char_type('0') <= c && c <= char_type('9')) { state = state::number_seen; numbered_args = true; current_arg = (current_arg * 10) + (c - char_type('0')); } else { state = state::error; } break; case state::close_seen: if (c == char_type('{')) { state = state::error; } else if (c == char_type('}')) { state = state::text; traits::append(result, 1, char_type('}')); // output } here } else { state = state::error; } break; case state::error: state = state::error; break; } } if (state == state::error) { success = false; } if (state != state::text) { success = false; } if (numbered_args && unnumbered_args) { success = false; } if (!success) { throw format_message_syntax_error(); } return result; } public: MPT_FORCEINLINE message_formatter(Tstring format_) : format(std::move(format_)) { } public: template MPT_NOINLINE Tstring operator()(Ts &&... xs) const { const std::array vals{{Tformatter::template format(std::forward(xs))...}}; return do_format(mpt::as_span(vals)); } }; // struct message_formatter template class message_formatter_counted { private: message_formatter formatter; public: template inline message_formatter_counted(const Tchar (&format)[literal_length]) : formatter(Tstring(format)) { return; } public: template inline Tstring operator()(Ts &&... xs) const { static_assert(static_cast(sizeof...(xs)) == N); return formatter(std::forward(xs)...); } }; // struct message_formatter_counted template MPT_CONSTEXPRINLINE std::ptrdiff_t parse_format_string_argument_count_impl(const Tchar * const format, const std::size_t len) { std::size_t max_arg = 0; std::size_t args = 0; bool success = true; enum class state : int { error = -1, text = 0, open_seen = 1, number_seen = 2, close_seen = 3, }; state state = state::text; bool numbered_args = false; bool unnumbered_args = false; std::size_t last_arg = 0; std::size_t this_arg = 0; std::size_t current_arg = 0; for (std::size_t pos = 0; pos != len; ++pos) { Tchar c = format[pos]; switch (state) { case state::text: if (c == Tchar('{')) { state = state::open_seen; } else if (c == Tchar('}')) { state = state::close_seen; } else { state = state::text; // output c here } break; case state::open_seen: if (c == Tchar('{')) { state = state::text; // output { here } else if (c == Tchar('}')) { state = state::text; unnumbered_args = true; last_arg++; this_arg = last_arg; // output this_arg here if (this_arg > max_arg) { max_arg = this_arg; } args += 1; } else if (Tchar('0') <= c && c <= Tchar('9')) { state = state::number_seen; numbered_args = true; current_arg = c - Tchar('0'); } else { state = state::error; } break; case state::number_seen: if (c == Tchar('{')) { state = state::error; } else if (c == Tchar('}')) { state = state::text; this_arg = current_arg + 1; // output this_arg here if (this_arg > max_arg) { max_arg = this_arg; } args += 1; } else if (Tchar('0') <= c && c <= Tchar('9')) { state = state::number_seen; numbered_args = true; current_arg = (current_arg * 10) + (c - Tchar('0')); } else { state = state::error; } break; case state::close_seen: if (c == Tchar('{')) { state = state::error; } else if (c == Tchar('}')) { state = state::text; // output } here } else { state = state::error; } break; case state::error: state = state::error; break; } } if (state == state::error) { success = false; } if (state != state::text) { success = false; } if (numbered_args && unnumbered_args) { success = false; } if (!success) { throw format_message_syntax_error(); } if (max_arg != args) { throw format_message_syntax_error(); } return max_arg; } template MPT_CONSTEXPRINLINE std::ptrdiff_t parse_format_string_argument_count(const Tchar (&format)[literal_length]) { return parse_format_string_argument_count_impl(format, literal_length - 1); } template inline auto format_message(const Tchar (&format)[N]) { using Tstring = typename mpt::make_string_type::type; return message_formatter_counted(format); } template inline auto format_message_typed(const Tchar (&format)[N]) { return message_formatter_counted(format); } } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_FORMAT_MESSAGE_HPP libopenmpt-0.6.1+release.autotools/src/mpt/format/message_macros.hpp0000644000175000017500000000320414053176735022646 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_FORMAT_MESSAGE_MACROS_HPP #define MPT_FORMAT_MESSAGE_MACROS_HPP #include "mpt/base/detect.hpp" #include "mpt/detect/mfc.hpp" #include "mpt/format/default_formatter.hpp" #include "mpt/format/message.hpp" #define MPT_AFORMAT_MESSAGE(f) mpt::format_message(f) #if !defined(MPT_COMPILER_QUIRK_NO_WCHAR) #define MPT_WFORMAT_MESSAGE(f) mpt::format_message_typed(L##f) #endif // !MPT_COMPILER_QUIRK_NO_WCHAR #define MPT_UFORMAT_MESSAGE(f) mpt::format_message_typed(MPT_ULITERAL(f)) #define MPT_LFORMAT_MESSAGE(f) mpt::format_message_typed(f) #if MPT_OS_WINDOWS #define MPT_TFORMAT_MESSAGE(f) mpt::format_message_typed(TEXT(f)) #endif // MPT_OS_WINDOWS #if MPT_DETECTED_MFC #define MPT_CWFORMAT_MESSAGE(f) mpt::format_message_typed(L##f) #define MPT_CAFORMAT_MESSAGE(f) mpt::format_message_typed(f) #define MPT_CFORMAT_MESSAGE(f) mpt::format_message_typed(TEXT(f)) #endif // MPT_DETECTED_MFC #endif // MPT_FORMAT_MESSAGE_MACROS_HPP libopenmpt-0.6.1+release.autotools/src/mpt/format/simple.hpp0000644000175000017500000001473414044173026021147 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_FORMAT_SIMPLE_HPP #define MPT_FORMAT_SIMPLE_HPP #include "mpt/base/namespace.hpp" #include "mpt/base/pointer.hpp" #include "mpt/format/default_formatter.hpp" #include "mpt/format/simple_floatingpoint.hpp" #include "mpt/format/simple_integer.hpp" #include "mpt/format/simple_spec.hpp" #include "mpt/string/utility.hpp" #include #include namespace mpt { inline namespace MPT_INLINE_NS { template struct format : format_simple_base { template static inline Tstring val(const T & x) { return mpt::default_formatter::format(x); } template static inline Tstring fmt(const T & x, const format_simple_spec & f) { return mpt::format_simple(x, f); } template static inline Tstring dec(const T & x) { static_assert(std::numeric_limits::is_integer); return mpt::format_simple(x, format_simple_spec().BaseDec().FillOff()); } template static inline Tstring dec0(const T & x) { static_assert(std::numeric_limits::is_integer); return mpt::format_simple(x, format_simple_spec().BaseDec().FillNul().Width(width)); } template static inline Tstring dec(unsigned int g, char s, const T & x) { static_assert(std::numeric_limits::is_integer); return mpt::format_simple(x, format_simple_spec().BaseDec().FillOff().Group(g).GroupSep(s)); } template static inline Tstring dec0(unsigned int g, char s, const T & x) { static_assert(std::numeric_limits::is_integer); return mpt::format_simple(x, format_simple_spec().BaseDec().FillNul().Width(width).Group(g).GroupSep(s)); } template static inline Tstring hex(const T & x) { static_assert(std::numeric_limits::is_integer); return mpt::format_simple(x, format_simple_spec().BaseHex().CaseLow().FillOff()); } template static inline Tstring HEX(const T & x) { static_assert(std::numeric_limits::is_integer); return mpt::format_simple(x, format_simple_spec().BaseHex().CaseUpp().FillOff()); } template static inline Tstring hex0(const T & x) { static_assert(std::numeric_limits::is_integer); return mpt::format_simple(x, format_simple_spec().BaseHex().CaseLow().FillNul().Width(width)); } template static inline Tstring HEX0(const T & x) { static_assert(std::numeric_limits::is_integer); return mpt::format_simple(x, format_simple_spec().BaseHex().CaseUpp().FillNul().Width(width)); } template static inline Tstring hex(unsigned int g, char s, const T & x) { static_assert(std::numeric_limits::is_integer); return mpt::format_simple(x, format_simple_spec().BaseHex().CaseLow().FillOff().Group(g).GroupSep(s)); } template static inline Tstring HEX(unsigned int g, char s, const T & x) { static_assert(std::numeric_limits::is_integer); return mpt::format_simple(x, format_simple_spec().BaseHex().CaseUpp().FillOff().Group(g).GroupSep(s)); } template static inline Tstring hex0(unsigned int g, char s, const T & x) { static_assert(std::numeric_limits::is_integer); return mpt::format_simple(x, format_simple_spec().BaseHex().CaseLow().FillNul().Width(width).Group(g).GroupSep(s)); } template static inline Tstring HEX0(unsigned int g, char s, const T & x) { static_assert(std::numeric_limits::is_integer); return mpt::format_simple(x, format_simple_spec().BaseHex().CaseUpp().FillNul().Width(width).Group(g).GroupSep(s)); } template static inline Tstring flt(const T & x, int precision = -1) { static_assert(std::is_floating_point::value); return mpt::format_simple(x, format_simple_spec().NotaNrm().FillOff().Precision(precision)); } template static inline Tstring fix(const T & x, int precision = -1) { static_assert(std::is_floating_point::value); return mpt::format_simple(x, format_simple_spec().NotaFix().FillOff().Precision(precision)); } template static inline Tstring sci(const T & x, int precision = -1) { static_assert(std::is_floating_point::value); return mpt::format_simple(x, format_simple_spec().NotaSci().FillOff().Precision(precision)); } template static inline Tstring ptr(const T & x) { static_assert(std::is_pointer::value || std::is_same::value || std::is_same::value, ""); return hex0(mpt::pointer_cast(x)); } template static inline Tstring PTR(const T & x) { static_assert(std::is_pointer::value || std::is_same::value || std::is_same::value, ""); return HEX0(mpt::pointer_cast(x)); } static inline Tstring pad_left(std::size_t width_, const Tstring & str) { typedef mpt::string_traits traits; typename traits::size_type width = static_cast(width_); return traits::pad(str, width, 0); } static inline Tstring pad_right(std::size_t width_, const Tstring & str) { typedef mpt::string_traits traits; typename traits::size_type width = static_cast(width_); return traits::pad(str, 0, width); } static inline Tstring left(std::size_t width_, const Tstring & str) { typedef mpt::string_traits traits; typename traits::size_type width = static_cast(width_); return (traits::length(str) < width) ? traits::pad(str, 0, width - traits::length(str)) : str; } static inline Tstring right(std::size_t width_, const Tstring & str) { typedef mpt::string_traits traits; typename traits::size_type width = static_cast(width_); return (traits::length(str) < width) ? traits::pad(str, width - traits::length(str), 0) : str; } static inline Tstring center(std::size_t width_, const Tstring & str) { typedef mpt::string_traits traits; typename traits::size_type width = static_cast(width_); return (traits::length(str) < width) ? traits::pad(str, (width - traits::length(str)) / 2, (width - traits::length(str) + 1) / 2) : str; } }; // struct format } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_FORMAT_SIMPLE_HPP libopenmpt-0.6.1+release.autotools/src/mpt/format/simple_floatingpoint.hpp0000644000175000017500000001757614174570175024125 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_FORMAT_SIMPLE_FLOATINGPOINT_HPP #define MPT_FORMAT_SIMPLE_FLOATINGPOINT_HPP #include "mpt/base/detect.hpp" #if MPT_MSVC_AT_LEAST(2019, 4) || MPT_GCC_AT_LEAST(11, 1, 0) #define MPT_FORMAT_FORMAT_SIMPLE_FLOAT_CXX17 1 #else #define MPT_FORMAT_FORMAT_SIMPLE_FLOAT_CXX17 0 #endif #if MPT_FORMAT_FORMAT_SIMPLE_FLOAT_CXX17 #include "mpt/base/algorithm.hpp" #endif #include "mpt/base/namespace.hpp" #include "mpt/format/default_floatingpoint.hpp" #include "mpt/format/helpers.hpp" #include "mpt/format/simple_spec.hpp" #include "mpt/string/types.hpp" #include "mpt/string_transcode/transcode.hpp" #if MPT_FORMAT_FORMAT_SIMPLE_FLOAT_CXX17 #include #endif #if !MPT_FORMAT_FORMAT_SIMPLE_FLOAT_CXX17 #include #include #endif #if MPT_FORMAT_FORMAT_SIMPLE_FLOAT_CXX17 #include #endif #if !MPT_FORMAT_FORMAT_SIMPLE_FLOAT_CXX17 #include #include #include #endif #include #if MPT_FORMAT_FORMAT_SIMPLE_FLOAT_CXX17 #include #endif #include namespace mpt { inline namespace MPT_INLINE_NS { #if MPT_FORMAT_FORMAT_SIMPLE_FLOAT_CXX17 template inline Tstring format_simple_floatingpoint_to_chars(const T & x, std::chars_format fmt) { std::string str(1, '\0'); bool done = false; while (!done) { std::to_chars_result result = std::to_chars(str.data(), str.data() + str.size(), x, fmt); if (result.ec != std::errc{}) { str.resize(mpt::exponential_grow(str.size()), '\0'); } else { str.resize(result.ptr - str.data()); done = true; } } return mpt::convert_formatted_simple(str); } template inline Tstring format_simple_floatingpoint_to_chars(const T & x, std::chars_format fmt, int precision) { std::string str(1, '\0'); bool done = false; while (!done) { std::to_chars_result result = std::to_chars(str.data(), str.data() + str.size(), x, fmt, precision); if (result.ec != std::errc{}) { str.resize(mpt::exponential_grow(str.size()), '\0'); } else { str.resize(result.ptr - str.data()); done = true; } } return mpt::convert_formatted_simple(str); } template inline Tstring format_simple_floatingpoint_postprocess_width(Tstring str, const format_simple_spec & format) { format_simple_flags f = format.GetFlags(); std::size_t width = format.GetWidth(); if (f & format_simple_base::FillNul) { auto pos = str.begin(); if (str.length() > 0) { if (str[0] == mpt::unsafe_char_convert('+')) { pos++; width++; } else if (str[0] == mpt::unsafe_char_convert('-')) { pos++; width++; } } if (str.length() - std::distance(str.begin(), pos) < width) { str.insert(pos, width - str.length() - std::distance(str.begin(), pos), '0'); } } else { if (str.length() < width) { str.insert(0, width - str.length(), ' '); } } return str; } template ::value, bool> = true> inline Tstring format_simple(const T & x, const format_simple_spec & f) { using format_string_type = typename mpt::select_format_string_type::type; if (f.GetPrecision() != -1) { if (f.GetFlags() & format_simple_base::NotaSci) { return mpt::transcode(mpt::format_simple_floatingpoint_postprocess_width(format_simple_floatingpoint_to_chars(x, std::chars_format::scientific, f.GetPrecision()), f)); } else if (f.GetFlags() & format_simple_base::NotaFix) { return mpt::transcode(mpt::format_simple_floatingpoint_postprocess_width(format_simple_floatingpoint_to_chars(x, std::chars_format::fixed, f.GetPrecision()), f)); } else { return mpt::transcode(mpt::format_simple_floatingpoint_postprocess_width(format_simple_floatingpoint_to_chars(x, std::chars_format::general, f.GetPrecision()), f)); } } else { if (f.GetFlags() & format_simple_base::NotaSci) { return mpt::transcode(mpt::format_simple_floatingpoint_postprocess_width(format_simple_floatingpoint_to_chars(x, std::chars_format::scientific), f)); } else if (f.GetFlags() & format_simple_base::NotaFix) { return mpt::transcode(mpt::format_simple_floatingpoint_postprocess_width(format_simple_floatingpoint_to_chars(x, std::chars_format::fixed), f)); } else { return mpt::transcode(mpt::format_simple_floatingpoint_postprocess_width(format_simple_floatingpoint_to_chars(x, std::chars_format::general), f)); } } } #else // !MPT_FORMAT_FORMAT_SIMPLE_FLOAT_CXX17 template struct NumPunct : std::numpunct { private: unsigned int group; char sep; public: NumPunct(unsigned int g, char s) : group(g) , sep(s) { } std::string do_grouping() const override { return std::string(1, static_cast(group)); } Tchar do_thousands_sep() const override { return static_cast(sep); } }; template inline void format_simple_floatingpoint_apply_stream_format(Tostream & o, const format_simple_spec & format, const T &) { if (format.GetGroup() > 0) { o.imbue(std::locale(o.getloc(), new NumPunct(format.GetGroup(), format.GetGroupSep()))); } format_simple_flags f = format.GetFlags(); std::size_t width = format.GetWidth(); int precision = format.GetPrecision(); if (precision != -1 && width != 0 && !(f & format_simple_base::NotaFix) && !(f & format_simple_base::NotaSci)) { // fixup: // precision behaves differently from .# // avoid default format when precision and width are set f &= ~format_simple_base::NotaNrm; f |= format_simple_base::NotaFix; } if (f & format_simple_base::BaseDec) { o << std::dec; } else if (f & format_simple_base::BaseHex) { o << std::hex; } if (f & format_simple_base::NotaNrm) { /*nothing*/ } else if (f & format_simple_base::NotaFix) { o << std::setiosflags(std::ios::fixed); } else if (f & format_simple_base::NotaSci) { o << std::setiosflags(std::ios::scientific); } if (f & format_simple_base::CaseLow) { o << std::nouppercase; } else if (f & format_simple_base::CaseUpp) { o << std::uppercase; } if (f & format_simple_base::FillOff) { /* nothing */ } else if (f & format_simple_base::FillNul) { o << std::setw(width) << std::setfill(typename Tostream::char_type('0')); } if (precision != -1) { o << std::setprecision(precision); } else { if constexpr (std::is_floating_point::value) { if (f & format_simple_base::NotaNrm) { o << std::setprecision(std::numeric_limits::max_digits10); } else if (f & format_simple_base::NotaFix) { o << std::setprecision(std::numeric_limits::digits10); } else if (f & format_simple_base::NotaSci) { o << std::setprecision(std::numeric_limits::max_digits10 - 1); } else { o << std::setprecision(std::numeric_limits::max_digits10); } } } } template inline Tstring format_simple_floatingpoint_stream(const T & x, const format_simple_spec & f) { using stream_char_type = typename mpt::select_format_char_type::type; std::basic_ostringstream s; s.imbue(std::locale::classic()); mpt::format_simple_floatingpoint_apply_stream_format(s, f, x); s << x; return mpt::convert_formatted_simple(s.str()); } template ::value, bool> = true> inline Tstring format_simple(const T & x, const format_simple_spec & format) { return mpt::transcode(mpt::format_simple_floatingpoint_stream::type>(x, format)); } #endif // MPT_FORMAT_FORMAT_SIMPLE_FLOAT_CXX17 } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_FORMAT_SIMPLE_FLOATINGPOINT_HPP libopenmpt-0.6.1+release.autotools/src/mpt/format/simple_integer.hpp0000644000175000017500000001035514164603406022662 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_FORMAT_SIMPLE_INTEGER_HPP #define MPT_FORMAT_SIMPLE_INTEGER_HPP #include "mpt/base/algorithm.hpp" #include "mpt/base/detect.hpp" #include "mpt/base/namespace.hpp" #include "mpt/format/helpers.hpp" #include "mpt/format/simple_spec.hpp" #include "mpt/string/types.hpp" #include #include #include #include #include namespace mpt { inline namespace MPT_INLINE_NS { template ::value, bool> = true> inline Tstring format_simple_integer_to_chars(const T & x, int base) { std::string str(1, '\0'); bool done = false; while (!done) { if constexpr (std::is_same::value) { std::to_chars_result result = std::to_chars(str.data(), str.data() + str.size(), static_cast(x), base); if (result.ec != std::errc{}) { str.resize(mpt::exponential_grow(str.size()), '\0'); } else { str.resize(result.ptr - str.data()); done = true; } } else { std::to_chars_result result = std::to_chars(str.data(), str.data() + str.size(), x, base); if (result.ec != std::errc{}) { str.resize(mpt::exponential_grow(str.size()), '\0'); } else { str.resize(result.ptr - str.data()); done = true; } } } return mpt::convert_formatted_simple(str); } template inline Tstring format_simple_integer_postprocess_case(Tstring str, const format_simple_spec & format) { format_simple_flags f = format.GetFlags(); if (f & format_simple_base::CaseUpp) { for (auto & c : str) { if (mpt::unsafe_char_convert('a') <= c && c <= mpt::unsafe_char_convert('z')) { c -= mpt::unsafe_char_convert('a') - mpt::unsafe_char_convert('A'); } } } return str; } template inline Tstring format_simple_integer_postprocess_digits(Tstring str, const format_simple_spec & format) { format_simple_flags f = format.GetFlags(); std::size_t width = format.GetWidth(); if (f & format_simple_base::FillNul) { auto pos = str.begin(); if (str.length() > 0) { if (str[0] == mpt::unsafe_char_convert('+')) { pos++; width++; } else if (str[0] == mpt::unsafe_char_convert('-')) { pos++; width++; } } if (str.length() < width) { str.insert(pos, width - str.length(), mpt::unsafe_char_convert('0')); } } return str; } #if MPT_COMPILER_MSVC #pragma warning(push) #pragma warning(disable : 4723) // potential divide by 0 #endif // MPT_COMPILER_MSVC template inline Tstring format_simple_integer_postprocess_group(Tstring str, const format_simple_spec & format) { if (format.GetGroup() > 0) { const unsigned int groupSize = format.GetGroup(); const char groupSep = format.GetGroupSep(); std::size_t len = str.length(); for (std::size_t n = 0; n < len; ++n) { if (n > 0 && (n % groupSize) == 0) { if (!(n == (len - 1) && (str[0] == mpt::unsafe_char_convert('+') || str[0] == mpt::unsafe_char_convert('-')))) { str.insert(str.begin() + (len - n), 1, mpt::unsafe_char_convert(groupSep)); } } } } return str; } #if MPT_COMPILER_MSVC #pragma warning(pop) #endif // MPT_COMPILER_MSVC template ::value, bool> = true> inline Tstring format_simple(const T & x, const format_simple_spec & format) { int base = 10; if (format.GetFlags() & format_simple_base::BaseDec) { base = 10; } if (format.GetFlags() & format_simple_base::BaseHex) { base = 16; } using format_string_type = typename mpt::select_format_string_type::type; return mpt::transcode(mpt::format_simple_integer_postprocess_group(mpt::format_simple_integer_postprocess_digits(mpt::format_simple_integer_postprocess_case(mpt::format_simple_integer_to_chars(x, base), format), format), format)); } } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_FORMAT_SIMPLE_INTEGER_HPP libopenmpt-0.6.1+release.autotools/src/mpt/format/simple_spec.hpp0000644000175000017500000001433614044173026022157 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_FORMAT_SIMPLE_SPEC_HPP #define MPT_FORMAT_SIMPLE_SPEC_HPP #include "mpt/base/macros.hpp" #include "mpt/base/namespace.hpp" #include namespace mpt { inline namespace MPT_INLINE_NS { struct format_simple_base { enum FormatFlagsEnum { BaseDec = 0x0001, // base 10 (integers only) // int+float BaseHex = 0x0002, // base 16 (integers only) // int+float CaseLow = 0x0010, // lower case hex digits // int+float CaseUpp = 0x0020, // upper case hex digits // int+float FillOff = 0x0100, // do not fill up width // int+float FillNul = 0x0400, // fill up width with zeros // int+float NotaNrm = 0x1000, // float: normal/default notation // float NotaFix = 0x2000, // float: fixed point notation // float NotaSci = 0x4000, // float: scientific notation // float }; }; // struct format_simple_base using format_simple_flags = unsigned int; static_assert(sizeof(format_simple_flags) >= sizeof(format_simple_base::FormatFlagsEnum)); class format_simple_spec { private: format_simple_flags flags; std::size_t width; // int+float int precision; // float unsigned int group; // int char group_sep; // int public: MPT_CONSTEXPRINLINE format_simple_spec() noexcept : flags(0), width(0), precision(-1), group(0), group_sep(',') { } MPT_CONSTEXPRINLINE format_simple_flags GetFlags() const noexcept { return flags; } MPT_CONSTEXPRINLINE std::size_t GetWidth() const noexcept { return width; } MPT_CONSTEXPRINLINE int GetPrecision() const noexcept { return precision; } MPT_CONSTEXPRINLINE unsigned int GetGroup() const noexcept { return group; } MPT_CONSTEXPRINLINE char GetGroupSep() const noexcept { return group_sep; } MPT_CONSTEXPRINLINE format_simple_spec & SetFlags(format_simple_flags f) noexcept { flags = f; return *this; } MPT_CONSTEXPRINLINE format_simple_spec & SetWidth(std::size_t w) noexcept { width = w; return *this; } MPT_CONSTEXPRINLINE format_simple_spec & SetPrecision(int p) noexcept { precision = p; return *this; } MPT_CONSTEXPRINLINE format_simple_spec & SetGroup(unsigned int g) noexcept { group = g; return *this; } MPT_CONSTEXPRINLINE format_simple_spec & SetGroupSep(char s) noexcept { group_sep = s; return *this; } public: MPT_CONSTEXPRINLINE format_simple_spec & BaseDec() noexcept { flags &= ~(format_simple_base::BaseDec | format_simple_base::BaseHex); flags |= format_simple_base::BaseDec; return *this; } MPT_CONSTEXPRINLINE format_simple_spec & BaseHex() noexcept { flags &= ~(format_simple_base::BaseDec | format_simple_base::BaseHex); flags |= format_simple_base::BaseHex; return *this; } MPT_CONSTEXPRINLINE format_simple_spec & CaseLow() noexcept { flags &= ~(format_simple_base::CaseLow | format_simple_base::CaseUpp); flags |= format_simple_base::CaseLow; return *this; } MPT_CONSTEXPRINLINE format_simple_spec & CaseUpp() noexcept { flags &= ~(format_simple_base::CaseLow | format_simple_base::CaseUpp); flags |= format_simple_base::CaseUpp; return *this; } MPT_CONSTEXPRINLINE format_simple_spec & FillOff() noexcept { flags &= ~(format_simple_base::FillOff | format_simple_base::FillNul); flags |= format_simple_base::FillOff; return *this; } MPT_CONSTEXPRINLINE format_simple_spec & FillNul() noexcept { flags &= ~(format_simple_base::FillOff | format_simple_base::FillNul); flags |= format_simple_base::FillNul; return *this; } MPT_CONSTEXPRINLINE format_simple_spec & NotaNrm() noexcept { flags &= ~(format_simple_base::NotaNrm | format_simple_base::NotaFix | format_simple_base::NotaSci); flags |= format_simple_base::NotaNrm; return *this; } MPT_CONSTEXPRINLINE format_simple_spec & NotaFix() noexcept { flags &= ~(format_simple_base::NotaNrm | format_simple_base::NotaFix | format_simple_base::NotaSci); flags |= format_simple_base::NotaFix; return *this; } MPT_CONSTEXPRINLINE format_simple_spec & NotaSci() noexcept { flags &= ~(format_simple_base::NotaNrm | format_simple_base::NotaFix | format_simple_base::NotaSci); flags |= format_simple_base::NotaSci; return *this; } MPT_CONSTEXPRINLINE format_simple_spec & Width(std::size_t w) noexcept { width = w; return *this; } MPT_CONSTEXPRINLINE format_simple_spec & Prec(int p) noexcept { precision = p; return *this; } MPT_CONSTEXPRINLINE format_simple_spec & Group(unsigned int g) noexcept { group = g; return *this; } MPT_CONSTEXPRINLINE format_simple_spec & GroupSep(char s) noexcept { group_sep = s; return *this; } public: MPT_CONSTEXPRINLINE format_simple_spec & Dec() noexcept { return BaseDec(); } MPT_CONSTEXPRINLINE format_simple_spec & Hex() noexcept { return BaseHex(); } MPT_CONSTEXPRINLINE format_simple_spec & Low() noexcept { return CaseLow(); } MPT_CONSTEXPRINLINE format_simple_spec & Upp() noexcept { return CaseUpp(); } MPT_CONSTEXPRINLINE format_simple_spec & Off() noexcept { return FillOff(); } MPT_CONSTEXPRINLINE format_simple_spec & Nul() noexcept { return FillNul(); } MPT_CONSTEXPRINLINE format_simple_spec & Nrm() noexcept { return NotaNrm(); } MPT_CONSTEXPRINLINE format_simple_spec & Fix() noexcept { return NotaFix(); } MPT_CONSTEXPRINLINE format_simple_spec & Sci() noexcept { return NotaSci(); } public: MPT_CONSTEXPRINLINE format_simple_spec & Decimal() noexcept { return BaseDec(); } MPT_CONSTEXPRINLINE format_simple_spec & Hexadecimal() noexcept { return BaseHex(); } MPT_CONSTEXPRINLINE format_simple_spec & Lower() noexcept { return CaseLow(); } MPT_CONSTEXPRINLINE format_simple_spec & Upper() noexcept { return CaseUpp(); } MPT_CONSTEXPRINLINE format_simple_spec & FillNone() noexcept { return FillOff(); } MPT_CONSTEXPRINLINE format_simple_spec & FillZero() noexcept { return FillNul(); } MPT_CONSTEXPRINLINE format_simple_spec & FloatNormal() noexcept { return NotaNrm(); } MPT_CONSTEXPRINLINE format_simple_spec & FloatFixed() noexcept { return NotaFix(); } MPT_CONSTEXPRINLINE format_simple_spec & FloatScientific() noexcept { return NotaSci(); } MPT_CONSTEXPRINLINE format_simple_spec & Precision(int p) noexcept { return Prec(p); } }; } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_FORMAT_SIMPLE_SPEC_HPP libopenmpt-0.6.1+release.autotools/src/mpt/io/0000755000175000017500000000000014175541574016347 500000000000000libopenmpt-0.6.1+release.autotools/src/mpt/io/tests/0000755000175000017500000000000014175541575017512 500000000000000libopenmpt-0.6.1+release.autotools/src/mpt/io/tests/tests_io.hpp0000644000175000017500000004543314056456107021777 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_IO_TESTS_IO_HPP #define MPT_IO_TESTS_IO_HPP #include "mpt/base/integer.hpp" #include "mpt/base/namespace.hpp" #include "mpt/base/utility.hpp" #include "mpt/endian/integer.hpp" #include "mpt/io/base.hpp" #include "mpt/io/io.hpp" #include "mpt/io/io_stdstream.hpp" #include "mpt/test/test.hpp" #include "mpt/test/test_macros.hpp" #include #include #include #include #include namespace mpt { inline namespace MPT_INLINE_NS { namespace tests { namespace io { #if MPT_COMPILER_CLANG #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wglobal-constructors" #endif MPT_TEST_GROUP_INLINE("mpt/io") #if MPT_COMPILER_CLANG #pragma clang diagnostic pop #endif { // check that empty stringstream behaves correctly with our MSVC workarounds when using iostream interface directly { std::ostringstream ss; MPT_TEST_EXPECT_EQUAL(ss.tellp(), std::streampos(0)); } { std::ostringstream ss; ss.seekp(0); MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekAbsolute(ss, 0), true); } { std::ostringstream ss; ss.seekp(0, std::ios_base::beg); MPT_TEST_EXPECT_EQUAL(!ss.fail(), true); } { std::ostringstream ss; ss.seekp(0, std::ios_base::cur); MPT_TEST_EXPECT_EQUAL(!ss.fail(), true); } { std::istringstream ss; MPT_TEST_EXPECT_EQUAL(ss.tellg(), std::streampos(0)); } { std::istringstream ss; ss.seekg(0); MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekAbsolute(ss, 0), true); } { std::istringstream ss; ss.seekg(0, std::ios_base::beg); MPT_TEST_EXPECT_EQUAL(!ss.fail(), true); } { std::istringstream ss; ss.seekg(0, std::ios_base::cur); MPT_TEST_EXPECT_EQUAL(!ss.fail(), true); } { std::ostringstream s; char b = 23; MPT_TEST_EXPECT_EQUAL(!s.fail(), true); MPT_TEST_EXPECT_EQUAL(s.tellp(), std::streampos(0)); MPT_TEST_EXPECT_EQUAL(!s.fail(), true); s.seekp(0, std::ios_base::beg); MPT_TEST_EXPECT_EQUAL(!s.fail(), true); MPT_TEST_EXPECT_EQUAL(s.tellp(), std::streampos(0)); MPT_TEST_EXPECT_EQUAL(!s.fail(), true); s.write(&b, 1); MPT_TEST_EXPECT_EQUAL(!s.fail(), true); MPT_TEST_EXPECT_EQUAL(s.tellp(), std::streampos(1)); MPT_TEST_EXPECT_EQUAL(!s.fail(), true); s.seekp(0, std::ios_base::beg); MPT_TEST_EXPECT_EQUAL(!s.fail(), true); MPT_TEST_EXPECT_EQUAL(s.tellp(), std::streampos(0)); MPT_TEST_EXPECT_EQUAL(!s.fail(), true); s.seekp(0, std::ios_base::end); MPT_TEST_EXPECT_EQUAL(!s.fail(), true); MPT_TEST_EXPECT_EQUAL(s.tellp(), std::streampos(1)); MPT_TEST_EXPECT_EQUAL(!s.fail(), true); MPT_TEST_EXPECT_EQUAL(s.str(), std::string(1, b)); } { std::istringstream s; MPT_TEST_EXPECT_EQUAL(!s.fail(), true); MPT_TEST_EXPECT_EQUAL(s.tellg(), std::streampos(0)); MPT_TEST_EXPECT_EQUAL(!s.fail(), true); s.seekg(0, std::ios_base::beg); MPT_TEST_EXPECT_EQUAL(!s.fail(), true); MPT_TEST_EXPECT_EQUAL(s.tellg(), std::streampos(0)); MPT_TEST_EXPECT_EQUAL(!s.fail(), true); s.seekg(0, std::ios_base::end); MPT_TEST_EXPECT_EQUAL(!s.fail(), true); MPT_TEST_EXPECT_EQUAL(s.tellg(), std::streampos(0)); MPT_TEST_EXPECT_EQUAL(!s.fail(), true); } { std::istringstream s("a"); char a = 0; MPT_TEST_EXPECT_EQUAL(!s.fail(), true); MPT_TEST_EXPECT_EQUAL(s.tellg(), std::streampos(0)); MPT_TEST_EXPECT_EQUAL(!s.fail(), true); s.seekg(0, std::ios_base::beg); MPT_TEST_EXPECT_EQUAL(!s.fail(), true); MPT_TEST_EXPECT_EQUAL(s.tellg(), std::streampos(0)); MPT_TEST_EXPECT_EQUAL(!s.fail(), true); s.read(&a, 1); MPT_TEST_EXPECT_EQUAL(a, 'a'); MPT_TEST_EXPECT_EQUAL(!s.fail(), true); MPT_TEST_EXPECT_EQUAL(s.tellg(), std::streampos(1)); MPT_TEST_EXPECT_EQUAL(!s.fail(), true); s.seekg(0, std::ios_base::beg); MPT_TEST_EXPECT_EQUAL(!s.fail(), true); MPT_TEST_EXPECT_EQUAL(s.tellg(), std::streampos(0)); MPT_TEST_EXPECT_EQUAL(!s.fail(), true); s.seekg(0, std::ios_base::end); MPT_TEST_EXPECT_EQUAL(!s.fail(), true); MPT_TEST_EXPECT_EQUAL(s.tellg(), std::streampos(1)); MPT_TEST_EXPECT_EQUAL(!s.fail(), true); MPT_TEST_EXPECT_EQUAL(std::string(1, a), std::string(1, 'a')); } // check that empty native and fixed stringstream both behaves correctly with out IO functions { std::ostringstream ss; MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(ss), 0); } { std::ostringstream ss; MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(ss), true); } { std::ostringstream ss; MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekAbsolute(ss, 0), true); } { std::ostringstream ss; MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekRelative(ss, 0), true); } { std::istringstream ss; MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(ss), 0); } { std::istringstream ss; MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(ss), true); } { std::istringstream ss; MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekAbsolute(ss, 0), true); } { std::istringstream ss; MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekRelative(ss, 0), true); } { std::ostringstream ss; MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(ss), 0); } { std::ostringstream ss; MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(ss), true); } { std::ostringstream ss; MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekAbsolute(ss, 0), true); } { std::ostringstream ss; MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekRelative(ss, 0), true); } { std::istringstream ss; MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(ss), 0); } { std::istringstream ss; MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(ss), true); } { std::istringstream ss; MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekAbsolute(ss, 0), true); } { std::istringstream ss; MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekRelative(ss, 0), true); } { std::ostringstream s; char b = 23; MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(s), 0); MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(s), 0); MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::WriteRaw(s, &b, 1), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(s), 1); MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(s), 0); MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekEnd(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(s), 1); MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true); MPT_TEST_EXPECT_EQUAL(s.str(), std::string(1, b)); } { std::istringstream s; MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 0); MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 0); MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekEnd(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 0); MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true); } { std::istringstream s("a"); char a = 0; MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 0); MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 0); MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::ReadRaw(s, &a, 1).size(), 1u); MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 1); MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 0); MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekEnd(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 1); MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true); MPT_TEST_EXPECT_EQUAL(std::string(1, a), std::string(1, 'a')); } { std::ostringstream s; char b = 23; MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(s), 0); MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(s), 0); MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::WriteRaw(s, &b, 1), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(s), 1); MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(s), 0); MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekEnd(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(s), 1); MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true); MPT_TEST_EXPECT_EQUAL(s.str(), std::string(1, b)); } { std::istringstream s; MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 0); MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 0); MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekEnd(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 0); MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true); } { std::istringstream s("a"); char a = 0; MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 0); MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 0); MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::ReadRaw(s, &a, 1).size(), 1u); MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 1); MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 0); MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekEnd(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 1); MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true); MPT_TEST_EXPECT_EQUAL(std::string(1, a), std::string(1, 'a')); } // General file I/O tests { // Verify that writing arrays does not confuse the compiler. // This is both, compile-time and run-time cheking. // Run-time in case some weird compiler gets confused by our templates // and only writes the first array element. std::ostringstream f; uint16be data[2]; mpt::reset(data); data[0] = 0x1234; data[1] = 0x5678; mpt::IO::Write(f, data); MPT_TEST_EXPECT_EQUAL(f.str(), std::string("\x12\x34\x56\x78")); } { std::ostringstream f; std::vector data; data.resize(3); data[0] = 0x1234; data[1] = 0x5678; data[2] = 0x1234; mpt::IO::Write(f, data); MPT_TEST_EXPECT_EQUAL(f.str(), std::string("\x12\x34\x56\x78\x12\x34")); } { std::ostringstream f; int16be data[3]; mpt::reset(data); data[0] = 0x1234; data[1] = 0x5678; data[2] = 0x1234; mpt::IO::Write(f, data); MPT_TEST_EXPECT_EQUAL(f.str(), std::string("\x12\x34\x56\x78\x12\x34")); } { auto TestAdaptive16 = [&](uint16 value, mpt::IO::Offset expected_size, std::size_t fixedSize, const char * bytes) { std::stringstream f; MPT_TEST_EXPECT_EQUAL(mpt::IO::WriteAdaptiveInt16LE(f, value, fixedSize), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(f), expected_size); if (bytes) { mpt::IO::SeekBegin(f); for (mpt::IO::Offset i = 0; i < expected_size; ++i) { uint8 val = 0; mpt::IO::ReadIntLE(f, val); MPT_TEST_EXPECT_EQUAL(val, static_cast(bytes[i])); } } mpt::IO::SeekBegin(f); uint16 result = 0; MPT_TEST_EXPECT_EQUAL(mpt::IO::ReadAdaptiveInt16LE(f, result), true); MPT_TEST_EXPECT_EQUAL(result, value); }; auto TestAdaptive32 = [&](uint32 value, mpt::IO::Offset expected_size, std::size_t fixedSize, const char * bytes) { std::stringstream f; MPT_TEST_EXPECT_EQUAL(mpt::IO::WriteAdaptiveInt32LE(f, value, fixedSize), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(f), expected_size); if (bytes) { mpt::IO::SeekBegin(f); for (mpt::IO::Offset i = 0; i < expected_size; ++i) { uint8 val = 0; mpt::IO::ReadIntLE(f, val); MPT_TEST_EXPECT_EQUAL(val, static_cast(bytes[i])); } } mpt::IO::SeekBegin(f); uint32 result = 0; MPT_TEST_EXPECT_EQUAL(mpt::IO::ReadAdaptiveInt32LE(f, result), true); MPT_TEST_EXPECT_EQUAL(result, value); }; auto TestAdaptive64 = [&](uint64 value, mpt::IO::Offset expected_size, std::size_t fixedSize, const char * bytes) { std::stringstream f; MPT_TEST_EXPECT_EQUAL(mpt::IO::WriteAdaptiveInt64LE(f, value, fixedSize), true); MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(f), expected_size); if (bytes) { mpt::IO::SeekBegin(f); for (mpt::IO::Offset i = 0; i < expected_size; ++i) { uint8 val = 0; mpt::IO::ReadIntLE(f, val); MPT_TEST_EXPECT_EQUAL(val, static_cast(bytes[i])); } } mpt::IO::SeekBegin(f); uint64 result = 0; MPT_TEST_EXPECT_EQUAL(mpt::IO::ReadAdaptiveInt64LE(f, result), true); MPT_TEST_EXPECT_EQUAL(result, value); }; TestAdaptive16(0, 1, 0, "\x00"); TestAdaptive16(1, 1, 0, "\x02"); TestAdaptive16(2, 1, 0, nullptr); TestAdaptive16(0x7f, 1, 0, nullptr); TestAdaptive16(0x80, 2, 0, "\x01\x01"); TestAdaptive16(0x81, 2, 0, "\x03\x01"); TestAdaptive16(0x7fff, 2, 0, "\xff\xff"); TestAdaptive16(0, 1, 1, nullptr); TestAdaptive16(1, 1, 1, nullptr); TestAdaptive16(2, 1, 1, nullptr); TestAdaptive16(0x7f, 1, 1, nullptr); TestAdaptive16(0x80, 2, 0, nullptr); TestAdaptive16(0x81, 2, 0, nullptr); TestAdaptive16(0x7fff, 2, 0, nullptr); TestAdaptive16(0, 2, 2, "\x01\x00"); TestAdaptive16(1, 2, 2, "\x03\x00"); TestAdaptive16(2, 2, 2, nullptr); TestAdaptive16(0x7f, 2, 2, nullptr); TestAdaptive16(0x80, 2, 2, nullptr); TestAdaptive16(0x81, 2, 2, nullptr); TestAdaptive16(0x7fff, 2, 2, nullptr); TestAdaptive32(0, 1, 0, "\x00"); TestAdaptive32(1, 1, 0, nullptr); TestAdaptive32(2, 1, 0, nullptr); TestAdaptive32(0x3f, 1, 0, nullptr); TestAdaptive32(0x40, 2, 0, "\x01\x01"); TestAdaptive32(0x41, 2, 0, "\x05\x01"); TestAdaptive32(0x7f, 2, 0, nullptr); TestAdaptive32(0x80, 2, 0, nullptr); TestAdaptive32(0x3fff, 2, 0, nullptr); TestAdaptive32(0x4000, 3, 0, "\x02\x00\x01"); TestAdaptive32(0x4001, 3, 0, nullptr); TestAdaptive32(0x3fffff, 3, 0, nullptr); TestAdaptive32(0x400000, 4, 0, "\x03\x00\x00\x01"); TestAdaptive32(0x400001, 4, 0, nullptr); TestAdaptive32(0x3fffffff, 4, 0, "\xff\xff\xff\xff"); TestAdaptive32(0, 2, 2, nullptr); TestAdaptive32(1, 2, 2, nullptr); TestAdaptive32(2, 2, 2, nullptr); TestAdaptive32(0x3f, 2, 2, nullptr); TestAdaptive32(0x40, 2, 2, nullptr); TestAdaptive32(0x41, 2, 2, nullptr); TestAdaptive32(0x7f, 2, 2, nullptr); TestAdaptive32(0x80, 2, 2, nullptr); TestAdaptive32(0x3fff, 2, 2, nullptr); TestAdaptive32(0, 3, 3, nullptr); TestAdaptive32(1, 3, 3, nullptr); TestAdaptive32(2, 3, 3, nullptr); TestAdaptive32(0x3f, 3, 3, nullptr); TestAdaptive32(0x40, 3, 3, nullptr); TestAdaptive32(0x41, 3, 3, nullptr); TestAdaptive32(0x7f, 3, 3, nullptr); TestAdaptive32(0x80, 3, 3, nullptr); TestAdaptive32(0x3fff, 3, 3, nullptr); TestAdaptive32(0x4000, 3, 3, nullptr); TestAdaptive32(0x4001, 3, 3, nullptr); TestAdaptive32(0x3fffff, 3, 3, nullptr); TestAdaptive32(0, 4, 4, nullptr); TestAdaptive32(1, 4, 4, nullptr); TestAdaptive32(2, 4, 4, nullptr); TestAdaptive32(0x3f, 4, 4, nullptr); TestAdaptive32(0x40, 4, 4, nullptr); TestAdaptive32(0x41, 4, 4, nullptr); TestAdaptive32(0x7f, 4, 4, nullptr); TestAdaptive32(0x80, 4, 4, nullptr); TestAdaptive32(0x3fff, 4, 4, nullptr); TestAdaptive32(0x4000, 4, 4, nullptr); TestAdaptive32(0x4001, 4, 4, nullptr); TestAdaptive32(0x3fffff, 4, 4, nullptr); TestAdaptive32(0x400000, 4, 4, nullptr); TestAdaptive32(0x400001, 4, 4, nullptr); TestAdaptive32(0x3fffffff, 4, 4, nullptr); TestAdaptive64(0, 1, 0, nullptr); TestAdaptive64(1, 1, 0, nullptr); TestAdaptive64(2, 1, 0, nullptr); TestAdaptive64(0x3f, 1, 0, nullptr); TestAdaptive64(0x40, 2, 0, nullptr); TestAdaptive64(0x41, 2, 0, nullptr); TestAdaptive64(0x7f, 2, 0, nullptr); TestAdaptive64(0x80, 2, 0, nullptr); TestAdaptive64(0x3fff, 2, 0, nullptr); TestAdaptive64(0x4000, 4, 0, nullptr); TestAdaptive64(0x4001, 4, 0, nullptr); TestAdaptive64(0x3fffff, 4, 0, nullptr); TestAdaptive64(0x400000, 4, 0, nullptr); TestAdaptive64(0x400001, 4, 0, nullptr); TestAdaptive64(0x3fffffff, 4, 0, nullptr); TestAdaptive64(0x40000000, 8, 0, nullptr); TestAdaptive64(0x40000001, 8, 0, nullptr); TestAdaptive64(0x3fffffffffffffffull, 8, 0, nullptr); TestAdaptive64(0, 2, 2, nullptr); TestAdaptive64(1, 2, 2, nullptr); TestAdaptive64(2, 2, 2, nullptr); TestAdaptive64(0x3f, 2, 2, nullptr); TestAdaptive64(0, 4, 4, nullptr); TestAdaptive64(1, 4, 4, nullptr); TestAdaptive64(2, 4, 4, nullptr); TestAdaptive64(0x3f, 4, 4, nullptr); TestAdaptive64(0x40, 4, 4, nullptr); TestAdaptive64(0x41, 4, 4, nullptr); TestAdaptive64(0x7f, 4, 4, nullptr); TestAdaptive64(0x80, 4, 4, nullptr); TestAdaptive64(0x3fff, 4, 4, nullptr); TestAdaptive64(0, 8, 8, nullptr); TestAdaptive64(1, 8, 8, nullptr); TestAdaptive64(2, 8, 8, nullptr); TestAdaptive64(0x3f, 8, 8, nullptr); TestAdaptive64(0x40, 8, 8, nullptr); TestAdaptive64(0x41, 8, 8, nullptr); TestAdaptive64(0x7f, 8, 8, nullptr); TestAdaptive64(0x80, 8, 8, nullptr); TestAdaptive64(0x3fff, 8, 8, nullptr); TestAdaptive64(0x4000, 8, 8, nullptr); TestAdaptive64(0x4001, 8, 8, nullptr); TestAdaptive64(0x3fffff, 8, 8, nullptr); TestAdaptive64(0x400000, 8, 8, nullptr); TestAdaptive64(0x400001, 8, 8, nullptr); TestAdaptive64(0x3fffffff, 8, 8, nullptr); TestAdaptive64(0x40000000, 8, 8, nullptr); TestAdaptive64(0x40000001, 8, 8, nullptr); TestAdaptive64(0x3fffffffffffffffull, 8, 8, nullptr); } } } // namespace io } // namespace tests } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_IO_TESTS_IO_HPP libopenmpt-0.6.1+release.autotools/src/mpt/io/base.hpp0000644000175000017500000000450214056206101017671 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_IO_BASE_HPP #define MPT_IO_BASE_HPP #include "mpt/base/integer.hpp" #include "mpt/base/namespace.hpp" #include namespace mpt { inline namespace MPT_INLINE_NS { namespace IO { using Offset = int64; inline constexpr std::size_t BUFFERSIZE_MINUSCULE = 1 * 256; // on stack usage, tuned for single word/line buffers inline constexpr std::size_t BUFFERSIZE_TINY = 1 * 1024; // on stack usage inline constexpr std::size_t BUFFERSIZE_SMALL = 4 * 1024; // on heap inline constexpr std::size_t BUFFERSIZE_NORMAL = 64 * 1024; // FILE I/O inline constexpr std::size_t BUFFERSIZE_LARGE = 1024 * 1024; template struct FileOperations { }; template inline FileOperations FileOps(Tfile & f) { ; return FileOperations{f}; } template inline bool IsValid(Tfile & f) { return FileOps(f).IsValid(); } template inline bool IsReadSeekable(Tfile & f) { return FileOps(f).IsReadSeekable(); } template inline bool IsWriteSeekable(Tfile & f) { return FileOps(f).IsWriteSeekable(); } template inline IO::Offset TellRead(Tfile & f) { return FileOps(f).TellRead(); } template inline IO::Offset TellWrite(Tfile & f) { return FileOps(f).TellWrite(); } template inline bool SeekBegin(Tfile & f) { return FileOps(f).SeekBegin(); } template inline bool SeekEnd(Tfile & f) { return FileOps(f).SeekEnd(); } template inline bool SeekAbsolute(Tfile & f, IO::Offset pos) { return FileOps(f).SeekAbsolute(pos); } template inline bool SeekRelative(Tfile & f, IO::Offset off) { return FileOps(f).SeekRelative(off); } template inline mpt::byte_span ReadRawImpl(Tfile & f, mpt::byte_span data) { return FileOps(f).ReadRawImpl(data); } template inline bool WriteRawImpl(Tfile & f, mpt::const_byte_span data) { return FileOps(f).WriteRawImpl(data); } template inline bool IsEof(Tfile & f) { return FileOps(f).IsEof(); } template inline bool Flush(Tfile & f) { return FileOps(f).Flush(); } } // namespace IO } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_IO_BASE_HPP libopenmpt-0.6.1+release.autotools/src/mpt/io/io.hpp0000644000175000017500000002545114101716665017410 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_IO_IO_HPP #define MPT_IO_IO_HPP #include "mpt/base/array.hpp" #include "mpt/base/bit.hpp" #include "mpt/base/integer.hpp" #include "mpt/base/memory.hpp" #include "mpt/base/namespace.hpp" #include "mpt/base/span.hpp" #include "mpt/endian/integer.hpp" #include "mpt/io/base.hpp" #include #include #include #include #include #include namespace mpt { inline namespace MPT_INLINE_NS { namespace IO { template inline mpt::byte_span ReadRaw(Tfile & f, Tbyte * data, std::size_t size) { return mpt::IO::ReadRawImpl(f, mpt::as_span(mpt::byte_cast(data), size)); } template inline mpt::byte_span ReadRaw(Tfile & f, mpt::span data) { return mpt::IO::ReadRawImpl(f, mpt::byte_cast(data)); } template inline bool WriteRaw(Tfile & f, const Tbyte * data, std::size_t size) { return mpt::IO::WriteRawImpl(f, mpt::as_span(mpt::byte_cast(data), size)); } template inline bool WriteRaw(Tfile & f, mpt::span data) { return mpt::IO::WriteRawImpl(f, mpt::byte_cast(data)); } template inline bool Read(Tfile & f, Tbinary & v) { return mpt::IO::ReadRaw(f, mpt::as_raw_memory(v)).size() == mpt::as_raw_memory(v).size(); } template inline bool Write(Tfile & f, const Tbinary & v) { return mpt::IO::WriteRaw(f, mpt::as_raw_memory(v)); } template inline bool Write(Tfile & f, const std::vector & v) { static_assert(mpt::is_binary_safe::value); return mpt::IO::WriteRaw(f, mpt::as_raw_memory(v)); } template inline bool WritePartial(Tfile & f, const T & v, std::size_t size = sizeof(T)) { assert(size <= sizeof(T)); return mpt::IO::WriteRaw(f, mpt::as_span(mpt::as_raw_memory(v).data(), size)); } template inline bool ReadByte(Tfile & f, std::byte & v) { bool result = false; std::byte byte = mpt::as_byte(0); const std::size_t readResult = mpt::IO::ReadRaw(f, &byte, sizeof(std::byte)).size(); result = (readResult == sizeof(std::byte)); v = byte; return result; } template inline bool ReadBinaryTruncatedLE(Tfile & f, T & v, std::size_t size) { bool result = false; static_assert(std::numeric_limits::is_integer); std::array bytes = mpt::init_array(uint8{0}); const std::size_t readResult = mpt::IO::ReadRaw(f, bytes.data(), std::min(size, sizeof(T))).size(); result = (readResult == std::min(size, sizeof(T))); v = mpt::bit_cast::type>(bytes); return result; } template inline bool ReadIntLE(Tfile & f, T & v) { bool result = false; static_assert(std::numeric_limits::is_integer); std::array bytes = mpt::init_array(uint8{0}); const std::size_t readResult = mpt::IO::ReadRaw(f, mpt::as_span(bytes)).size(); result = (readResult == sizeof(T)); v = mpt::bit_cast::type>(bytes); return result; } template inline bool ReadIntBE(Tfile & f, T & v) { bool result = false; static_assert(std::numeric_limits::is_integer); std::array bytes = mpt::init_array(uint8{0}); const std::size_t readResult = mpt::IO::ReadRaw(f, mpt::as_span(bytes)).size(); result = (readResult == sizeof(T)); v = mpt::bit_cast::type>(bytes); return result; } template inline bool ReadAdaptiveInt16LE(Tfile & f, uint16 & v) { bool result = true; uint8 byte = 0; std::size_t additionalBytes = 0; v = 0; byte = 0; if (!mpt::IO::ReadIntLE(f, byte)) { result = false; } additionalBytes = (byte & 0x01); v = byte >> 1; for (std::size_t i = 0; i < additionalBytes; ++i) { byte = 0; if (!mpt::IO::ReadIntLE(f, byte)) { result = false; } v |= (static_cast(byte) << (((i + 1) * 8) - 1)); } return result; } template inline bool ReadAdaptiveInt32LE(Tfile & f, uint32 & v) { bool result = true; uint8 byte = 0; std::size_t additionalBytes = 0; v = 0; byte = 0; if (!mpt::IO::ReadIntLE(f, byte)) { result = false; } additionalBytes = (byte & 0x03); v = byte >> 2; for (std::size_t i = 0; i < additionalBytes; ++i) { byte = 0; if (!mpt::IO::ReadIntLE(f, byte)) { result = false; } v |= (static_cast(byte) << (((i + 1) * 8) - 2)); } return result; } template inline bool ReadAdaptiveInt64LE(Tfile & f, uint64 & v) { bool result = true; uint8 byte = 0; std::size_t additionalBytes = 0; v = 0; byte = 0; if (!mpt::IO::ReadIntLE(f, byte)) { result = false; } additionalBytes = (1 << (byte & 0x03)) - 1; v = byte >> 2; for (std::size_t i = 0; i < additionalBytes; ++i) { byte = 0; if (!mpt::IO::ReadIntLE(f, byte)) { result = false; } v |= (static_cast(byte) << (((i + 1) * 8) - 2)); } return result; } template inline bool ReadSizedStringLE(Tfile & f, std::string & str, Tsize maxSize = std::numeric_limits::max()) { static_assert(std::numeric_limits::is_integer); str.clear(); Tsize size = 0; if (!mpt::IO::ReadIntLE(f, size)) { return false; } if (size > maxSize) { return false; } for (Tsize i = 0; i != size; ++i) { char c = '\0'; if (!mpt::IO::ReadIntLE(f, c)) { return false; } str.push_back(c); } return true; } template inline bool WriteIntLE(Tfile & f, const T v) { static_assert(std::numeric_limits::is_integer); return mpt::IO::Write(f, mpt::as_le(v)); } template inline bool WriteIntBE(Tfile & f, const T v) { static_assert(std::numeric_limits::is_integer); return mpt::IO::Write(f, mpt::as_be(v)); } template inline bool WriteAdaptiveInt16LE(Tfile & f, const uint16 v, std::size_t fixedSize = 0) { std::size_t minSize = fixedSize; std::size_t maxSize = fixedSize; assert(minSize == 0 || minSize == 1 || minSize == 2); assert(maxSize == 0 || maxSize == 1 || maxSize == 2); assert(maxSize == 0 || maxSize >= minSize); if (maxSize == 0) { maxSize = 2; } if (v < 0x80 && minSize <= 1 && 1 <= maxSize) { return mpt::IO::WriteIntLE(f, static_cast(v << 1) | 0x00); } else if (v < 0x8000 && minSize <= 2 && 2 <= maxSize) { return mpt::IO::WriteIntLE(f, static_cast(v << 1) | 0x01); } else { assert(false); return false; } } template inline bool WriteAdaptiveInt32LE(Tfile & f, const uint32 v, std::size_t fixedSize = 0) { std::size_t minSize = fixedSize; std::size_t maxSize = fixedSize; assert(minSize == 0 || minSize == 1 || minSize == 2 || minSize == 3 || minSize == 4); assert(maxSize == 0 || maxSize == 1 || maxSize == 2 || maxSize == 3 || maxSize == 4); assert(maxSize == 0 || maxSize >= minSize); if (maxSize == 0) { maxSize = 4; } if (v < 0x40 && minSize <= 1 && 1 <= maxSize) { return mpt::IO::WriteIntLE(f, static_cast(v << 2) | 0x00); } else if (v < 0x4000 && minSize <= 2 && 2 <= maxSize) { return mpt::IO::WriteIntLE(f, static_cast(v << 2) | 0x01); } else if (v < 0x400000 && minSize <= 3 && 3 <= maxSize) { uint32 value = static_cast(v << 2) | 0x02; std::byte bytes[3]; bytes[0] = static_cast(value >> 0); bytes[1] = static_cast(value >> 8); bytes[2] = static_cast(value >> 16); return mpt::IO::WriteRaw(f, bytes, 3); } else if (v < 0x40000000 && minSize <= 4 && 4 <= maxSize) { return mpt::IO::WriteIntLE(f, static_cast(v << 2) | 0x03); } else { assert(false); return false; } } template inline bool WriteAdaptiveInt64LE(Tfile & f, const uint64 v, std::size_t fixedSize = 0) { std::size_t minSize = fixedSize; std::size_t maxSize = fixedSize; assert(minSize == 0 || minSize == 1 || minSize == 2 || minSize == 4 || minSize == 8); assert(maxSize == 0 || maxSize == 1 || maxSize == 2 || maxSize == 4 || maxSize == 8); assert(maxSize == 0 || maxSize >= minSize); if (maxSize == 0) { maxSize = 8; } if (v < 0x40 && minSize <= 1 && 1 <= maxSize) { return mpt::IO::WriteIntLE(f, static_cast(v << 2) | 0x00); } else if (v < 0x4000 && minSize <= 2 && 2 <= maxSize) { return mpt::IO::WriteIntLE(f, static_cast(v << 2) | 0x01); } else if (v < 0x40000000 && minSize <= 4 && 4 <= maxSize) { return mpt::IO::WriteIntLE(f, static_cast(v << 2) | 0x02); } else if (v < 0x4000000000000000ull && minSize <= 8 && 8 <= maxSize) { return mpt::IO::WriteIntLE(f, static_cast(v << 2) | 0x03); } else { assert(false); return false; } } // Write a variable-length integer, as found in MIDI files. The number of written bytes is placed in the bytesWritten parameter. template bool WriteVarInt(Tfile & f, const T v, std::size_t * bytesWritten = nullptr) { static_assert(std::numeric_limits::is_integer); static_assert(!std::numeric_limits::is_signed); std::byte out[(sizeof(T) * 8 + 6) / 7]; std::size_t numBytes = 0; for (uint32 n = (sizeof(T) * 8) / 7; n > 0; n--) { if (v >= (static_cast(1) << (n * 7u))) { out[numBytes++] = static_cast(((v >> (n * 7u)) & 0x7F) | 0x80); } } out[numBytes++] = static_cast(v & 0x7F); assert(numBytes <= std::size(out)); if (bytesWritten != nullptr) { *bytesWritten = numBytes; } return mpt::IO::WriteRaw(f, out, numBytes); } template inline bool WriteSizedStringLE(Tfile & f, const std::string & str) { static_assert(std::numeric_limits::is_integer); if (str.size() > std::numeric_limits::max()) { return false; } Tsize size = static_cast(str.size()); if (!mpt::IO::WriteIntLE(f, size)) { return false; } if (!mpt::IO::WriteRaw(f, str.data(), str.size())) { return false; } return true; } template inline bool WriteText(Tfile & f, const std::string & s) { return mpt::IO::WriteRaw(f, s.data(), s.size()); } template inline bool WriteTextCRLF(Tfile & f) { return mpt::IO::WriteText(f, "\r\n"); } template inline bool WriteTextLF(Tfile & f) { return mpt::IO::WriteText(f, "\n"); } template inline bool WriteTextCRLF(Tfile & f, const std::string & s) { return mpt::IO::WriteText(f, s) && mpt::IO::WriteTextCRLF(f); } template inline bool WriteTextLF(Tfile & f, const std::string & s) { return mpt::IO::WriteText(f, s) && mpt::IO::WriteTextLF(f); } } // namespace IO } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_IO_IO_HPP libopenmpt-0.6.1+release.autotools/src/mpt/io/io_span.hpp0000644000175000017500000000516714056153222020424 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_IO_IO_SPAN_HPP #define MPT_IO_IO_SPAN_HPP #include "mpt/base/macros.hpp" #include "mpt/base/memory.hpp" #include "mpt/base/namespace.hpp" #include "mpt/base/saturate_cast.hpp" #include "mpt/base/span.hpp" #include "mpt/io/base.hpp" #include #include #include namespace mpt { inline namespace MPT_INLINE_NS { namespace IO { template struct FileOperations, IO::Offset>> { private: std::pair, IO::Offset> & f; public: FileOperations(std::pair, IO::Offset> & f_) : f(f_) { return; } public: inline bool IsValid() { return (f.second >= 0); } inline bool IsReadSeekable() { MPT_UNUSED(f); return true; } inline bool IsWriteSeekable() { MPT_UNUSED(f); return true; } inline IO::Offset TellRead() { return f.second; } inline IO::Offset TellWrite() { return f.second; } inline bool SeekBegin() { f.second = 0; return true; } inline bool SeekEnd() { f.second = f.first.size(); return true; } inline bool SeekAbsolute(IO::Offset pos) { f.second = pos; return true; } inline bool SeekRelative(IO::Offset off) { if (f.second < 0) { return false; } f.second += off; return true; } inline mpt::byte_span ReadRawImpl(mpt::byte_span data) { if (f.second < 0) { return data.first(0); } if (f.second >= static_cast(f.first.size())) { return data.first(0); } std::size_t num = mpt::saturate_cast(std::min(static_cast(f.first.size()) - f.second, static_cast(data.size()))); std::copy(mpt::byte_cast(f.first.data() + f.second), mpt::byte_cast(f.first.data() + f.second + num), data.data()); f.second += num; return data.first(num); } inline bool WriteRawImpl(mpt::const_byte_span data) { if (f.second < 0) { return false; } if (f.second > static_cast(f.first.size())) { return false; } std::size_t num = mpt::saturate_cast(std::min(static_cast(f.first.size()) - f.second, static_cast(data.size()))); if (num != data.size()) { return false; } std::copy(data.data(), data.data() + num, mpt::byte_cast(f.first.data() + f.second)); f.second += num; return true; } inline bool IsEof() { return (f.second >= static_cast(f.first.size())); } inline bool Flush() { MPT_UNUSED(f); return true; } }; } // namespace IO } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_IO_IO_SPAN_HPP libopenmpt-0.6.1+release.autotools/src/mpt/io/io_stdstream.hpp0000644000175000017500000001353214056153222021464 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_IO_IO_STDSTREAM_HPP #define MPT_IO_IO_STDSTREAM_HPP #include "mpt/base/macros.hpp" #include "mpt/base/memory.hpp" #include "mpt/base/namespace.hpp" #include "mpt/base/utility.hpp" #include "mpt/io/base.hpp" #include #include #include #include namespace mpt { inline namespace MPT_INLINE_NS { namespace IO { //static_assert(sizeof(std::streamoff) == 8); // Assert 64bit file support. struct FileOperationsStdIos { private: std::ios & f; public: FileOperationsStdIos(std::ios & f_) : f(f_) { return; } public: inline bool IsValid() { return !f.fail(); } }; struct FileOperationsStdIstream : public FileOperationsStdIos { private: std::istream & f; public: FileOperationsStdIstream(std::istream & f_) : FileOperationsStdIos(f_) , f(f_) { return; } public: inline bool IsReadSeekable() { f.clear(); std::streampos oldpos = f.tellg(); if (f.fail() || oldpos == std::streampos(-1)) { f.clear(); return false; } f.seekg(0, std::ios::beg); if (f.fail()) { f.clear(); f.seekg(oldpos); f.clear(); return false; } f.seekg(0, std::ios::end); if (f.fail()) { f.clear(); f.seekg(oldpos); f.clear(); return false; } std::streampos length = f.tellg(); if (f.fail() || length == std::streampos(-1)) { f.clear(); f.seekg(oldpos); f.clear(); return false; } f.seekg(oldpos); f.clear(); return true; } inline IO::Offset TellRead() { return f.tellg(); } inline bool SeekBegin() { f.seekg(0); return !f.fail(); } inline bool SeekEnd() { f.seekg(0, std::ios::end); return !f.fail(); } inline bool SeekAbsolute(IO::Offset pos) { if (!mpt::in_range(pos)) { return false; } f.seekg(static_cast(pos), std::ios::beg); return !f.fail(); } inline bool SeekRelative(IO::Offset off) { if (!mpt::in_range(off)) { return false; } f.seekg(static_cast(off), std::ios::cur); return !f.fail(); } inline mpt::byte_span ReadRawImpl(mpt::byte_span data) { f.read(mpt::byte_cast(data.data()), data.size()); return data.first(mpt::saturate_cast(f.gcount())); } inline bool IsEof() { return f.eof(); } }; struct FileOperationsStdOstream : public FileOperationsStdIos { private: std::ostream & f; public: FileOperationsStdOstream(std::ostream & f_) : FileOperationsStdIos(f_) , f(f_) { return; } public: inline bool IsWriteSeekable() { f.clear(); std::streampos oldpos = f.tellp(); if (f.fail() || oldpos == std::streampos(-1)) { f.clear(); return false; } f.seekp(0, std::ios::beg); if (f.fail()) { f.clear(); f.seekp(oldpos); f.clear(); return false; } f.seekp(0, std::ios::end); if (f.fail()) { f.clear(); f.seekp(oldpos); f.clear(); return false; } std::streampos length = f.tellp(); if (f.fail() || length == std::streampos(-1)) { f.clear(); f.seekp(oldpos); f.clear(); return false; } f.seekp(oldpos); f.clear(); return true; } inline IO::Offset TellWrite() { return f.tellp(); } inline bool SeekBegin() { f.seekp(0); return !f.fail(); } inline bool SeekEnd() { f.seekp(0, std::ios::end); return !f.fail(); } inline bool SeekAbsolute(IO::Offset pos) { if (!mpt::in_range(pos)) { return false; } f.seekp(static_cast(pos), std::ios::beg); return !f.fail(); } inline bool SeekRelative(IO::Offset off) { if (!mpt::in_range(off)) { return false; } f.seekp(static_cast(off), std::ios::cur); return !f.fail(); } inline bool WriteRawImpl(mpt::const_byte_span data) { f.write(mpt::byte_cast(data.data()), data.size()); return !f.fail(); } inline bool Flush() { f.flush(); return !f.fail(); } }; struct FileOperationsStdIOstream : public FileOperationsStdIstream , public FileOperationsStdOstream { private: std::iostream & f; public: FileOperationsStdIOstream(std::iostream & f_) : FileOperationsStdIstream(f_) , FileOperationsStdOstream(f_) , f(f_) { return; } public: inline bool SeekBegin() { FileOperationsStdIstream::SeekBegin(); FileOperationsStdOstream::SeekBegin(); return !f.fail(); } inline bool SeekEnd() { FileOperationsStdIstream::SeekEnd(); FileOperationsStdOstream::SeekEnd(); return !f.fail(); } inline bool SeekAbsolute(IO::Offset pos) { if (!mpt::in_range(pos)) { return false; } FileOperationsStdIstream::SeekAbsolute(pos); FileOperationsStdOstream::SeekAbsolute(pos); return !f.fail(); } inline bool SeekRelative(IO::Offset off) { if (!mpt::in_range(off)) { return false; } FileOperationsStdIstream::SeekRelative(off); FileOperationsStdOstream::SeekRelative(off); return !f.fail(); } }; template struct FileOperations::value>> : public FileOperationsStdIOstream { public: FileOperations(Tstream & f) : FileOperationsStdIOstream(f) { return; } }; template struct FileOperations::value && !std::is_base_of::value>> : public FileOperationsStdIstream { public: FileOperations(Tstream & f) : FileOperationsStdIstream(f) { return; } }; template struct FileOperations::value && !std::is_base_of::value>> : public FileOperationsStdOstream { public: FileOperations(Tstream & f) : FileOperationsStdOstream(f) { return; } }; } // namespace IO } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_IO_IO_STDSTREAM_HPP libopenmpt-0.6.1+release.autotools/src/mpt/io/io_virtual_wrapper.hpp0000644000175000017500000001641014056153222022702 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_IO_IO_VIRTUAL_WRAPPER_HPP #define MPT_IO_IO_VIRTUAL_WRAPPER_HPP #include "mpt/base/memory.hpp" #include "mpt/base/namespace.hpp" #include "mpt/io/base.hpp" #include namespace mpt { inline namespace MPT_INLINE_NS { namespace IO { class IFileBase { protected: IFileBase() = default; virtual ~IFileBase() = default; public: virtual bool IsValid() = 0; virtual bool IsReadSeekable() = 0; virtual IO::Offset TellRead() = 0; virtual bool SeekBegin() = 0; virtual bool SeekEnd() = 0; virtual bool SeekAbsolute(IO::Offset pos) = 0; virtual bool SeekRelative(IO::Offset off) = 0; virtual mpt::byte_span ReadRawImpl(mpt::byte_span data) = 0; virtual bool IsEof() = 0; }; template class IFile : public IFileBase { private: Tfile & f; public: IFile(Tfile & f_) : f(f_) { } ~IFile() override = default; public: bool IsValid() override { return mpt::IO::IsValid(f); } bool IsReadSeekable() override { return mpt::IO::IsReadSeekable(f); } IO::Offset TellRead() override { return mpt::IO::TellRead(f); } bool SeekBegin() override { return mpt::IO::SeekBegin(f); } bool SeekEnd() override { return mpt::IO::SeekEnd(f); } bool SeekAbsolute(IO::Offset pos) override { return mpt::IO::SeekAbsolute(f, pos); } bool SeekRelative(IO::Offset off) override { return mpt::IO::SeekRelative(f, off); } mpt::byte_span ReadRawImpl(mpt::byte_span data) override { return mpt::IO::ReadRawImpl(f, data); } bool IsEof() override { return mpt::IO::IsEof(f); } }; class OFileBase { protected: OFileBase() = default; virtual ~OFileBase() = default; public: virtual bool IsValid() = 0; virtual bool IsWriteSeekable() = 0; virtual IO::Offset TellWrite() = 0; virtual bool SeekBegin() = 0; virtual bool SeekEnd() = 0; virtual bool SeekAbsolute(IO::Offset pos) = 0; virtual bool SeekRelative(IO::Offset off) = 0; virtual bool WriteRawImpl(mpt::const_byte_span data) = 0; virtual bool Flush() = 0; }; template class OFile : public OFileBase { private: Tfile & f; public: OFile(Tfile & f_) : f(f_) { } ~OFile() override = default; public: bool IsValid() override { return mpt::IO::IsValid(f); } bool IsWriteSeekable() override { return mpt::IO::IsWriteSeekable(f); } IO::Offset TellWrite() override { return mpt::IO::TellWrite(f); } bool SeekBegin() override { return mpt::IO::SeekBegin(f); } bool SeekEnd() override { return mpt::IO::SeekEnd(f); } bool SeekAbsolute(IO::Offset pos) override { return mpt::IO::SeekAbsolute(f, pos); } bool SeekRelative(IO::Offset off) override { return mpt::IO::SeekRelative(f, off); } bool WriteRawImpl(mpt::const_byte_span data) override { return mpt::IO::WriteRawImpl(f, data); } bool Flush() override { return mpt::IO::Flush(f); } }; class IOFileBase { protected: IOFileBase() = default; virtual ~IOFileBase() = default; public: virtual bool IsValid() = 0; virtual bool IsReadSeekable() = 0; virtual bool IsWriteSeekable() = 0; virtual IO::Offset TellRead() = 0; virtual IO::Offset TellWrite() = 0; virtual bool SeekBegin() = 0; virtual bool SeekEnd() = 0; virtual bool SeekAbsolute(IO::Offset pos) = 0; virtual bool SeekRelative(IO::Offset off) = 0; virtual mpt::byte_span ReadRawImpl(mpt::byte_span data) = 0; virtual bool WriteRawImpl(mpt::const_byte_span data) = 0; virtual bool IsEof() = 0; virtual bool Flush() = 0; }; template class IOFile : public IOFileBase { private: Tfile & f; public: IOFile(Tfile & f_) : f(f_) { } ~IOFile() override = default; public: bool IsValid() override { return mpt::IO::IsValid(f); } bool IsReadSeekable() override { return mpt::IO::IsReadSeekable(f); } bool IsWriteSeekable() override { return mpt::IO::IsWriteSeekable(f); } IO::Offset TellRead() override { return mpt::IO::TellRead(f); } IO::Offset TellWrite() override { return mpt::IO::TellWrite(f); } bool SeekBegin() override { return mpt::IO::SeekBegin(f); } bool SeekEnd() override { return mpt::IO::SeekEnd(f); } bool SeekAbsolute(IO::Offset pos) override { return mpt::IO::SeekAbsolute(f, pos); } bool SeekRelative(IO::Offset off) override { return mpt::IO::SeekRelative(f, off); } mpt::byte_span ReadRawImpl(mpt::byte_span data) override { return mpt::IO::ReadRawImpl(f, data); } bool WriteRawImpl(mpt::const_byte_span data) override { return mpt::IO::WriteRawImpl(f, data); } bool IsEof() override { return mpt::IO::IsEof(f); } bool Flush() override { return mpt::IO::Flush(f); } }; template struct FileOperations::value>> { private: IFileBase & f; public: FileOperations(IFileBase & f_) : f(f_) { return; } public: inline bool IsValid() { return f.IsValid(); } inline bool IsReadSeekable() { return f.IsReadSeekable(); } inline IO::Offset TellRead() { return f.TellRead(); } inline bool SeekBegin() { return f.SeekBegin(); } inline bool SeekEnd() { return f.SeekEnd(); } inline bool SeekAbsolute(IO::Offset pos) { return f.SeekAbsolute(pos); } inline bool SeekRelative(IO::Offset off) { return f.SeekRelative(off); } inline mpt::byte_span ReadRawImpl(mpt::byte_span data) { return f.ReadRawImpl(data); } inline bool IsEof() { return f.IsEof(); } }; template struct FileOperations::value>> { private: OFileBase & f; public: FileOperations(OFileBase & f_) : f(f_) { return; } public: inline bool IsValid() { return f.IsValid(); } inline bool IsWriteSeekable() { return f.IsWriteSeekable(); } inline IO::Offset TellWrite() { return f.TellWrite(); } inline bool SeekBegin() { return f.SeekBegin(); } inline bool SeekEnd() { return f.SeekEnd(); } inline bool SeekAbsolute(IO::Offset pos) { return f.SeekAbsolute(pos); } inline bool SeekRelative(IO::Offset off) { return f.SeekRelative(off); } inline bool WriteRawImpl(mpt::const_byte_span data) { return f.WriteRawImpl(data); } inline bool Flush() { return f.Flush(); } }; template struct FileOperations::value>> { private: IOFileBase & f; public: FileOperations(IOFileBase & f_) : f(f_) { return; } public: inline bool IsValid() { return f.IsValid(); } inline bool IsReadSeekable() { return f.IsReadSeekable(); } inline bool IsWriteSeekable() { return f.IsWriteSeekable(); } inline IO::Offset TellRead() { return f.TellRead(); } inline IO::Offset TellWrite() { return f.TellWrite(); } inline bool SeekBegin() { return f.SeekBegin(); } inline bool SeekEnd() { return f.SeekEnd(); } inline bool SeekAbsolute(IO::Offset pos) { return f.SeekAbsolute(pos); } inline bool SeekRelative(IO::Offset off) { return f.SeekRelative(off); } inline mpt::byte_span ReadRawImpl(mpt::byte_span data) { return f.ReadRawImpl(data); } inline bool WriteRawImpl(mpt::const_byte_span data) { return f.WriteRawImpl(data); } inline bool IsEof() { return f.IsEof(); } inline bool Flush() { return f.Flush(); } }; } // namespace IO } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_IO_IO_VIRTUAL_WRAPPER_HPP libopenmpt-0.6.1+release.autotools/src/mpt/io_read/0000755000175000017500000000000014175541574017342 500000000000000libopenmpt-0.6.1+release.autotools/src/mpt/io_read/callbackstream.hpp0000644000175000017500000000143714056206101022726 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_IO_READ_CALLBACKSTREAM_HPP #define MPT_IO_READ_CALLBACKSTREAM_HPP #include "mpt/base/integer.hpp" #include "mpt/base/memory.hpp" #include "mpt/base/namespace.hpp" #include namespace mpt { inline namespace MPT_INLINE_NS { namespace IO { template struct CallbackStreamTemplate { enum : int { SeekSet = 0, SeekCur = 1, SeekEnd = 2 }; Tstream stream; std::size_t (*read)(Tstream stream, void * dst, std::size_t bytes); int (*seek)(Tstream stream, int64 offset, int whence); int64 (*tell)(Tstream stream); }; using CallbackStream = CallbackStreamTemplate; } // namespace IO } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_IO_READ_CALLBACKSTREAM_HPP libopenmpt-0.6.1+release.autotools/src/mpt/io_read/filecursor.hpp0000644000175000017500000002760214056206101022135 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_IO_READ_FILECURSOR_HPP #define MPT_IO_READ_FILECURSOR_HPP #include "mpt/base/alloc.hpp" #include "mpt/base/memory.hpp" #include "mpt/base/namespace.hpp" #include "mpt/base/span.hpp" #include #include #include #include namespace mpt { inline namespace MPT_INLINE_NS { namespace IO { // change to show warnings for functions which trigger pre-caching the whole file for unseekable streams //#define MPT_FILECURSOR_DEPRECATED [[deprecated]] #define MPT_FILECURSOR_DEPRECATED template class FileCursor { private: using traits_type = Ttraits; using filename_traits_type = Tfilenametraits; public: using pos_type = typename traits_type::pos_type; using data_type = typename traits_type::data_type; using ref_data_type = typename traits_type::ref_data_type; using shared_data_type = typename traits_type::shared_data_type; using value_data_type = typename traits_type::value_data_type; using filename_type = typename filename_traits_type::filename_type; using shared_filename_type = typename filename_traits_type::shared_filename_type; protected: shared_data_type SharedDataContainer() const { return traits_type::get_shared(m_data); } ref_data_type DataContainer() const { return traits_type::get_ref(m_data); } static value_data_type DataInitializer() { return traits_type::make_data(); } static value_data_type DataInitializer(mpt::const_byte_span data) { return traits_type::make_data(data); } static value_data_type CreateChunkImpl(shared_data_type data, pos_type position, pos_type size) { return traits_type::make_chunk(data, position, size); } private: data_type m_data; pos_type streamPos; // Cursor location in the file shared_filename_type m_fileName; // Filename that corresponds to this FileCursor. It is only set if this FileCursor represents the whole contents of fileName. May be nullopt. public: // Initialize invalid file reader object. FileCursor() : m_data(DataInitializer()) , streamPos(0) , m_fileName(nullptr) { return; } // Initialize file reader object with pointer to data and data length. template explicit FileCursor(mpt::span bytedata, shared_filename_type filename = shared_filename_type{}) : m_data(DataInitializer(mpt::byte_cast(bytedata))) , streamPos(0) , m_fileName(std::move(filename)) { return; } // Initialize file reader object based on an existing file reader object window. explicit FileCursor(value_data_type other, shared_filename_type filename = shared_filename_type{}) : m_data(std::move(other)) , streamPos(0) , m_fileName(std::move(filename)) { return; } public: std::optional GetOptionalFileName() const { return filename_traits_type::get_optional_filename(m_fileName); } // Returns true if the object points to a valid (non-empty) stream. operator bool() const { return IsValid(); } // Returns true if the object points to a valid (non-empty) stream. bool IsValid() const { return DataContainer().IsValid(); } // Reset cursor to first byte in file. void Rewind() { streamPos = 0; } // Seek to a position in the mapped file. // Returns false if position is invalid. bool Seek(pos_type position) { if (position <= streamPos) { streamPos = position; return true; } if (position <= DataContainer().GetLength()) { streamPos = position; return true; } else { return false; } } // Increases position by skipBytes. // Returns true if skipBytes could be skipped or false if the file end was reached earlier. bool Skip(pos_type skipBytes) { if (CanRead(skipBytes)) { streamPos += skipBytes; return true; } else { streamPos = DataContainer().GetLength(); return false; } } // Decreases position by skipBytes. // Returns true if skipBytes could be skipped or false if the file start was reached earlier. bool SkipBack(pos_type skipBytes) { if (streamPos >= skipBytes) { streamPos -= skipBytes; return true; } else { streamPos = 0; return false; } } // Returns cursor position in the mapped file. pos_type GetPosition() const { return streamPos; } // Return true IFF seeking and GetLength() is fast. // In particular, it returns false for unseekable stream where GetLength() // requires pre-caching. bool HasFastGetLength() const { return DataContainer().HasFastGetLength(); } // Returns size of the mapped file in bytes. MPT_FILECURSOR_DEPRECATED pos_type GetLength() const { // deprecated because in case of an unseekable std::istream, this triggers caching of the whole file return DataContainer().GetLength(); } // Return byte count between cursor position and end of file, i.e. how many bytes can still be read. MPT_FILECURSOR_DEPRECATED pos_type BytesLeft() const { // deprecated because in case of an unseekable std::istream, this triggers caching of the whole file return DataContainer().GetLength() - streamPos; } bool EndOfFile() const { return !CanRead(1); } bool NoBytesLeft() const { return !CanRead(1); } // Check if "amount" bytes can be read from the current position in the stream. bool CanRead(pos_type amount) const { return DataContainer().CanRead(streamPos, amount); } // Check if file size is at least size, without potentially caching the whole file to query the exact file length. bool LengthIsAtLeast(pos_type size) const { return DataContainer().CanRead(0, size); } // Check if file size is exactly size, without potentially caching the whole file if it is larger. bool LengthIs(pos_type size) const { return DataContainer().CanRead(0, size) && !DataContainer().CanRead(size, 1); } protected: FileCursor CreateChunk(pos_type position, pos_type length) const { pos_type readableLength = DataContainer().GetReadableLength(position, length); if (readableLength == 0) { return FileCursor(); } return FileCursor(CreateChunkImpl(SharedDataContainer(), position, std::min(length, DataContainer().GetLength() - position))); } public: // Create a new FileCursor object for parsing a sub chunk at a given position with a given length. // The file cursor is not modified. FileCursor GetChunkAt(pos_type position, pos_type length) const { return CreateChunk(position, length); } // Create a new FileCursor object for parsing a sub chunk at the current position with a given length. // The file cursor is not advanced. FileCursor GetChunk(pos_type length) { return CreateChunk(streamPos, length); } // Create a new FileCursor object for parsing a sub chunk at the current position with a given length. // The file cursor is advanced by "length" bytes. FileCursor ReadChunk(pos_type length) { pos_type position = streamPos; Skip(length); return CreateChunk(position, length); } class PinnedView { private: std::size_t size_; const std::byte * pinnedData; std::vector cache; private: void Init(const FileCursor & file, std::size_t size) { size_ = 0; pinnedData = nullptr; if (!file.CanRead(size)) { size = file.BytesLeft(); } size_ = size; if (file.DataContainer().HasPinnedView()) { pinnedData = file.DataContainer().GetRawData() + file.GetPosition(); } else { cache.resize(size_); if (!cache.empty()) { file.GetRaw(mpt::as_span(cache)); } } } public: PinnedView() : size_(0) , pinnedData(nullptr) { } PinnedView(const FileCursor & file) { Init(file, file.BytesLeft()); } PinnedView(const FileCursor & file, std::size_t size) { Init(file, size); } PinnedView(FileCursor & file, bool advance) { Init(file, file.BytesLeft()); if (advance) { file.Skip(size_); } } PinnedView(FileCursor & file, std::size_t size, bool advance) { Init(file, size); if (advance) { file.Skip(size_); } } public: mpt::const_byte_span GetSpan() const { if (pinnedData) { return mpt::as_span(pinnedData, size_); } else if (!cache.empty()) { return mpt::as_span(cache); } else { return mpt::const_byte_span(); } } mpt::const_byte_span span() const { return GetSpan(); } void invalidate() { size_ = 0; pinnedData = nullptr; cache = std::vector(); } const std::byte * data() const { return span().data(); } std::size_t size() const { return size_; } mpt::const_byte_span::pointer begin() const { return span().data(); } mpt::const_byte_span::pointer end() const { return span().data() + span().size(); } mpt::const_byte_span::const_pointer cbegin() const { return span().data(); } mpt::const_byte_span::const_pointer cend() const { return span().data() + span().size(); } }; // Returns a pinned view into the remaining raw data from cursor position. PinnedView GetPinnedView() const { return PinnedView(*this); } // Returns a pinned view into the remeining raw data from cursor position, clamped at size. PinnedView GetPinnedView(std::size_t size) const { return PinnedView(*this, size); } // Returns a pinned view into the remeining raw data from cursor position. // File cursor is advaned by the size of the returned pinned view. PinnedView ReadPinnedView() { return PinnedView(*this, true); } // Returns a pinned view into the remeining raw data from cursor position, clamped at size. // File cursor is advaned by the size of the returned pinned view. PinnedView ReadPinnedView(std::size_t size) { return PinnedView(*this, size, true); } // Returns raw stream data at cursor position. // Should only be used if absolutely necessary, for example for sample reading, or when used with a small chunk of the file retrieved by ReadChunk(). // Use GetPinnedView(size) whenever possible. MPT_FILECURSOR_DEPRECATED mpt::const_byte_span GetRawData() const { // deprecated because in case of an unseekable std::istream, this triggers caching of the whole file return mpt::span(DataContainer().GetRawData() + streamPos, DataContainer().GetLength() - streamPos); } template MPT_FILECURSOR_DEPRECATED mpt::span GetRawData() const { // deprecated because in case of an unseekable std::istream, this triggers caching of the whole file return mpt::span(mpt::byte_cast(DataContainer().GetRawData() + streamPos), DataContainer().GetLength() - streamPos); } mpt::byte_span GetRawWithOffset(std::size_t offset, mpt::byte_span dst) const { return DataContainer().Read(streamPos + offset, dst); } template Tspan GetRawWithOffset(std::size_t offset, Tspan dst) const { return mpt::byte_cast(DataContainer().Read(streamPos + offset, mpt::byte_cast(dst))); } mpt::byte_span GetRaw(mpt::byte_span dst) const { return DataContainer().Read(streamPos, dst); } template Tspan GetRaw(Tspan dst) const { return mpt::byte_cast(DataContainer().Read(streamPos, mpt::byte_cast(dst))); } mpt::byte_span ReadRaw(mpt::byte_span dst) { mpt::byte_span result = DataContainer().Read(streamPos, dst); streamPos += result.size(); return result; } template Tspan ReadRaw(Tspan dst) { Tspan result = mpt::byte_cast(DataContainer().Read(streamPos, mpt::byte_cast(dst))); streamPos += result.size(); return result; } std::vector GetRawDataAsByteVector() const { PinnedView view = GetPinnedView(); return mpt::make_vector(view.span()); } std::vector ReadRawDataAsByteVector() { PinnedView view = ReadPinnedView(); return mpt::make_vector(view.span()); } std::vector GetRawDataAsByteVector(std::size_t size) const { PinnedView view = GetPinnedView(size); return mpt::make_vector(view.span()); } std::vector ReadRawDataAsByteVector(std::size_t size) { PinnedView view = ReadPinnedView(size); return mpt::make_vector(view.span()); } }; } // namespace IO } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_IO_READ_FILECURSOR_HPP libopenmpt-0.6.1+release.autotools/src/mpt/io_read/filecursor_callbackstream.hpp0000644000175000017500000000263714056211536025176 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_IO_READ_FILECURSOR_CALLBACKSTREAM_HPP #define MPT_IO_READ_FILECURSOR_CALLBACKSTREAM_HPP #include "mpt/base/namespace.hpp" #include "mpt/io_read/callbackstream.hpp" #include "mpt/io_read/filecursor.hpp" #include "mpt/io_read/filecursor_traits_filedata.hpp" #include "mpt/io_read/filecursor_filename_traits.hpp" #include "mpt/io_read/filedata_callbackstream.hpp" #include #include namespace mpt { inline namespace MPT_INLINE_NS { namespace IO { // Initialize file reader object with a CallbackStream. template inline FileCursor> make_FileCursor(CallbackStreamTemplate s, std::shared_ptr filename = nullptr) { if (FileDataCallbackStreamTemplate::IsSeekable(s)) { return FileCursor>(std::static_pointer_cast(std::make_shared>(s)), std::move(filename)); } else { return FileCursor>(std::static_pointer_cast(std::make_shared>(s)), std::move(filename)); } } } // namespace IO } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_IO_READ_FILECURSOR_CALLBACKSTREAM_HPP libopenmpt-0.6.1+release.autotools/src/mpt/io_read/filecursor_filename_traits.hpp0000644000175000017500000000200614056206101025352 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_IO_READ_FILECURSOR_FILENAME_TRAITS_HPP #define MPT_IO_READ_FILECURSOR_FILENAME_TRAITS_HPP #include "mpt/base/namespace.hpp" #include #include namespace mpt { inline namespace MPT_INLINE_NS { namespace IO { class FileCursorFilenameTraitsNone { public: struct empty_type { }; using filename_type = empty_type; using shared_filename_type = empty_type; static std::optional get_optional_filename(shared_filename_type /* filename */) { return std::nullopt; } }; template class FileCursorFilenameTraits { public: using filename_type = Tpath; using shared_filename_type = std::shared_ptr; static std::optional get_optional_filename(shared_filename_type filename) { if (!filename) { return std::nullopt; } return *filename; } }; } // namespace IO } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_IO_READ_FILECURSOR_FILENAME_TRAITS_HPP libopenmpt-0.6.1+release.autotools/src/mpt/io_read/filecursor_memory.hpp0000644000175000017500000000176514056211755023542 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_IO_READ_FILECURSOR_MEMORY_HPP #define MPT_IO_READ_FILECURSOR_MEMORY_HPP #include "mpt/base/namespace.hpp" #include "mpt/base/span.hpp" #include "mpt/io_read/filecursor.hpp" #include "mpt/io_read/filecursor_traits_filedata.hpp" #include "mpt/io_read/filecursor_filename_traits.hpp" #include #include namespace mpt { inline namespace MPT_INLINE_NS { namespace IO { // Initialize file reader object with pointer to data and data length. template inline FileCursor> make_FileCursor(mpt::span bytedata, std::shared_ptr filename = nullptr) { return FileCursor>(mpt::byte_cast(bytedata), std::move(filename)); } } // namespace IO } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_IO_READ_FILECURSOR_STDSTREAM_HPP libopenmpt-0.6.1+release.autotools/src/mpt/io_read/filecursor_stdstream.hpp0000644000175000017500000000241514056211536024226 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_IO_READ_FILECURSOR_STDSTREAM_HPP #define MPT_IO_READ_FILECURSOR_STDSTREAM_HPP #include "mpt/base/namespace.hpp" #include "mpt/io_read/filecursor.hpp" #include "mpt/io_read/filecursor_traits_filedata.hpp" #include "mpt/io_read/filecursor_filename_traits.hpp" #include "mpt/io_read/filedata_stdstream.hpp" #include #include #include namespace mpt { inline namespace MPT_INLINE_NS { namespace IO { // Initialize file reader object with a std::istream. template inline FileCursor> make_FileCursor(std::istream & s, std::shared_ptr filename = nullptr) { if (FileDataStdStream::IsSeekable(s)) { return FileCursor>(std::static_pointer_cast(std::make_shared(s)), std::move(filename)); } else { return FileCursor>(std::static_pointer_cast(std::make_shared(s)), std::move(filename)); } } } // namespace IO } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_IO_READ_FILECURSOR_STDSTREAM_HPP libopenmpt-0.6.1+release.autotools/src/mpt/io_read/filecursor_traits_filedata.hpp0000644000175000017500000000261614056206101025352 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_IO_READ_FILECURSOR_TRAITS_FILEDATA_HPP #define MPT_IO_READ_FILECURSOR_TRAITS_FILEDATA_HPP #include "mpt/base/memory.hpp" #include "mpt/base/namespace.hpp" #include "mpt/io_read/filedata.hpp" #include "mpt/io_read/filedata_base.hpp" #include "mpt/io_read/filedata_memory.hpp" #include namespace mpt { inline namespace MPT_INLINE_NS { namespace IO { class FileCursorTraitsFileData { public: using pos_type = IFileData::pos_type; using data_type = std::shared_ptr; using ref_data_type = const IFileData &; using shared_data_type = std::shared_ptr; using value_data_type = std::shared_ptr; static shared_data_type get_shared(const data_type & data) { return data; } static ref_data_type get_ref(const data_type & data) { return *data; } static value_data_type make_data() { return std::make_shared(); } static value_data_type make_data(mpt::const_byte_span data) { return std::make_shared(data); } static value_data_type make_chunk(shared_data_type data, pos_type position, pos_type size) { return std::static_pointer_cast(std::make_shared(data, position, size)); } }; } // namespace IO } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_IO_READ_FILECURSOR_TRAITS_FILEDATA_HPP libopenmpt-0.6.1+release.autotools/src/mpt/io_read/filecursor_traits_memory.hpp0000644000175000017500000000235214056206101025106 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_IO_READ_FILECURSOR_TRAITS_MEMORY_HPP #define MPT_IO_READ_FILECURSOR_TRAITS_MEMORY_HPP #include "mpt/base/memory.hpp" #include "mpt/base/namespace.hpp" #include "mpt/base/span.hpp" #include "mpt/io_read/filedata.hpp" #include "mpt/io_read/filedata_memory.hpp" namespace mpt { inline namespace MPT_INLINE_NS { namespace IO { class FileCursorTraitsMemory { public: using pos_type = FileDataMemory::pos_type; using data_type = FileDataMemory; using ref_data_type = const FileDataMemory &; using shared_data_type = const FileDataMemory &; using value_data_type = FileDataMemory; static shared_data_type get_shared(const data_type & data) { return data; } static ref_data_type get_ref(const data_type & data) { return data; } static value_data_type make_data() { return mpt::const_byte_span(); } static value_data_type make_data(mpt::const_byte_span data) { return data; } static value_data_type make_chunk(shared_data_type data, pos_type position, pos_type size) { return mpt::as_span(data.GetRawData() + position, size); } }; } // namespace IO } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_IO_READ_FILECURSOR_TRAITS_MEMORY_HPP libopenmpt-0.6.1+release.autotools/src/mpt/io_read/filedata.hpp0000644000175000017500000000260114056206101021521 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_IO_READ_FILEDATA_HPP #define MPT_IO_READ_FILEDATA_HPP #include "mpt/base/memory.hpp" #include "mpt/base/namespace.hpp" #include #include namespace mpt { inline namespace MPT_INLINE_NS { namespace IO { class IFileData { public: typedef std::size_t pos_type; protected: IFileData() = default; public: IFileData(const IFileData &) = default; IFileData & operator=(const IFileData &) = default; virtual ~IFileData() = default; public: virtual bool IsValid() const = 0; virtual bool HasFastGetLength() const = 0; virtual bool HasPinnedView() const = 0; virtual const std::byte * GetRawData() const = 0; virtual pos_type GetLength() const = 0; virtual mpt::byte_span Read(pos_type pos, mpt::byte_span dst) const = 0; virtual bool CanRead(pos_type pos, std::size_t length) const { pos_type dataLength = GetLength(); if ((pos == dataLength) && (length == 0)) { return true; } if (pos >= dataLength) { return false; } return length <= dataLength - pos; } virtual std::size_t GetReadableLength(pos_type pos, std::size_t length) const { pos_type dataLength = GetLength(); if (pos >= dataLength) { return 0; } return std::min(length, dataLength - pos); } }; } // namespace IO } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_IO_READ_FILEDATA_HPP libopenmpt-0.6.1+release.autotools/src/mpt/io_read/filedata_base.hpp0000644000175000017500000000431014056206101022512 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_IO_READ_FILEDATA_BASE_HPP #define MPT_IO_READ_FILEDATA_BASE_HPP #include "mpt/base/memory.hpp" #include "mpt/base/namespace.hpp" #include "mpt/io_read/filedata.hpp" #include #include #include namespace mpt { inline namespace MPT_INLINE_NS { namespace IO { class FileDataDummy : public IFileData { public: FileDataDummy() { } public: bool IsValid() const override { return false; } bool HasFastGetLength() const override { return true; } bool HasPinnedView() const override { return true; } const std::byte * GetRawData() const override { return nullptr; } pos_type GetLength() const override { return 0; } mpt::byte_span Read(pos_type /* pos */, mpt::byte_span dst) const override { return dst.first(0); } }; class FileDataWindow : public IFileData { private: std::shared_ptr data; const pos_type dataOffset; const pos_type dataLength; public: FileDataWindow(std::shared_ptr src, pos_type off, pos_type len) : data(src), dataOffset(off), dataLength(len) { } bool IsValid() const override { return data->IsValid(); } bool HasFastGetLength() const override { return data->HasFastGetLength(); } bool HasPinnedView() const override { return data->HasPinnedView(); } const std::byte * GetRawData() const override { return data->GetRawData() + dataOffset; } pos_type GetLength() const override { return dataLength; } mpt::byte_span Read(pos_type pos, mpt::byte_span dst) const override { if (pos >= dataLength) { return dst.first(0); } return data->Read(dataOffset + pos, dst.first(std::min(dst.size(), dataLength - pos))); } bool CanRead(pos_type pos, std::size_t length) const override { if ((pos == dataLength) && (length == 0)) { return true; } if (pos >= dataLength) { return false; } return (length <= dataLength - pos); } pos_type GetReadableLength(pos_type pos, std::size_t length) const override { if (pos >= dataLength) { return 0; } return std::min(length, dataLength - pos); } }; } // namespace IO } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_IO_READ_FILEDATA_BASE_HPP libopenmpt-0.6.1+release.autotools/src/mpt/io_read/filedata_base_buffered.hpp0000644000175000017500000000701414056354456024401 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_IO_READ_FILEDATA_BASE_BUFFERED_HPP #define MPT_IO_READ_FILEDATA_BASE_BUFFERED_HPP #include "mpt/base/memory.hpp" #include "mpt/base/namespace.hpp" #include "mpt/base/numeric.hpp" #include "mpt/io/base.hpp" #include "mpt/io_read/filedata.hpp" #include "mpt/io_read/filedata_base_seekable.hpp" #include #include #include namespace mpt { inline namespace MPT_INLINE_NS { namespace IO { class FileDataSeekableBuffered : public FileDataSeekable { private: enum : std::size_t { CHUNK_SIZE = mpt::IO::BUFFERSIZE_SMALL, BUFFER_SIZE = mpt::IO::BUFFERSIZE_NORMAL }; enum : std::size_t { NUM_CHUNKS = BUFFER_SIZE / CHUNK_SIZE }; struct chunk_info { pos_type ChunkOffset = 0; pos_type ChunkLength = 0; bool ChunkValid = false; }; mutable std::vector m_Buffer = std::vector(BUFFER_SIZE); mpt::byte_span chunk_data(std::size_t chunkIndex) const { return mpt::byte_span(m_Buffer.data() + (chunkIndex * CHUNK_SIZE), CHUNK_SIZE); } mutable std::array m_ChunkInfo = {}; mutable std::array m_ChunkIndexLRU = {}; std::size_t InternalFillPageAndReturnIndex(pos_type pos) const { pos = mpt::align_down(pos, static_cast(CHUNK_SIZE)); for (std::size_t chunkLRUIndex = 0; chunkLRUIndex < NUM_CHUNKS; ++chunkLRUIndex) { std::size_t chunkIndex = m_ChunkIndexLRU[chunkLRUIndex]; if (m_ChunkInfo[chunkIndex].ChunkValid && (m_ChunkInfo[chunkIndex].ChunkOffset == pos)) { std::size_t chunk = std::move(m_ChunkIndexLRU[chunkLRUIndex]); std::move_backward(m_ChunkIndexLRU.begin(), m_ChunkIndexLRU.begin() + chunkLRUIndex, m_ChunkIndexLRU.begin() + (chunkLRUIndex + 1)); m_ChunkIndexLRU[0] = std::move(chunk); return chunkIndex; } } { std::size_t chunk = std::move(m_ChunkIndexLRU[NUM_CHUNKS - 1]); std::move_backward(m_ChunkIndexLRU.begin(), m_ChunkIndexLRU.begin() + (NUM_CHUNKS - 1), m_ChunkIndexLRU.begin() + NUM_CHUNKS); m_ChunkIndexLRU[0] = std::move(chunk); } std::size_t chunkIndex = m_ChunkIndexLRU[0]; chunk_info & chunk = m_ChunkInfo[chunkIndex]; chunk.ChunkOffset = pos; chunk.ChunkLength = InternalReadBuffered(pos, chunk_data(chunkIndex)).size(); chunk.ChunkValid = true; return chunkIndex; } protected: FileDataSeekableBuffered(pos_type streamLength_) : FileDataSeekable(streamLength_) { return; } private: mpt::byte_span InternalReadSeekable(pos_type pos, mpt::byte_span dst) const override { pos_type totalRead = 0; std::byte * pdst = dst.data(); std::size_t count = dst.size(); while (count > 0) { std::size_t chunkIndex = InternalFillPageAndReturnIndex(pos); pos_type pageSkip = pos - m_ChunkInfo[chunkIndex].ChunkOffset; pos_type chunkWanted = std::min(static_cast(CHUNK_SIZE) - pageSkip, count); pos_type chunkGot = (m_ChunkInfo[chunkIndex].ChunkLength > pageSkip) ? (m_ChunkInfo[chunkIndex].ChunkLength - pageSkip) : 0; pos_type chunk = std::min(chunkWanted, chunkGot); std::copy(chunk_data(chunkIndex).data() + pageSkip, chunk_data(chunkIndex).data() + pageSkip + chunk, pdst); pos += chunk; pdst += chunk; totalRead += chunk; count -= chunk; if (chunkWanted > chunk) { return dst.first(totalRead); } } return dst.first(totalRead); } virtual mpt::byte_span InternalReadBuffered(pos_type pos, mpt::byte_span dst) const = 0; }; } // namespace IO } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_IO_READ_FILEDATA_BASE_BUFFERED_HPP libopenmpt-0.6.1+release.autotools/src/mpt/io_read/filedata_base_seekable.hpp0000644000175000017500000000332214056206101024347 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_IO_READ_FILEDATA_BASE_SEEKABLE_HPP #define MPT_IO_READ_FILEDATA_BASE_SEEKABLE_HPP #include "mpt/base/memory.hpp" #include "mpt/base/namespace.hpp" #include "mpt/io_read/filedata.hpp" #include #include #include namespace mpt { inline namespace MPT_INLINE_NS { namespace IO { class FileDataSeekable : public IFileData { private: pos_type streamLength; mutable bool cached; mutable std::vector cache; protected: FileDataSeekable(pos_type streamLength_) : streamLength(streamLength_) , cached(false) { return; } private: void CacheStream() const { if (cached) { return; } cache.resize(streamLength); InternalReadSeekable(0, mpt::as_span(cache)); cached = true; } public: bool IsValid() const override { return true; } bool HasFastGetLength() const override { return true; } bool HasPinnedView() const override { return cached; } const std::byte * GetRawData() const override { CacheStream(); return cache.data(); } pos_type GetLength() const override { return streamLength; } mpt::byte_span Read(pos_type pos, mpt::byte_span dst) const override { if (cached) { IFileData::pos_type cache_avail = std::min(IFileData::pos_type(cache.size()) - pos, dst.size()); std::copy(cache.begin() + pos, cache.begin() + pos + cache_avail, dst.data()); return dst.first(cache_avail); } else { return InternalReadSeekable(pos, dst); } } private: virtual mpt::byte_span InternalReadSeekable(pos_type pos, mpt::byte_span dst) const = 0; }; } // namespace IO } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_IO_READ_FILEDATA_BASE_SEEKABLE_HPP libopenmpt-0.6.1+release.autotools/src/mpt/io_read/filedata_base_unseekable.hpp0000644000175000017500000001005614056206101024714 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_IO_READ_FILEDATA_BASE_UNSEEKABLE_HPP #define MPT_IO_READ_FILEDATA_BASE_UNSEEKABLE_HPP #include "mpt/base/algorithm.hpp" #include "mpt/base/memory.hpp" #include "mpt/base/namespace.hpp" #include "mpt/base/numeric.hpp" #include "mpt/io/base.hpp" #include "mpt/io_read/filedata.hpp" #include #include #include #include namespace mpt { inline namespace MPT_INLINE_NS { namespace IO { class FileDataUnseekable : public IFileData { private: mutable std::vector cache; mutable std::size_t cachesize; mutable bool streamFullyCached; protected: FileDataUnseekable() : cachesize(0), streamFullyCached(false) { return; } private: enum : std::size_t { QUANTUM_SIZE = mpt::IO::BUFFERSIZE_SMALL, BUFFER_SIZE = mpt::IO::BUFFERSIZE_NORMAL }; void EnsureCacheBuffer(std::size_t requiredbuffersize) const { if (cache.size() >= cachesize + requiredbuffersize) { return; } if (cache.size() == 0) { cache.resize(mpt::align_up(cachesize + requiredbuffersize, BUFFER_SIZE)); } else if (mpt::exponential_grow(cache.size()) < cachesize + requiredbuffersize) { cache.resize(mpt::align_up(cachesize + requiredbuffersize, BUFFER_SIZE)); } else { cache.resize(mpt::exponential_grow(cache.size())); } } void CacheStream() const { if (streamFullyCached) { return; } while (!InternalEof()) { EnsureCacheBuffer(BUFFER_SIZE); std::size_t readcount = InternalReadUnseekable(mpt::span(&cache[cachesize], BUFFER_SIZE)).size(); cachesize += readcount; } streamFullyCached = true; } void CacheStreamUpTo(pos_type pos, pos_type length) const { if (streamFullyCached) { return; } if (length > std::numeric_limits::max() - pos) { length = std::numeric_limits::max() - pos; } std::size_t target = mpt::saturate_cast(pos + length); if (target <= cachesize) { return; } std::size_t alignedpos = mpt::align_up(target, QUANTUM_SIZE); std::size_t needcount = alignedpos - cachesize; EnsureCacheBuffer(needcount); std::size_t readcount = InternalReadUnseekable(mpt::span(&cache[cachesize], alignedpos - cachesize)).size(); cachesize += readcount; if (!InternalEof()) { // can read further return; } streamFullyCached = true; } private: void ReadCached(pos_type pos, mpt::byte_span dst) const { std::copy(cache.begin() + pos, cache.begin() + pos + dst.size(), dst.data()); } public: bool IsValid() const override { return true; } bool HasFastGetLength() const override { return false; } bool HasPinnedView() const override { return true; // we have the cache which is required for seeking anyway } const std::byte * GetRawData() const override { CacheStream(); return cache.data(); } pos_type GetLength() const override { CacheStream(); return cachesize; } mpt::byte_span Read(pos_type pos, mpt::byte_span dst) const override { CacheStreamUpTo(pos, dst.size()); if (pos >= IFileData::pos_type(cachesize)) { return dst.first(0); } IFileData::pos_type cache_avail = std::min(IFileData::pos_type(cachesize) - pos, dst.size()); ReadCached(pos, dst.subspan(0, cache_avail)); return dst.subspan(0, cache_avail); } bool CanRead(pos_type pos, std::size_t length) const override { CacheStreamUpTo(pos, length); if ((pos == IFileData::pos_type(cachesize)) && (length == 0)) { return true; } if (pos >= IFileData::pos_type(cachesize)) { return false; } return length <= IFileData::pos_type(cachesize) - pos; } std::size_t GetReadableLength(pos_type pos, std::size_t length) const override { CacheStreamUpTo(pos, length); if (pos >= cachesize) { return 0; } return std::min(static_cast(cachesize) - pos, length); } private: virtual bool InternalEof() const = 0; virtual mpt::byte_span InternalReadUnseekable(mpt::byte_span dst) const = 0; }; } // namespace IO } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_IO_READ_FILEDATA_BASE_UNSEEKABLE_HPP libopenmpt-0.6.1+release.autotools/src/mpt/io_read/filedata_callbackstream.hpp0000644000175000017500000001116514056211755024571 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_IO_READ_FILEDATA_CALLBACKSTREAM_HPP #define MPT_IO_READ_FILEDATA_CALLBACKSTREAM_HPP #include "mpt/base/integer.hpp" #include "mpt/base/memory.hpp" #include "mpt/base/namespace.hpp" #include "mpt/io_read/callbackstream.hpp" #include "mpt/io_read/filedata.hpp" #include "mpt/io_read/filedata_base_seekable.hpp" #include "mpt/io_read/filedata_base_unseekable.hpp" #include #include #include namespace mpt { inline namespace MPT_INLINE_NS { namespace IO { template class FileDataCallbackStreamTemplate { public: static bool IsSeekable(CallbackStreamTemplate stream) { if (!stream.stream) { return false; } if (!stream.seek) { return false; } if (!stream.tell) { return false; } int64 oldpos = stream.tell(stream.stream); if (oldpos < 0) { return false; } if (stream.seek(stream.stream, 0, CallbackStream::SeekSet) < 0) { stream.seek(stream.stream, oldpos, CallbackStream::SeekSet); return false; } if (stream.seek(stream.stream, 0, CallbackStream::SeekEnd) < 0) { stream.seek(stream.stream, oldpos, CallbackStream::SeekSet); return false; } int64 length = stream.tell(stream.stream); if (length < 0) { stream.seek(stream.stream, oldpos, CallbackStream::SeekSet); return false; } stream.seek(stream.stream, oldpos, CallbackStream::SeekSet); return true; } static IFileData::pos_type GetLength(CallbackStreamTemplate stream) { if (!stream.stream) { return 0; } if (!stream.seek) { return false; } if (!stream.tell) { return false; } int64 oldpos = stream.tell(stream.stream); if (oldpos < 0) { return 0; } if (stream.seek(stream.stream, 0, CallbackStream::SeekSet) < 0) { stream.seek(stream.stream, oldpos, CallbackStream::SeekSet); return 0; } if (stream.seek(stream.stream, 0, CallbackStream::SeekEnd) < 0) { stream.seek(stream.stream, oldpos, CallbackStream::SeekSet); return 0; } int64 length = stream.tell(stream.stream); if (length < 0) { stream.seek(stream.stream, oldpos, CallbackStream::SeekSet); return 0; } stream.seek(stream.stream, oldpos, CallbackStream::SeekSet); return mpt::saturate_cast(length); } }; using FileDataCallbackStream = FileDataCallbackStreamTemplate; template class FileDataCallbackStreamSeekableTemplate : public FileDataSeekable { private: CallbackStreamTemplate stream; public: FileDataCallbackStreamSeekableTemplate(CallbackStreamTemplate s) : FileDataSeekable(FileDataCallbackStream::GetLength(s)) , stream(s) { return; } private: mpt::byte_span InternalReadSeekable(pos_type pos, mpt::byte_span dst) const override { if (!stream.read) { return dst.first(0); } if (stream.seek(stream.stream, pos, CallbackStream::SeekSet) < 0) { return dst.first(0); } int64 totalread = 0; std::byte * pdst = dst.data(); std::size_t count = dst.size(); while (count > 0) { int64 readcount = stream.read(stream.stream, pdst, count); if (readcount <= 0) { break; } pdst += static_cast(readcount); count -= static_cast(readcount); totalread += readcount; } return dst.first(static_cast(totalread)); } }; using FileDataCallbackStreamSeekable = FileDataCallbackStreamSeekableTemplate; template class FileDataCallbackStreamUnseekableTemplate : public FileDataUnseekable { private: CallbackStreamTemplate stream; mutable bool eof_reached; public: FileDataCallbackStreamUnseekableTemplate(CallbackStreamTemplate s) : FileDataUnseekable() , stream(s) , eof_reached(false) { return; } private: bool InternalEof() const override { return eof_reached; } mpt::byte_span InternalReadUnseekable(mpt::byte_span dst) const override { if (eof_reached) { return dst.first(0); } if (!stream.read) { eof_reached = true; return dst.first(0); } int64 totalread = 0; std::byte * pdst = dst.data(); std::size_t count = dst.size(); while (count > 0) { int64 readcount = stream.read(stream.stream, pdst, count); if (readcount <= 0) { eof_reached = true; break; } pdst += static_cast(readcount); count -= static_cast(readcount); totalread += readcount; } return dst.first(static_cast(totalread)); } }; using FileDataCallbackStreamUnseekable = FileDataCallbackStreamUnseekableTemplate; } // namespace IO } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_IO_READ_FILEDATA_CALLBACKSTREAM_HPP libopenmpt-0.6.1+release.autotools/src/mpt/io_read/filedata_memory.hpp0000644000175000017500000000351514056206101023116 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_IO_READ_FILEDATA_MEMORY_HPP #define MPT_IO_READ_FILEDATA_MEMORY_HPP #include "mpt/base/memory.hpp" #include "mpt/base/namespace.hpp" #include "mpt/io_read/filedata.hpp" #include #include namespace mpt { inline namespace MPT_INLINE_NS { namespace IO { class FileDataMemory : public IFileData { private: const std::byte * streamData; // Pointer to memory-mapped file pos_type streamLength; // Size of memory-mapped file in bytes public: FileDataMemory() : streamData(nullptr), streamLength(0) { } FileDataMemory(mpt::const_byte_span data) : streamData(data.data()), streamLength(data.size()) { } public: bool IsValid() const override { return streamData != nullptr; } bool HasFastGetLength() const override { return true; } bool HasPinnedView() const override { return true; } const std::byte * GetRawData() const override { return streamData; } pos_type GetLength() const override { return streamLength; } mpt::byte_span Read(pos_type pos, mpt::byte_span dst) const override { if (pos >= streamLength) { return dst.first(0); } pos_type avail = std::min(streamLength - pos, dst.size()); std::copy(streamData + pos, streamData + pos + avail, dst.data()); return dst.first(avail); } bool CanRead(pos_type pos, std::size_t length) const override { if ((pos == streamLength) && (length == 0)) { return true; } if (pos >= streamLength) { return false; } return (length <= streamLength - pos); } std::size_t GetReadableLength(pos_type pos, std::size_t length) const override { if (pos >= streamLength) { return 0; } return std::min(length, streamLength - pos); } }; } // namespace IO } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_IO_READ_FILEDATA_MEMORY_HPP libopenmpt-0.6.1+release.autotools/src/mpt/io_read/filedata_stdstream.hpp0000644000175000017500000000466114056206101023617 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_IO_READ_FILEDATA_STDSTREAM_HPP #define MPT_IO_READ_FILEDATA_STDSTREAM_HPP #include "mpt/base/memory.hpp" #include "mpt/base/namespace.hpp" #include "mpt/base/saturate_cast.hpp" #include "mpt/io/base.hpp" #include "mpt/io/io_stdstream.hpp" #include "mpt/io_read/filedata.hpp" #include "mpt/io_read/filedata_base_buffered.hpp" #include "mpt/io_read/filedata_base_unseekable.hpp" #include #include #include #include namespace mpt { inline namespace MPT_INLINE_NS { namespace IO { class FileDataStdStream { public: static bool IsSeekable(std::istream & stream) { return mpt::IO::IsReadSeekable(stream); } static IFileData::pos_type GetLength(std::istream & stream) { stream.clear(); std::streampos oldpos = stream.tellg(); stream.seekg(0, std::ios::end); std::streampos length = stream.tellg(); stream.seekg(oldpos); return mpt::saturate_cast(static_cast(length)); } }; class FileDataStdStreamSeekable : public FileDataSeekableBuffered { private: std::istream & stream; public: FileDataStdStreamSeekable(std::istream & s) : FileDataSeekableBuffered(FileDataStdStream::GetLength(s)) , stream(s) { return; } private: mpt::byte_span InternalReadBuffered(pos_type pos, mpt::byte_span dst) const override { stream.clear(); // tellg needs eof and fail bits unset std::streampos currentpos = stream.tellg(); if (currentpos == std::streampos(-1) || static_cast(pos) != currentpos) { // inefficient istream implementations might invalidate their buffer when seeking, even when seeking to the current position stream.seekg(pos); } stream.read(mpt::byte_cast(dst.data()), dst.size()); return dst.first(static_cast(stream.gcount())); } }; class FileDataStdStreamUnseekable : public FileDataUnseekable { private: std::istream & stream; public: FileDataStdStreamUnseekable(std::istream & s) : stream(s) { return; } private: bool InternalEof() const override { if (stream) { return false; } else { return true; } } mpt::byte_span InternalReadUnseekable(mpt::byte_span dst) const override { stream.read(mpt::byte_cast(dst.data()), dst.size()); return dst.first(static_cast(stream.gcount())); } }; } // namespace IO } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_IO_READ_FILEDATA_STDSTREAM_HPP libopenmpt-0.6.1+release.autotools/src/mpt/io_read/filereader.hpp0000644000175000017500000004433014134351016022062 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_IO_READ_FILEREADER_HPP #define MPT_IO_READ_FILEREADER_HPP #include "mpt/base/alloc.hpp" #include "mpt/base/bit.hpp" #include "mpt/base/integer.hpp" #include "mpt/base/memory.hpp" #include "mpt/base/namespace.hpp" #include "mpt/base/span.hpp" #include "mpt/base/utility.hpp" #include "mpt/io/base.hpp" #include "mpt/endian/floatingpoint.hpp" #include "mpt/endian/integer.hpp" #include "mpt/string/utility.hpp" #include #include #include #include #include #include #include #include namespace mpt { inline namespace MPT_INLINE_NS { namespace IO { namespace FileReader { // Read a "T" object from the stream. // If not enough bytes can be read, false is returned. // If successful, the file cursor is advanced by the size of "T". template bool Read(TFileCursor & f, T & target) { // cppcheck false-positive // cppcheck-suppress uninitvar mpt::byte_span dst = mpt::as_raw_memory(target); if (dst.size() != f.GetRaw(dst).size()) { return false; } f.Skip(dst.size()); return true; } // Read an array of binary-safe T values. // If successful, the file cursor is advanced by the size of the array. // Otherwise, the target is zeroed. template bool ReadArray(TFileCursor & f, T (&destArray)[destSize]) { static_assert(mpt::is_binary_safe::value); if (!f.CanRead(sizeof(destArray))) { mpt::reset(destArray); return false; } f.ReadRaw(mpt::as_raw_memory(destArray)); return true; } // Read an array of binary-safe T values. // If successful, the file cursor is advanced by the size of the array. // Otherwise, the target is zeroed. template bool ReadArray(TFileCursor & f, std::array & destArray) { static_assert(mpt::is_binary_safe::value); if (!f.CanRead(sizeof(destArray))) { destArray.fill(T{}); return false; } f.ReadRaw(mpt::as_raw_memory(destArray)); return true; } // Read destSize elements of binary-safe type T into a vector. // If successful, the file cursor is advanced by the size of the vector. // Otherwise, the vector is resized to destSize, but possibly existing contents are not cleared. template bool ReadVector(TFileCursor & f, std::vector & destVector, size_t destSize) { static_assert(mpt::is_binary_safe::value); destVector.resize(destSize); if (!f.CanRead(sizeof(T) * destSize)) { return false; } f.ReadRaw(mpt::as_raw_memory(destVector)); return true; } template std::array ReadArray(TFileCursor & f) { std::array destArray; ReadArray(f, destArray); return destArray; } // Read some kind of integer in little-endian format. // If successful, the file cursor is advanced by the size of the integer. template T ReadIntLE(TFileCursor & f) { static_assert(std::numeric_limits::is_integer == true, "Target type is a not an integer"); typename mpt::make_le::type target; if (!FileReader::Read(f, target)) { return 0; } return target; } // Read some kind of integer in big-endian format. // If successful, the file cursor is advanced by the size of the integer. template T ReadIntBE(TFileCursor & f) { static_assert(std::numeric_limits::is_integer == true, "Target type is a not an integer"); typename mpt::make_be::type target; if (!FileReader::Read(f, target)) { return 0; } return target; } // Read a integer in little-endian format which has some of its higher bytes not stored in file. // If successful, the file cursor is advanced by the given size. template T ReadTruncatedIntLE(TFileCursor & f, typename TFileCursor::pos_type size) { static_assert(std::numeric_limits::is_integer == true, "Target type is a not an integer"); assert(sizeof(T) >= size); if (size == 0) { return 0; } if (!f.CanRead(size)) { return 0; } uint8 buf[sizeof(T)]; bool negative = false; for (std::size_t i = 0; i < sizeof(T); ++i) { uint8 byte = 0; if (i < size) { FileReader::Read(f, byte); negative = std::numeric_limits::is_signed && ((byte & 0x80) != 0x00); } else { // sign or zero extend byte = negative ? 0xff : 0x00; } buf[i] = byte; } return mpt::bit_cast::type>(buf); } // Read a supplied-size little endian integer to a fixed size variable. // The data is properly sign-extended when fewer bytes are stored. // If more bytes are stored, higher order bytes are silently ignored. // If successful, the file cursor is advanced by the given size. template T ReadSizedIntLE(TFileCursor & f, typename TFileCursor::pos_type size) { static_assert(std::numeric_limits::is_integer == true, "Target type is a not an integer"); if (size == 0) { return 0; } if (!f.CanRead(size)) { return 0; } if (size < sizeof(T)) { return FileReader::ReadTruncatedIntLE(f, size); } T retval = FileReader::ReadIntLE(f); f.Skip(size - sizeof(T)); return retval; } // Read unsigned 32-Bit integer in little-endian format. // If successful, the file cursor is advanced by the size of the integer. template uint32 ReadUint32LE(TFileCursor & f) { return FileReader::ReadIntLE(f); } // Read unsigned 32-Bit integer in big-endian format. // If successful, the file cursor is advanced by the size of the integer. template uint32 ReadUint32BE(TFileCursor & f) { return FileReader::ReadIntBE(f); } // Read signed 32-Bit integer in little-endian format. // If successful, the file cursor is advanced by the size of the integer. template int32 ReadInt32LE(TFileCursor & f) { return FileReader::ReadIntLE(f); } // Read signed 32-Bit integer in big-endian format. // If successful, the file cursor is advanced by the size of the integer. template int32 ReadInt32BE(TFileCursor & f) { return FileReader::ReadIntBE(f); } // Read unsigned 24-Bit integer in little-endian format. // If successful, the file cursor is advanced by the size of the integer. template uint32 ReadUint24LE(TFileCursor & f) { const auto arr = FileReader::ReadArray(f); return arr[0] | (arr[1] << 8) | (arr[2] << 16); } // Read unsigned 24-Bit integer in big-endian format. // If successful, the file cursor is advanced by the size of the integer. template uint32 ReadUint24BE(TFileCursor & f) { const auto arr = FileReader::ReadArray(f); return (arr[0] << 16) | (arr[1] << 8) | arr[2]; } // Read unsigned 16-Bit integer in little-endian format. // If successful, the file cursor is advanced by the size of the integer. template uint16 ReadUint16LE(TFileCursor & f) { return FileReader::ReadIntLE(f); } // Read unsigned 16-Bit integer in big-endian format. // If successful, the file cursor is advanced by the size of the integer. template uint16 ReadUint16BE(TFileCursor & f) { return FileReader::ReadIntBE(f); } // Read signed 16-Bit integer in little-endian format. // If successful, the file cursor is advanced by the size of the integer. template int16 ReadInt16LE(TFileCursor & f) { return FileReader::ReadIntLE(f); } // Read signed 16-Bit integer in big-endian format. // If successful, the file cursor is advanced by the size of the integer. template int16 ReadInt16BE(TFileCursor & f) { return FileReader::ReadIntBE(f); } // Read a single 8bit character. // If successful, the file cursor is advanced by the size of the integer. template char ReadChar(TFileCursor & f) { char target; if (!FileReader::Read(f, target)) { return 0; } return target; } // Read unsigned 8-Bit integer. // If successful, the file cursor is advanced by the size of the integer. template uint8 ReadUint8(TFileCursor & f) { uint8 target; if (!FileReader::Read(f, target)) { return 0; } return target; } // Read signed 8-Bit integer. If successful, the file cursor is advanced by the size of the integer. template int8 ReadInt8(TFileCursor & f) { int8 target; if (!FileReader::Read(f, target)) { return 0; } return target; } // Read 32-Bit float in little-endian format. // If successful, the file cursor is advanced by the size of the float. template float ReadFloatLE(TFileCursor & f) { IEEE754binary32LE target; if (!FileReader::Read(f, target)) { return 0.0f; } return target; } // Read 32-Bit float in big-endian format. // If successful, the file cursor is advanced by the size of the float. template float ReadFloatBE(TFileCursor & f) { IEEE754binary32BE target; if (!FileReader::Read(f, target)) { return 0.0f; } return target; } // Read 64-Bit float in little-endian format. // If successful, the file cursor is advanced by the size of the float. template double ReadDoubleLE(TFileCursor & f) { IEEE754binary64LE target; if (!FileReader::Read(f, target)) { return 0.0; } return target; } // Read 64-Bit float in big-endian format. // If successful, the file cursor is advanced by the size of the float. template double ReadDoubleBE(TFileCursor & f) { IEEE754binary64BE target; if (!FileReader::Read(f, target)) { return 0.0; } return target; } // Read a struct. // If successful, the file cursor is advanced by the size of the struct. Otherwise, the target is zeroed. template bool ReadStruct(TFileCursor & f, T & target) { static_assert(mpt::is_binary_safe::value); if (!FileReader::Read(f, target)) { mpt::reset(target); return false; } return true; } // Allow to read a struct partially (if there's less memory available than the struct's size, fill it up with zeros). // The file cursor is advanced by "partialSize" bytes. template typename TFileCursor::pos_type ReadStructPartial(TFileCursor & f, T & target, typename TFileCursor::pos_type partialSize = sizeof(T)) { static_assert(mpt::is_binary_safe::value); typename TFileCursor::pos_type copyBytes = std::min(partialSize, sizeof(T)); if (!f.CanRead(copyBytes)) { copyBytes = f.BytesLeft(); } f.GetRaw(mpt::span(mpt::as_raw_memory(target).data(), copyBytes)); std::memset(mpt::as_raw_memory(target).data() + copyBytes, 0, sizeof(target) - copyBytes); f.Skip(partialSize); return copyBytes; } // Read a null-terminated string into a std::string template bool ReadNullString(TFileCursor & f, std::string & dest, const typename TFileCursor::pos_type maxLength = std::numeric_limits::max()) { dest.clear(); if (!f.CanRead(1)) { return false; } char buffer[mpt::IO::BUFFERSIZE_MINUSCULE]; typename TFileCursor::pos_type avail = 0; while ((avail = std::min(f.GetRaw(mpt::as_span(buffer)).size(), maxLength - dest.length())) != 0) { auto end = std::find(buffer, buffer + avail, '\0'); dest.insert(dest.end(), buffer, end); f.Skip(end - buffer); if (end < buffer + avail) { // Found null char f.Skip(1); break; } } return dest.length() != 0; } // Read a string up to the next line terminator into a std::string template bool ReadLine(TFileCursor & f, std::string & dest, const typename TFileCursor::pos_type maxLength = std::numeric_limits::max()) { dest.clear(); if (!f.CanRead(1)) { return false; } char buffer[mpt::IO::BUFFERSIZE_MINUSCULE]; char c = '\0'; typename TFileCursor::pos_type avail = 0; while ((avail = std::min(f.GetRaw(mpt::as_span(buffer)).size(), maxLength - dest.length())) != 0) { auto end = std::find_if(buffer, buffer + avail, mpt::is_any_line_ending); dest.insert(dest.end(), buffer, end); f.Skip(end - buffer); if (end < buffer + avail) { // Found line ending f.Skip(1); // Handle CRLF line ending if (*end == '\r') { if (FileReader::Read(f, c) && c != '\n') { f.SkipBack(1); } } break; } } return true; } // Compare a magic string with the current stream position. // Returns true if they are identical and advances the file cursor by the the length of the "magic" string. // Returns false if the string could not be found. The file cursor is not advanced in this case. template bool ReadMagic(TFileCursor & f, const char (&magic)[N]) { assert(magic[N - 1] == '\0'); for (std::size_t i = 0; i < N - 1; ++i) { assert(magic[i] != '\0'); } constexpr typename TFileCursor::pos_type magicLength = N - 1; std::byte buffer[magicLength] = {}; if (f.GetRaw(mpt::span(buffer, magicLength)).size() != magicLength) { return false; } if (std::memcmp(buffer, magic, magicLength)) { return false; } f.Skip(magicLength); return true; } // Read variable-length unsigned integer (as found in MIDI files). // If successful, the file cursor is advanced by the size of the integer and true is returned. // False is returned if not enough bytes were left to finish reading of the integer or if an overflow happened (source doesn't fit into target integer). // In case of an overflow, the target is also set to the maximum value supported by its data type. template bool ReadVarInt(TFileCursor & f, T & target) { static_assert(std::numeric_limits::is_integer == true && std::numeric_limits::is_signed == false, "Target type is not an unsigned integer"); if (f.NoBytesLeft()) { target = 0; return false; } std::byte bytes[16]; // More than enough for any valid VarInt typename TFileCursor::pos_type avail = f.GetRaw(mpt::as_span(bytes)).size(); typename TFileCursor::pos_type readPos = 1; uint8 b = mpt::byte_cast(bytes[0]); target = (b & 0x7F); std::size_t writtenBits = static_cast(mpt::bit_width(target)); // Bits used in the most significant byte while (readPos < avail && (b & 0x80) != 0) { b = mpt::byte_cast(bytes[readPos++]); target <<= 7; target |= (b & 0x7F); writtenBits += 7; if (readPos == avail) { f.Skip(readPos); avail = f.GetRaw(mpt::as_span(bytes)).size(); readPos = 0; } } f.Skip(readPos); if (writtenBits > sizeof(target) * 8u) { // Overflow target = std::numeric_limits::max(); return false; } else if ((b & 0x80) != 0) { // Reached EOF return false; } return true; } template struct ChunkHeader { using id_type = Tid; using size_type = Tsize; friend constexpr bool declare_binary_safe(const ChunkHeader &) noexcept { return true; } id_type id{}; size_type size{}; id_type GetID() const { return id; } size_type GetLength() const { return size; } }; template struct Chunk { TChunkHeader header; TFileCursor data; TChunkHeader GetHeader() const { return header; } TFileCursor GetData() const { return data; } }; template struct ChunkList { using id_type = decltype(TChunkHeader().GetID()); using size_type = decltype(TChunkHeader().GetLength()); std::vector> chunks; // Check if the list contains a given chunk. bool ChunkExists(id_type id) const { return std::find_if(chunks.begin(), chunks.end(), [id](const Chunk & chunk) { return chunk.GetHeader().GetID() == id; }) != chunks.end(); } // Retrieve the first chunk with a given ID. TFileCursor GetChunk(id_type id) const { auto chunk = std::find_if(chunks.begin(), chunks.end(), [id](const Chunk & chunk) { return chunk.GetHeader().GetID() == id; }); if (chunk == chunks.end()) { return TFileCursor(); } return chunk->GetData(); } // Retrieve all chunks with a given ID. std::vector GetAllChunks(id_type id) const { std::vector result; for (const auto & chunk : chunks) { if (chunk.GetHeader().GetID() == id) { result.push_back(chunk.GetData()); } } return result; } }; // Read a single "TChunkHeader" chunk. // T is required to have the methods GetID() and GetLength(). // GetLength() must return the chunk size in bytes, and GetID() the chunk ID. template Chunk ReadNextChunk(TFileCursor & f, typename TFileCursor::pos_type alignment) { Chunk result; if (!FileReader::Read(f, result.header)) { return Chunk(); } typename TFileCursor::pos_type dataSize = result.header.GetLength(); result.data = f.ReadChunk(dataSize); if (alignment > 1) { if ((dataSize % alignment) != 0) { f.Skip(alignment - (dataSize % alignment)); } } return result; } // Read a series of "TChunkHeader" chunks until the end of file is reached. // T is required to have the methods GetID() and GetLength(). // GetLength() must return the chunk size in bytes, and GetID() the chunk ID. template ChunkList ReadChunks(TFileCursor & f, typename TFileCursor::pos_type alignment) { ChunkList result; while (f.CanRead(sizeof(TChunkHeader))) { result.chunks.push_back(FileReader::ReadNextChunk(f, alignment)); } return result; } // Read a series of "TChunkHeader" chunks until a given chunk ID is found. // T is required to have the methods GetID() and GetLength(). // GetLength() must return the chunk size in bytes, and GetID() the chunk ID. template ChunkList ReadChunksUntil(TFileCursor & f, typename TFileCursor::pos_type alignment, decltype(TChunkHeader().GetID()) lastID) { ChunkList result; while (f.CanRead(sizeof(TChunkHeader))) { result.chunks.push_back(FileReader::ReadNextChunk(f, alignment)); if (result.chunks.back().GetHeader().GetID() == lastID) { break; } } return result; } } // namespace FileReader } // namespace IO } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_IO_READ_FILEREADER_HPP libopenmpt-0.6.1+release.autotools/src/mpt/io_write/0000755000175000017500000000000014175541574017561 500000000000000libopenmpt-0.6.1+release.autotools/src/mpt/io_write/buffer.hpp0000644000175000017500000000653414056153222021456 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_IO_WRITE_BUFFER_HPP #define MPT_IO_WRITE_BUFFER_HPP #include "mpt/base/memory.hpp" #include "mpt/base/namespace.hpp" #include "mpt/base/integer.hpp" #include "mpt/io/base.hpp" #include namespace mpt { inline namespace MPT_INLINE_NS { namespace IO { // WriteBuffer class that avoids calling to the underlying file writing // functions for every operation, which would involve rather slow un-inlinable // virtual calls in the iostream and FILE* cases. It is the users responabiliy // to call HasWriteError() to check for writeback errors at this buffering // level. template class WriteBuffer { private: mpt::byte_span buffer; std::size_t size = 0; Tfile & f; bool writeError = false; public: WriteBuffer(const WriteBuffer &) = delete; WriteBuffer & operator=(const WriteBuffer &) = delete; public: inline WriteBuffer(Tfile & f_, mpt::byte_span buffer_) : buffer(buffer_) , f(f_) { } inline ~WriteBuffer() noexcept(false) { if (!writeError) { FlushLocal(); } } public: inline Tfile & file() const { if (IsDirty()) { FlushLocal(); } return f; } public: inline bool HasWriteError() const { return writeError; } inline void ClearError() { writeError = false; } inline bool IsDirty() const { return size > 0; } inline bool IsClean() const { return size == 0; } inline bool IsFull() const { return size == buffer.size(); } inline std::size_t GetCurrentSize() const { return size; } inline bool Write(mpt::const_byte_span data) { bool result = true; for (std::size_t i = 0; i < data.size(); ++i) { buffer[size] = data[i]; size++; if (IsFull()) { FlushLocal(); } } return result; } inline void FlushLocal() { if (IsClean()) { return; } try { if (!mpt::IO::WriteRaw(f, mpt::as_span(buffer.data(), size))) { writeError = true; } } catch (const std::exception &) { writeError = true; throw; } size = 0; } }; template struct FileOperations> { private: WriteBuffer & f; public: FileOperations(WriteBuffer & f_) : f(f_) { return; } public: inline bool IsValid() { return IsValid(f.file()); } inline bool IsReadSeekable() { return IsReadSeekable(f.file()); } inline bool IsWriteSeekable() { return IsWriteSeekable(f.file()); } inline IO::Offset TellRead() { f.FlushLocal(); return TellRead(f.file()); } inline IO::Offset TellWrite() { return TellWrite(f.file()) + f.GetCurrentSize(); } inline bool SeekBegin() { f.FlushLocal(); return SeekBegin(f.file()); } inline bool SeekEnd() { f.FlushLocal(); return SeekEnd(f.file()); } inline bool SeekAbsolute(IO::Offset pos) { f.FlushLocal(); return SeekAbsolute(f.file(), pos); } inline bool SeekRelative(IO::Offset off) { f.FlushLocal(); return SeekRelative(f.file(), off); } inline mpt::byte_span ReadRawImpl(mpt::byte_span data) { f.FlushLocal(); return ReadRawImpl(f.file(), data); } inline bool WriteRawImpl(mpt::const_byte_span data) { return f.Write(data); } inline bool IsEof() { f.FlushLocal(); return IsEof(f.file()); } inline bool Flush() { f.FlushLocal(); return Flush(f.file()); } }; } // namespace IO } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_IO_WRITE_BUFFER_HPP libopenmpt-0.6.1+release.autotools/src/mpt/mutex/0000755000175000017500000000000014175541574017102 500000000000000libopenmpt-0.6.1+release.autotools/src/mpt/mutex/mutex.hpp0000644000175000017500000000730714075537227020703 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_MUTEX_MUTEX_HPP #define MPT_MUTEX_MUTEX_HPP #include "mpt/base/detect.hpp" #include "mpt/base/namespace.hpp" #if !MPT_PLATFORM_MULTITHREADED #define MPT_MUTEX_NONE 1 #elif MPT_COMPILER_GENERIC #define MPT_MUTEX_STD 1 #elif MPT_OS_WINDOWS && MPT_LIBCXX_GNU && !defined(_GLIBCXX_HAS_GTHREADS) && defined(MPT_WITH_MINGWSTDTHREADS) #define MPT_MUTEX_STD 1 #elif MPT_OS_WINDOWS && MPT_LIBCXX_GNU && !defined(_GLIBCXX_HAS_GTHREADS) #define MPT_MUTEX_WIN32 1 #else #define MPT_MUTEX_STD 1 #endif #ifndef MPT_MUTEX_STD #define MPT_MUTEX_STD 0 #endif #ifndef MPT_MUTEX_WIN32 #define MPT_MUTEX_WIN32 0 #endif #ifndef MPT_MUTEX_NONE #define MPT_MUTEX_NONE 0 #endif #if MPT_MUTEX_STD #if !MPT_COMPILER_GENERIC && (defined(__MINGW32__) || defined(__MINGW64__)) && !defined(_GLIBCXX_HAS_GTHREADS) && defined(MPT_WITH_MINGWSTDTHREADS) #include #else #include #ifdef MPT_COMPILER_QUIRK_COMPLEX_STD_MUTEX #include #include #endif #endif #elif MPT_MUTEX_WIN32 #include #endif // MPT_MUTEX namespace mpt { inline namespace MPT_INLINE_NS { #if MPT_MUTEX_STD #ifdef MPT_COMPILER_QUIRK_COMPLEX_STD_MUTEX using mutex = std::conditional::type; #else using mutex = std::mutex; #endif using recursive_mutex = std::recursive_mutex; #elif MPT_MUTEX_WIN32 #if (_WIN32_WINNT >= 0x0600) // _WIN32_WINNT_VISTA // compatible with c++11 std::mutex, can eventually be replaced without touching any usage site class mutex { private: SRWLOCK impl = SRWLOCK_INIT; public: mutex() = default; ~mutex() = default; void lock() { AcquireSRWLockExclusive(&impl); } bool try_lock() { return TryAcquireSRWLockExclusive(&impl) ? true : false; } void unlock() { ReleaseSRWLockExclusive(&impl); } }; #else // !_WIN32_WINNT_VISTA // compatible with c++11 std::mutex, can eventually be replaced without touching any usage site class mutex { private: CRITICAL_SECTION impl; public: mutex() { InitializeCriticalSection(&impl); } ~mutex() { DeleteCriticalSection(&impl); } void lock() { EnterCriticalSection(&impl); } bool try_lock() { return TryEnterCriticalSection(&impl) ? true : false; } void unlock() { LeaveCriticalSection(&impl); } }; #endif // _WIN32_WINNT_VISTA // compatible with c++11 std::recursive_mutex, can eventually be replaced without touching any usage site class recursive_mutex { private: CRITICAL_SECTION impl; public: recursive_mutex() { InitializeCriticalSection(&impl); } ~recursive_mutex() { DeleteCriticalSection(&impl); } void lock() { EnterCriticalSection(&impl); } bool try_lock() { return TryEnterCriticalSection(&impl) ? true : false; } void unlock() { LeaveCriticalSection(&impl); } }; #else // MPT_MUTEX_NONE class mutex { public: mutex() { return; } ~mutex() { return; } void lock() { return; } bool try_lock() { return true; } void unlock() { return; } }; class recursive_mutex { public: recursive_mutex() { return; } ~recursive_mutex() { return; } void lock() { return; } bool try_lock() { return true; } void unlock() { return; } }; #endif // MPT_MUTEX #if MPT_MUTEX_STD template using lock_guard = std::lock_guard; #else // !MPT_MUTEX_STD // compatible with c++11 std::lock_guard, can eventually be replaced without touching any usage site template class lock_guard { private: mutex_type & mutex; public: lock_guard(mutex_type & m) : mutex(m) { mutex.lock(); } ~lock_guard() { mutex.unlock(); } }; #endif // MPT_MUTEX_STD } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_MUTEX_MUTEX_HPP libopenmpt-0.6.1+release.autotools/src/mpt/osinfo/0000755000175000017500000000000014175541574017235 500000000000000libopenmpt-0.6.1+release.autotools/src/mpt/osinfo/class.hpp0000644000175000017500000000337714145400332020764 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_OSINFO_CLASS_HPP #define MPT_OSINFO_CLASS_HPP #include "mpt/base/detect_os.hpp" #include "mpt/base/namespace.hpp" #if !MPT_OS_WINDOWS #include "mpt/string/buffer.hpp" #endif // !MPT_OS_WINDOWS #include #if !MPT_OS_WINDOWS #include #endif // !MPT_OS_WINDOWS namespace mpt { inline namespace MPT_INLINE_NS { namespace osinfo { enum class osclass { Unknown, Windows, Linux, Darwin, BSD, Haiku, DOS, }; inline mpt::osinfo::osclass get_class_from_sysname(const std::string & sysname) { mpt::osinfo::osclass result = mpt::osinfo::osclass::Unknown; if (sysname == "") { result = mpt::osinfo::osclass::Unknown; } else if (sysname == "Windows" || sysname == "WindowsNT" || sysname == "Windows_NT") { result = mpt::osinfo::osclass::Windows; } else if (sysname == "Linux") { result = mpt::osinfo::osclass::Linux; } else if (sysname == "Darwin") { result = mpt::osinfo::osclass::Darwin; } else if (sysname == "FreeBSD" || sysname == "DragonFly" || sysname == "NetBSD" || sysname == "OpenBSD" || sysname == "MidnightBSD") { result = mpt::osinfo::osclass::BSD; } else if (sysname == "Haiku") { result = mpt::osinfo::osclass::Haiku; } else if (sysname == "MS-DOS") { result = mpt::osinfo::osclass::DOS; } return result; } inline mpt::osinfo::osclass get_class() { #if MPT_OS_WINDOWS return mpt::osinfo::osclass::Windows; #else // !MPT_OS_WINDOWS utsname uname_result; if (uname(&uname_result) != 0) { return mpt::osinfo::osclass::Unknown; } return mpt::osinfo::get_class_from_sysname(mpt::ReadAutoBuf(uname_result.sysname)); #endif // MPT_OS_WINDOWS } } // namespace osinfo } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_OSINFO_CLASS_HPP libopenmpt-0.6.1+release.autotools/src/mpt/osinfo/windows_version.hpp0000644000175000017500000002470214053134771023122 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_OSINFO_WINDOWS_VERSION_HPP #define MPT_OSINFO_WINDOWS_VERSION_HPP #include "mpt/base/detect.hpp" #include "mpt/base/integer.hpp" #include "mpt/base/namespace.hpp" #if MPT_OS_WINDOWS #include #endif // MPT_OS_WINDOWS namespace mpt { inline namespace MPT_INLINE_NS { namespace osinfo { namespace windows { class Version { public: enum Number : uint64 { WinNT4 = 0x0000000400000000ull, Win2000 = 0x0000000500000000ull, WinXP = 0x0000000500000001ull, WinXP64 = 0x0000000500000002ull, WinVista = 0x0000000600000000ull, Win7 = 0x0000000600000001ull, Win8 = 0x0000000600000002ull, Win81 = 0x0000000600000003ull, Win10 = 0x0000000a00000000ull, WinNewer = Win10 + 1ull }; struct System { uint32 Major = 0; uint32 Minor = 0; System() = default; constexpr System(Number number) noexcept : Major(static_cast((static_cast(number) >> 32) & 0xffffffffu)) , Minor(static_cast((static_cast(number) >> 0) & 0xffffffffu)) { } explicit constexpr System(uint64 number) noexcept : Major(static_cast((number >> 32) & 0xffffffffu)) , Minor(static_cast((number >> 0) & 0xffffffffu)) { } explicit constexpr System(uint32 major, uint32 minor) noexcept : Major(major) , Minor(minor) { } constexpr operator uint64() const noexcept { return (static_cast(Major) << 32) | (static_cast(Minor) << 0); } }; struct ServicePack { uint16 Major = 0; uint16 Minor = 0; ServicePack() = default; explicit constexpr ServicePack(uint16 major, uint16 minor) noexcept : Major(major) , Minor(minor) { } constexpr bool HasServicePack() const noexcept { return Major != 0 || Minor != 0; } constexpr operator uint32() const noexcept { return (static_cast(Major) << 16) | (static_cast(Minor) << 0); } }; typedef uint32 Build; typedef uint32 TypeId; protected: bool m_SystemIsWindows; System m_System; ServicePack m_ServicePack; Build m_Build; TypeId m_Type; protected: Version() noexcept : m_SystemIsWindows(false) , m_System() , m_ServicePack() , m_Build() , m_Type() { } public: static Version NoWindows() noexcept { return Version(); } Version(mpt::osinfo::windows::Version::System system, mpt::osinfo::windows::Version::ServicePack servicePack, mpt::osinfo::windows::Version::Build build, mpt::osinfo::windows::Version::TypeId type) noexcept : m_SystemIsWindows(true) , m_System(system) , m_ServicePack(servicePack) , m_Build(build) , m_Type(type) { } protected: #if MPT_OS_WINDOWS static mpt::osinfo::windows::Version VersionFromNTDDI_VERSION() noexcept { // Initialize to used SDK version mpt::osinfo::windows::Version::System System = #if NTDDI_VERSION >= 0x0A000000 // NTDDI_WIN10 mpt::osinfo::windows::Version::Win10 #elif NTDDI_VERSION >= 0x06030000 // NTDDI_WINBLUE mpt::osinfo::windows::Version::Win81 #elif NTDDI_VERSION >= 0x06020000 // NTDDI_WIN8 mpt::osinfo::windows::Version::Win8 #elif NTDDI_VERSION >= 0x06010000 // NTDDI_WIN7 mpt::osinfo::windows::Version::Win7 #elif NTDDI_VERSION >= 0x06000000 // NTDDI_VISTA mpt::osinfo::windows::Version::WinVista #elif NTDDI_VERSION >= 0x05020000 // NTDDI_WS03 mpt::osinfo::windows::Version::WinXP64 #elif NTDDI_VERSION >= NTDDI_WINXP mpt::osinfo::windows::Version::WinXP #elif NTDDI_VERSION >= NTDDI_WIN2K mpt::osinfo::windows::Version::Win2000 #else mpt::osinfo::windows::Version::WinNT4 #endif ; return mpt::osinfo::windows::Version(System, mpt::osinfo::windows::Version::ServicePack(((NTDDI_VERSION & 0xffffu) >> 8) & 0xffu, ((NTDDI_VERSION & 0xffffu) >> 0) & 0xffu), 0, 0); } static mpt::osinfo::windows::Version::System SystemVersionFrom_WIN32_WINNT() noexcept { #if defined(_WIN32_WINNT) return mpt::osinfo::windows::Version::System((static_cast(_WIN32_WINNT) & 0xff00u) >> 8, (static_cast(_WIN32_WINNT) & 0x00ffu) >> 0); #else return mpt::osinfo::windows::Version::System(); #endif } static mpt::osinfo::windows::Version GatherWindowsVersion() noexcept { #if MPT_OS_WINDOWS_WINRT return VersionFromNTDDI_VERSION(); #else // !MPT_OS_WINDOWS_WINRT OSVERSIONINFOEXW versioninfoex{}; versioninfoex.dwOSVersionInfoSize = sizeof(versioninfoex); #if MPT_COMPILER_MSVC #pragma warning(push) #pragma warning(disable : 4996) // 'GetVersionExW': was declared deprecated #pragma warning(disable : 28159) // Consider using 'IsWindows*' instead of 'GetVersionExW'. Reason: Deprecated. Use VerifyVersionInfo* or IsWindows* macros from VersionHelpers. #endif // MPT_COMPILER_MSVC #if MPT_COMPILER_CLANG #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" #endif // MPT_COMPILER_CLANG if (GetVersionExW((LPOSVERSIONINFOW)&versioninfoex) == FALSE) { return VersionFromNTDDI_VERSION(); } #if MPT_COMPILER_MSVC #pragma warning(pop) #endif // MPT_COMPILER_MSVC #if MPT_COMPILER_CLANG #pragma clang diagnostic pop #endif // MPT_COMPILER_CLANG if (versioninfoex.dwPlatformId != VER_PLATFORM_WIN32_NT) { return VersionFromNTDDI_VERSION(); } DWORD dwProductType = 0; #if (_WIN32_WINNT >= 0x0600) // _WIN32_WINNT_VISTA dwProductType = PRODUCT_UNDEFINED; if (GetProductInfo(versioninfoex.dwMajorVersion, versioninfoex.dwMinorVersion, versioninfoex.wServicePackMajor, versioninfoex.wServicePackMinor, &dwProductType) == FALSE) { dwProductType = PRODUCT_UNDEFINED; } #endif return mpt::osinfo::windows::Version( mpt::osinfo::windows::Version::System(versioninfoex.dwMajorVersion, versioninfoex.dwMinorVersion), mpt::osinfo::windows::Version::ServicePack(versioninfoex.wServicePackMajor, versioninfoex.wServicePackMinor), versioninfoex.dwBuildNumber, dwProductType); #endif // MPT_OS_WINDOWS_WINRT } #endif // MPT_OS_WINDOWS public: static mpt::osinfo::windows::Version Current() noexcept { #if MPT_OS_WINDOWS return GatherWindowsVersion(); #else // !MPT_OS_WINDOWS return mpt::osinfo::windows::Version::NoWindows(); #endif // MPT_OS_WINDOWS } public: bool IsWindows() const noexcept { return m_SystemIsWindows; } bool IsBefore(mpt::osinfo::windows::Version::System version) const noexcept { if (!m_SystemIsWindows) { return false; } return m_System < version; } bool IsBefore(mpt::osinfo::windows::Version::System version, mpt::osinfo::windows::Version::ServicePack servicePack) const noexcept { if (!m_SystemIsWindows) { return false; } if (m_System > version) { return false; } if (m_System < version) { return true; } return m_ServicePack < servicePack; } bool IsBefore(mpt::osinfo::windows::Version::System version, mpt::osinfo::windows::Version::Build build) const noexcept { if (!m_SystemIsWindows) { return false; } if (m_System > version) { return false; } if (m_System < version) { return true; } return m_Build < build; } bool IsBefore(mpt::osinfo::windows::Version::System version, mpt::osinfo::windows::Version::ServicePack servicePack, mpt::osinfo::windows::Version::Build build) const noexcept { if (!m_SystemIsWindows) { return false; } if (m_System > version) { return false; } if (m_System < version) { return true; } if (m_ServicePack > servicePack) { return false; } if (m_ServicePack < servicePack) { return true; } return m_Build < build; } bool IsAtLeast(mpt::osinfo::windows::Version::System version) const noexcept { if (!m_SystemIsWindows) { return false; } return m_System >= version; } bool IsAtLeast(mpt::osinfo::windows::Version::System version, mpt::osinfo::windows::Version::ServicePack servicePack) const noexcept { if (!m_SystemIsWindows) { return false; } if (m_System < version) { return false; } if (m_System > version) { return true; } return m_ServicePack >= servicePack; } bool IsAtLeast(mpt::osinfo::windows::Version::System version, mpt::osinfo::windows::Version::Build build) const noexcept { if (!m_SystemIsWindows) { return false; } if (m_System < version) { return false; } if (m_System > version) { return true; } return m_Build >= build; } bool IsAtLeast(mpt::osinfo::windows::Version::System version, mpt::osinfo::windows::Version::ServicePack servicePack, mpt::osinfo::windows::Version::Build build) const noexcept { if (!m_SystemIsWindows) { return false; } if (m_System < version) { return false; } if (m_System > version) { return true; } if (m_ServicePack < servicePack) { return false; } if (m_ServicePack > servicePack) { return true; } return m_Build >= build; } mpt::osinfo::windows::Version::System GetSystem() const noexcept { return m_System; } mpt::osinfo::windows::Version::ServicePack GetServicePack() const noexcept { return m_ServicePack; } mpt::osinfo::windows::Version::Build GetBuild() const noexcept { return m_Build; } mpt::osinfo::windows::Version::TypeId GetTypeId() const noexcept { return m_Type; } }; // class Version namespace wine { class version { protected: bool valid = false; uint8 vmajor = 0; uint8 vminor = 0; uint8 vupdate = 0; public: version() { return; } version(uint8 vmajor_, uint8 vminor_, uint8 vupdate_) : valid(true) , vmajor(vmajor_) , vminor(vminor_) , vupdate(vupdate_) { return; } public: bool IsValid() const { return true; } private: static mpt::osinfo::windows::wine::version FromInteger(uint32 version) { mpt::osinfo::windows::wine::version result; result.valid = (version <= 0xffffff); result.vmajor = static_cast(version >> 16); result.vminor = static_cast(version >> 8); result.vupdate = static_cast(version >> 0); return result; } uint32 AsInteger() const { uint32 version = 0; version |= static_cast(vmajor) << 16; version |= static_cast(vminor) << 8; version |= static_cast(vupdate) << 0; return version; } public: bool IsBefore(mpt::osinfo::windows::wine::version other) const { if (!IsValid()) { return false; } return (AsInteger() < other.AsInteger()); } bool IsAtLeast(mpt::osinfo::windows::wine::version other) const { if (!IsValid()) { return false; } return (AsInteger() >= other.AsInteger()); } uint8 GetMajor() const { return vmajor; } uint8 GetMinor() const { return vminor; } uint8 GetUpdate() const { return vupdate; } }; } // namespace wine } // namespace windows } // namespace osinfo } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_OSINFO_WINDOWS_VERSION_HPP libopenmpt-0.6.1+release.autotools/src/mpt/out_of_memory/0000755000175000017500000000000014175541574020623 500000000000000libopenmpt-0.6.1+release.autotools/src/mpt/out_of_memory/out_of_memory.hpp0000644000175000017500000000315314075537720024136 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_OUT_OF_MEMORY_OUT_OF_MEMORY_HPP #define MPT_OUT_OF_MEMORY_OUT_OF_MEMORY_HPP #include "mpt/base/detect.hpp" #include "mpt/base/macros.hpp" #include "mpt/base/namespace.hpp" #include "mpt/detect/mfc.hpp" #include #if !MPT_DETECTED_MFC #include #endif // !MPT_DETECTED_MFC #if MPT_DETECTED_MFC // cppcheck-suppress missingInclude #include #endif // MPT_DETECTED_MFC namespace mpt { inline namespace MPT_INLINE_NS { // Exception handling helpers, because MFC requires explicit deletion of the exception object, // Thus, always call exactly one of mpt::rethrow_out_of_memory(e) or mpt::delete_out_of_memory(e). #if MPT_DETECTED_MFC using out_of_memory = CMemoryException *; [[noreturn]] inline void throw_out_of_memory() { AfxThrowMemoryException(); } [[noreturn]] inline void rethrow_out_of_memory(out_of_memory e) { MPT_UNUSED(e); // cppcheck false-positive // cppcheck-suppress rethrowNoCurrentException throw; } inline void delete_out_of_memory(out_of_memory & e) { if (e) { e->Delete(); e = nullptr; } } #else // !MPT_DETECTED_MFC using out_of_memory = const std::bad_alloc &; [[noreturn]] inline void throw_out_of_memory() { throw std::bad_alloc(); } [[noreturn]] inline void rethrow_out_of_memory(out_of_memory e) { MPT_UNUSED(e); // cppcheck false-positive // cppcheck-suppress rethrowNoCurrentException throw; } inline void delete_out_of_memory(out_of_memory e) { MPT_UNUSED(e); } #endif // MPT_DETECTED_MFC } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_OUT_OF_MEMORY_OUT_OF_MEMORY_HPP libopenmpt-0.6.1+release.autotools/src/mpt/parse/0000755000175000017500000000000014175541574017052 500000000000000libopenmpt-0.6.1+release.autotools/src/mpt/parse/tests/0000755000175000017500000000000014175541575020215 500000000000000libopenmpt-0.6.1+release.autotools/src/mpt/parse/tests/tests_parse.hpp0000644000175000017500000000517014044173026023170 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_PARSE_TESTS_PARSE_HPP #define MPT_PARSE_TESTS_PARSE_HPP #include "mpt/base/detect.hpp" #include "mpt/base/integer.hpp" #include "mpt/base/namespace.hpp" #include "mpt/format/simple.hpp" #include "mpt/parse/parse.hpp" #include "mpt/string/types.hpp" #include "mpt/test/test.hpp" #include "mpt/test/test_macros.hpp" #include namespace mpt { inline namespace MPT_INLINE_NS { namespace tests { namespace parse { #if MPT_COMPILER_CLANG #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wglobal-constructors" #endif MPT_TEST_GROUP_INLINE("mpt/parse") #if MPT_COMPILER_CLANG #pragma clang diagnostic pop #endif { MPT_TEST_EXPECT_EQUAL(mpt::ConvertStringTo("586"), 586u); MPT_TEST_EXPECT_EQUAL(mpt::ConvertStringTo("2147483647"), (uint32)std::numeric_limits::max()); MPT_TEST_EXPECT_EQUAL(mpt::ConvertStringTo("4294967295"), std::numeric_limits::max()); MPT_TEST_EXPECT_EQUAL(mpt::ConvertStringTo("-9223372036854775808"), std::numeric_limits::min()); MPT_TEST_EXPECT_EQUAL(mpt::ConvertStringTo("-159"), -159); MPT_TEST_EXPECT_EQUAL(mpt::ConvertStringTo("9223372036854775807"), std::numeric_limits::max()); MPT_TEST_EXPECT_EQUAL(mpt::ConvertStringTo("85059"), 85059u); MPT_TEST_EXPECT_EQUAL(mpt::ConvertStringTo("9223372036854775807"), (uint64)std::numeric_limits::max()); MPT_TEST_EXPECT_EQUAL(mpt::ConvertStringTo("18446744073709551615"), std::numeric_limits::max()); MPT_TEST_EXPECT_EQUAL(mpt::ConvertStringTo("-87.0"), -87.0f); #if !MPT_OS_DJGPP MPT_TEST_EXPECT_EQUAL(mpt::ConvertStringTo("-0.5e-6"), -0.5e-6); #endif #if !MPT_OS_DJGPP MPT_TEST_EXPECT_EQUAL(mpt::ConvertStringTo("58.65403492763"), 58.65403492763); #else MPT_TEST_EXPECT_EQUAL(std::abs(mpt::ConvertStringTo("58.65403492763") - 58.65403492763) <= 0.0001, true); #endif MPT_TEST_EXPECT_EQUAL(mpt::ConvertStringTo(mpt::format::val(-87.0)), -87.0f); #if !MPT_OS_DJGPP MPT_TEST_EXPECT_EQUAL(mpt::ConvertStringTo(mpt::format::val(-0.5e-6)), -0.5e-6); #endif MPT_TEST_EXPECT_EQUAL(mpt::ConvertHexStringTo("fe"), 254); #if !defined(MPT_COMPILER_QUIRK_NO_WCHAR) MPT_TEST_EXPECT_EQUAL(mpt::ConvertHexStringTo(L"fe"), 254); #endif // !MPT_COMPILER_QUIRK_NO_WCHAR MPT_TEST_EXPECT_EQUAL(mpt::ConvertHexStringTo(MPT_USTRING("ffff")), 65535u); } } // namespace parse } // namespace tests } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_PARSE_TESTS_PARSE_HPP libopenmpt-0.6.1+release.autotools/src/mpt/parse/parse.hpp0000644000175000017500000000504214107230757020607 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_PARSE_PARSE_HPP #define MPT_PARSE_PARSE_HPP #include "mpt/base/detect.hpp" #include "mpt/base/namespace.hpp" #include "mpt/string/types.hpp" #include "mpt/string_transcode/transcode.hpp" #include #include #include #include #include namespace mpt { inline namespace MPT_INLINE_NS { inline std::string parse_as_internal_string_type(const std::string & s) { return s; } #if !defined(MPT_COMPILER_QUIRK_NO_WCHAR) inline std::wstring parse_as_internal_string_type(const std::wstring & s) { return s; } #endif // !MPT_COMPILER_QUIRK_NO_WCHAR #if MPT_USTRING_MODE_WIDE template inline std::wstring parse_as_internal_string_type(const Tstring & s) { return mpt::transcode(s); } #else // !MPT_USTRING_MODE_WIDE template inline std::string parse_as_internal_string_type(const Tstring & s) { return mpt::transcode(mpt::common_encoding::utf8, s); } #endif // MPT_USTRING_MODE_WIDE template inline T ConvertStringTo(const Tstring & str) { std::basic_istringstream stream(mpt::parse_as_internal_string_type(mpt::as_string(str))); stream.imbue(std::locale::classic()); T value; if constexpr (std::is_same::value) { signed int tmp; if (!(stream >> tmp)) { return T{}; } value = static_cast(tmp); } else if constexpr (std::is_same::value) { unsigned int tmp; if (!(stream >> tmp)) { return T{}; } value = static_cast(tmp); } else { if (!(stream >> value)) { return T{}; } } return value; } template inline T ConvertHexStringTo(const Tstring & str) { std::basic_istringstream stream(mpt::parse_as_internal_string_type(mpt::as_string(str))); stream.imbue(std::locale::classic()); T value; if constexpr (std::is_same::value) { signed int tmp; if (!(stream >> std::hex >> tmp)) { return T{}; } value = static_cast(tmp); } else if constexpr (std::is_same::value) { unsigned int tmp; if (!(stream >> std::hex >> tmp)) { return T{}; } value = static_cast(tmp); } else { if (!(stream >> std::hex >> value)) { return T{}; } } return value; } } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_PARSE_PARSE_HPP libopenmpt-0.6.1+release.autotools/src/mpt/random/0000755000175000017500000000000014175541574017220 500000000000000libopenmpt-0.6.1+release.autotools/src/mpt/random/tests/0000755000175000017500000000000014175541575020363 500000000000000libopenmpt-0.6.1+release.autotools/src/mpt/random/tests/tests_random.hpp0000644000175000017500000000547614044173026023515 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_BASE_TESTS_RANDOM_HPP #define MPT_BASE_TESTS_RANDOM_HPP #include "mpt/base/algorithm.hpp" #include "mpt/base/detect_compiler.hpp" #include "mpt/base/integer.hpp" #include "mpt/base/namespace.hpp" #include "mpt/random/default_engines.hpp" #include "mpt/random/device.hpp" #include "mpt/random/random.hpp" #include "mpt/test/test.hpp" #include "mpt/test/test_macros.hpp" #include namespace mpt { inline namespace MPT_INLINE_NS { namespace tests { namespace random { #if MPT_COMPILER_CLANG #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wglobal-constructors" #endif MPT_TEST_GROUP_INLINE("mpt/random") #if MPT_COMPILER_CLANG #pragma clang diagnostic pop #endif { mpt::sane_random_device rd; mpt::good_engine prng = mpt::make_prng(rd); bool failed = false; for (std::size_t i = 0; i < 10000; ++i) { failed = failed || !mpt::is_in_range(mpt::random(prng), 0u, 127u); failed = failed || !mpt::is_in_range(mpt::random(prng), 0u, 255u); failed = failed || !mpt::is_in_range(mpt::random(prng), 0u, 511u); failed = failed || !mpt::is_in_range(mpt::random(prng), 0u, 1u); failed = failed || !mpt::is_in_range(mpt::random(prng, 7), 0u, 127u); failed = failed || !mpt::is_in_range(mpt::random(prng, 8), 0u, 255u); failed = failed || !mpt::is_in_range(mpt::random(prng, 9), 0u, 511u); failed = failed || !mpt::is_in_range(mpt::random(prng, 1), 0u, 1u); failed = failed || !mpt::is_in_range(mpt::random(prng), 0, 127); failed = failed || !mpt::is_in_range(mpt::random(prng), 0, 255); failed = failed || !mpt::is_in_range(mpt::random(prng), 0, 511); failed = failed || !mpt::is_in_range(mpt::random(prng), 0, 1); failed = failed || !mpt::is_in_range(mpt::random(prng, 7), 0, 127); failed = failed || !mpt::is_in_range(mpt::random(prng, 8), 0, 255); failed = failed || !mpt::is_in_range(mpt::random(prng, 9), 0, 511); failed = failed || !mpt::is_in_range(mpt::random(prng, 1), 0, 1); failed = failed || !mpt::is_in_range(mpt::random(prng, 0.0f, 1.0f), 0.0f, 1.0f); failed = failed || !mpt::is_in_range(mpt::random(prng, 0.0, 1.0), 0.0, 1.0); failed = failed || !mpt::is_in_range(mpt::random(prng, -1.0, 1.0), -1.0, 1.0); failed = failed || !mpt::is_in_range(mpt::random(prng, -1.0, 0.0), -1.0, 0.0); failed = failed || !mpt::is_in_range(mpt::random(prng, 1.0, 2.0), 1.0, 2.0); failed = failed || !mpt::is_in_range(mpt::random(prng, 1.0, 3.0), 1.0, 3.0); } MPT_TEST_EXPECT_EXPR(!failed); } } // namespace random } // namespace tests } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_BASE_TESTS_RANDOM_HPP libopenmpt-0.6.1+release.autotools/src/mpt/random/crand.hpp0000644000175000017500000000175114044173026020730 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_RANDOM_CRAND_HPP #define MPT_RANDOM_CRAND_HPP #include "mpt/base/namespace.hpp" #include "mpt/base/numeric.hpp" #include "mpt/random/random.hpp" #include namespace mpt { inline namespace MPT_INLINE_NS { class crand { public: using state_type = void; using result_type = int; private: static void reseed(uint32 seed) { std::srand(seed); } public: template static void reseed(Trd & rd) { reseed(mpt::random(rd)); } public: crand() = default; explicit crand(const std::string &) { return; } public: static MPT_CONSTEXPRINLINE result_type min() { return 0; } static MPT_CONSTEXPRINLINE result_type max() { return RAND_MAX; } static MPT_CONSTEXPRINLINE int result_bits() { return mpt::lower_bound_entropy_bits(RAND_MAX); } result_type operator()() { return std::rand(); } }; } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_RANDOM_CRAND_HPP libopenmpt-0.6.1+release.autotools/src/mpt/random/default_engines.hpp0000644000175000017500000000146614044173026023000 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_RANDOM_DEFAULT_ENGINES_HPP #define MPT_RANDOM_DEFAULT_ENGINES_HPP #include "mpt/base/detect.hpp" #include "mpt/base/integer.hpp" #include "mpt/base/namespace.hpp" #include "mpt/random/engine.hpp" #include "mpt/random/engine_lcg.hpp" #include namespace mpt { inline namespace MPT_INLINE_NS { using deterministic_fast_engine = mpt::lcg_msvc; using deterministic_good_engine = mpt::lcg_musl; // We cannot use std::minstd_rand here because it has not a power-of-2 sized // output domain which we rely upon. using fast_engine = mpt::lcg_msvc; // about 3 ALU operations, ~32bit of state, suited for inner loops using good_engine = std::ranlux48; } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_RANDOM_DEFAULT_ENGINES_HPP libopenmpt-0.6.1+release.autotools/src/mpt/random/device.hpp0000644000175000017500000002174214140673246021110 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_RANDOM_DEVICE_HPP #define MPT_RANDOM_DEVICE_HPP #include "mpt/base/bit.hpp" #include "mpt/base/detect.hpp" #include "mpt/base/macros.hpp" #include "mpt/base/math.hpp" #include "mpt/base/integer.hpp" #include "mpt/base/namespace.hpp" #include "mpt/crc/crc.hpp" #include "mpt/endian/integer.hpp" #include "mpt/mutex/mutex.hpp" #include "mpt/out_of_memory/out_of_memory.hpp" #include "mpt/random/engine.hpp" #include "mpt/random/engine_lcg.hpp" #include "mpt/random/random.hpp" #include #include #include #include #include #include #include namespace mpt { inline namespace MPT_INLINE_NS { inline constexpr uint32 DETERMINISTIC_RNG_SEED = 3141592653u; // pi template struct default_radom_seed_hash { }; template <> struct default_radom_seed_hash { using type = mpt::crc16; }; template <> struct default_radom_seed_hash { using type = mpt::crc16; }; template <> struct default_radom_seed_hash { using type = mpt::crc32c; }; template <> struct default_radom_seed_hash { using type = mpt::crc64_jones; }; class prng_random_device_time_seeder { public: template inline T generate_seed() { // Note: CRC is actually not that good a choice here, but it is simple and we // already have an implementaion available. Better choices for mixing entropy // would be a hash function with proper avalanche characteristics or a block // or stream cipher with any pre-choosen random key and IV. The only aspect we // really need here is whitening of the bits. typename mpt::default_radom_seed_hash::type hash; { uint64be time; time = std::chrono::duration_cast(std::chrono::system_clock().now().time_since_epoch()).count(); std::byte bytes[sizeof(time)]; std::memcpy(bytes, &time, sizeof(time)); hash(std::begin(bytes), std::end(bytes)); } #if !defined(MPT_COMPILER_QUIRK_CHRONO_NO_HIGH_RESOLUTION_CLOCK) // Avoid std::chrono::high_resolution_clock on Emscripten because availability is problematic in AudioWorklet context. { uint64be time; time = std::chrono::duration_cast(std::chrono::high_resolution_clock().now().time_since_epoch()).count(); std::byte bytes[sizeof(time)]; std::memcpy(bytes, &time, sizeof(time)); hash(std::begin(bytes), std::end(bytes)); } #endif // !MPT_COMPILER_QUIRK_CHRONO_NO_HIGH_RESOLUTION_CLOCK return static_cast(hash.result()); } public: prng_random_device_time_seeder() = default; }; // C++11 std::random_device may be implemented as a deterministic PRNG. // There is no way to seed this PRNG and it is allowed to be seeded with the // same value on each program invocation. This makes std::random_device // completely useless even as a non-cryptographic entropy pool. // We fallback to time-seeded std::mt19937 if std::random_device::entropy() is // 0 or less. class sane_random_device { private: mpt::mutex m; std::string token; #if !defined(MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE) std::unique_ptr prd; bool rd_reliable{false}; #endif // !MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE std::unique_ptr rd_fallback; public: using result_type = unsigned int; private: void init_fallback() { if (!rd_fallback) { if (token.length() > 0) { uint64 seed_val = mpt::prng_random_device_time_seeder().generate_seed(); std::vector seeds; seeds.push_back(static_cast(seed_val >> 32)); seeds.push_back(static_cast(seed_val >> 0)); for (std::size_t i = 0; i < token.length(); ++i) { seeds.push_back(static_cast(static_cast(token[i]))); } std::seed_seq seed(seeds.begin(), seeds.end()); rd_fallback = std::make_unique(seed); } else { uint64 seed_val = mpt::prng_random_device_time_seeder().generate_seed(); unsigned int seeds[2]; seeds[0] = static_cast(seed_val >> 32); seeds[1] = static_cast(seed_val >> 0); std::seed_seq seed(seeds + 0, seeds + 2); rd_fallback = std::make_unique(seed); } } } public: sane_random_device() { #if !defined(MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE) try { prd = std::make_unique(); rd_reliable = ((*prd).entropy() > 0.0); } catch (mpt::out_of_memory e) { mpt::rethrow_out_of_memory(e); } catch (const std::exception &) { rd_reliable = false; } if (!rd_reliable) { init_fallback(); } #else // MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE init_fallback(); #endif // !MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE } sane_random_device(const std::string & token_) : token(token_) { #if !defined(MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE) try { prd = std::make_unique(token); rd_reliable = ((*prd).entropy() > 0.0); } catch (mpt::out_of_memory e) { mpt::rethrow_out_of_memory(e); } catch (const std::exception &) { rd_reliable = false; } if (!rd_reliable) { init_fallback(); } #else // MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE init_fallback(); #endif // !MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE } static MPT_CONSTEXPRINLINE result_type min() { return std::numeric_limits::min(); } static MPT_CONSTEXPRINLINE result_type max() { return std::numeric_limits::max(); } static MPT_CONSTEXPRINLINE int result_bits() { return sizeof(result_type) * 8; } result_type operator()() { mpt::lock_guard l(m); result_type result = 0; #if !defined(MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE) if (prd) { try { if constexpr (std::random_device::min() != 0 || !mpt::is_mask(std::random_device::max())) { // insane std::random_device // This implementation is not exactly uniformly distributed but good enough // for OpenMPT. constexpr double rd_min = static_cast(std::random_device::min()); constexpr double rd_max = static_cast(std::random_device::max()); constexpr double rd_range = rd_max - rd_min; constexpr double rd_size = rd_range + 1.0; const double rd_entropy = mpt::log2(rd_size); const int iterations = static_cast(std::ceil(result_bits() / rd_entropy)); double tmp = 0.0; for (int i = 0; i < iterations; ++i) { tmp = (tmp * rd_size) + (static_cast((*prd)()) - rd_min); } double result_01 = std::floor(tmp / std::pow(rd_size, iterations)); result = static_cast(std::floor(result_01 * (static_cast(max() - min()) + 1.0))) + min(); } else { // sane std::random_device result = 0; std::size_t rd_bits = mpt::lower_bound_entropy_bits(std::random_device::max()); for (std::size_t entropy = 0; entropy < (sizeof(result_type) * 8); entropy += rd_bits) { if (rd_bits < (sizeof(result_type) * 8)) { result = (result << rd_bits) | static_cast((*prd)()); } else { result = result | static_cast((*prd)()); } } } } catch (const std::exception &) { rd_reliable = false; init_fallback(); } } else { rd_reliable = false; } if (!rd_reliable) { // std::random_device is unreliable // XOR the generated random number with more entropy from the time-seeded // PRNG. // Note: This is safe even if the std::random_device itself is implemented // as a std::mt19937 PRNG because we are very likely using a different // seed. result ^= mpt::random(*rd_fallback); } #else // MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE result ^= mpt::random(*rd_fallback); #endif // !MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE return result; } }; class prng_random_device_deterministic_seeder { protected: template constexpr T generate_seed() noexcept { return static_cast(mpt::DETERMINISTIC_RNG_SEED); } protected: prng_random_device_deterministic_seeder() = default; }; template class prng_random_device : private seeder { public: using result_type = unsigned int; private: mpt::mutex m; Trng rng; public: prng_random_device() : rng(seeder::template generate_seed()) { return; } prng_random_device(const std::string &) : rng(seeder::template generate_seed()) { return; } static MPT_CONSTEXPRINLINE result_type min() { return std::numeric_limits::min(); } static MPT_CONSTEXPRINLINE result_type max() { return std::numeric_limits::max(); } static MPT_CONSTEXPRINLINE int result_bits() { return sizeof(unsigned int) * 8; } result_type operator()() { mpt::lock_guard l(m); return mpt::random(rng); } }; using deterministc_random_device = mpt::prng_random_device; } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_RANDOM_DEVICE_HPP libopenmpt-0.6.1+release.autotools/src/mpt/random/engine.hpp0000644000175000017500000001025314044173026021103 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_RANDOM_ENGINE_HPP #define MPT_RANDOM_ENGINE_HPP #include "mpt/base/macros.hpp" #include "mpt/base/namespace.hpp" #include "mpt/random/seed.hpp" #include #include #include namespace mpt { inline namespace MPT_INLINE_NS { template struct engine_traits { typedef typename Trng::result_type result_type; static MPT_CONSTEXPRINLINE int result_bits() { return Trng::result_bits(); } template static inline Trng make(Trd & rd) { return Trng(rd); } }; // C++11 random does not provide any sane way to determine the amount of entropy // required to seed a particular engine. VERY STUPID. // List the ones we are likely to use. template <> struct engine_traits { enum : std::size_t { seed_bits = sizeof(std::mt19937::result_type) * 8 * std::mt19937::state_size }; typedef std::mt19937 rng_type; typedef rng_type::result_type result_type; static MPT_CONSTEXPRINLINE int result_bits() { return rng_type::word_size; } template static inline rng_type make(Trd & rd) { std::unique_ptr> values = std::make_unique>(rd); std::seed_seq seed(values->begin(), values->end()); return rng_type(seed); } }; template <> struct engine_traits { enum : std::size_t { seed_bits = sizeof(std::mt19937_64::result_type) * 8 * std::mt19937_64::state_size }; typedef std::mt19937_64 rng_type; typedef rng_type::result_type result_type; static MPT_CONSTEXPRINLINE int result_bits() { return rng_type::word_size; } template static inline rng_type make(Trd & rd) { std::unique_ptr> values = std::make_unique>(rd); std::seed_seq seed(values->begin(), values->end()); return rng_type(seed); } }; template <> struct engine_traits { enum : std::size_t { seed_bits = std::ranlux24_base::word_size }; typedef std::ranlux24_base rng_type; typedef rng_type::result_type result_type; static MPT_CONSTEXPRINLINE int result_bits() { return rng_type::word_size; } template static inline rng_type make(Trd & rd) { mpt::seed_seq_values values(rd); std::seed_seq seed(values.begin(), values.end()); return rng_type(seed); } }; template <> struct engine_traits { enum : std::size_t { seed_bits = std::ranlux48_base::word_size }; typedef std::ranlux48_base rng_type; typedef rng_type::result_type result_type; static MPT_CONSTEXPRINLINE int result_bits() { return rng_type::word_size; } template static inline rng_type make(Trd & rd) { mpt::seed_seq_values values(rd); std::seed_seq seed(values.begin(), values.end()); return rng_type(seed); } }; template <> struct engine_traits { enum : std::size_t { seed_bits = std::ranlux24_base::word_size }; typedef std::ranlux24 rng_type; typedef rng_type::result_type result_type; static MPT_CONSTEXPRINLINE int result_bits() { return std::ranlux24_base::word_size; } template static inline rng_type make(Trd & rd) { mpt::seed_seq_values values(rd); std::seed_seq seed(values.begin(), values.end()); return rng_type(seed); } }; template <> struct engine_traits { enum : std::size_t { seed_bits = std::ranlux48_base::word_size }; typedef std::ranlux48 rng_type; typedef rng_type::result_type result_type; static MPT_CONSTEXPRINLINE int result_bits() { return std::ranlux48_base::word_size; } template static inline rng_type make(Trd & rd) { mpt::seed_seq_values values(rd); std::seed_seq seed(values.begin(), values.end()); return rng_type(seed); } }; template inline Trng make_prng(Trd & rd) { return mpt::engine_traits::make(rd); } } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_RANDOM_ENGINE_HPP libopenmpt-0.6.1+release.autotools/src/mpt/random/engine_lcg.hpp0000644000175000017500000000470114053471040021726 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_RANDOM_ENGINE_LCG_HPP #define MPT_RANDOM_ENGINE_LCG_HPP #include "mpt/base/detect.hpp" #include "mpt/base/macros.hpp" #include "mpt/base/integer.hpp" #include "mpt/base/namespace.hpp" #include "mpt/base/numeric.hpp" #include "mpt/random/random.hpp" #include namespace mpt { inline namespace MPT_INLINE_NS { #if MPT_COMPILER_MSVC #pragma warning(push) #pragma warning(disable : 4724) // potential mod by 0 #endif // MPT_COMPILER_MSVC template class lcg_engine { public: typedef Tstate state_type; typedef Tvalue result_type; private: state_type state; public: template explicit inline lcg_engine(Trng & rd) : state(mpt::random(rd)) { operator()(); // we return results from the current state and update state after returning. results in better pipelining. } explicit inline lcg_engine(state_type seed) : state(seed) { operator()(); // we return results from the current state and update state after returning. results in better pipelining. } public: static MPT_CONSTEXPRINLINE result_type min() { return static_cast(0); } static MPT_CONSTEXPRINLINE result_type max() { static_assert(((result_mask >> result_shift) << result_shift) == result_mask); return static_cast(result_mask >> result_shift); } static MPT_CONSTEXPRINLINE int result_bits() { static_assert(((static_cast(1) << result_bits_) - 1) == (result_mask >> result_shift)); return result_bits_; } inline result_type operator()() { // we return results from the current state and update state after returning. results in better pipelining. state_type s = state; result_type result = static_cast((s & result_mask) >> result_shift); s = mpt::modulo_if_not_zero((a * s) + c); state = s; return result; } }; #if MPT_COMPILER_MSVC #pragma warning(pop) #endif // MPT_COMPILER_MSVC typedef lcg_engine lcg_msvc; typedef lcg_engine lcg_c99; typedef lcg_engine lcg_musl; } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_RANDOM_ENGINE_LCG_HPP libopenmpt-0.6.1+release.autotools/src/mpt/random/random.hpp0000644000175000017500000000742014044173026021120 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_RANDOM_RANDOM_HPP #define MPT_RANDOM_RANDOM_HPP #include "mpt/base/namespace.hpp" #include "mpt/random/engine.hpp" #include namespace mpt { inline namespace MPT_INLINE_NS { template inline T random(Trng & rng) { static_assert(std::numeric_limits::is_integer); typedef typename std::make_unsigned::type unsigned_T; const unsigned int rng_bits = mpt::engine_traits::result_bits(); unsigned_T result = 0; for (std::size_t entropy = 0; entropy < (sizeof(T) * 8); entropy += rng_bits) { if constexpr (rng_bits < (sizeof(T) * 8)) { constexpr unsigned int shift_bits = rng_bits % (sizeof(T) * 8); // silence utterly stupid MSVC and GCC warnings about shifting by too big amount (in which case this branch is not even taken however) result = (result << shift_bits) ^ static_cast(rng()); } else { result = static_cast(rng()); } } return static_cast(result); } template inline T random(Trng & rng) { static_assert(std::numeric_limits::is_integer); typedef typename std::make_unsigned::type unsigned_T; const unsigned int rng_bits = mpt::engine_traits::result_bits(); unsigned_T result = 0; for (std::size_t entropy = 0; entropy < std::min(required_entropy_bits, sizeof(T) * 8); entropy += rng_bits) { if constexpr (rng_bits < (sizeof(T) * 8)) { constexpr unsigned int shift_bits = rng_bits % (sizeof(T) * 8); // silence utterly stupid MSVC and GCC warnings about shifting by too big amount (in which case this branch is not even taken however) result = (result << shift_bits) ^ static_cast(rng()); } else { result = static_cast(rng()); } } if constexpr (required_entropy_bits >= (sizeof(T) * 8)) { return static_cast(result); } else { return static_cast(result & ((static_cast(1) << required_entropy_bits) - static_cast(1))); } } template inline T random(Trng & rng, std::size_t required_entropy_bits) { static_assert(std::numeric_limits::is_integer); typedef typename std::make_unsigned::type unsigned_T; const unsigned int rng_bits = mpt::engine_traits::result_bits(); unsigned_T result = 0; for (std::size_t entropy = 0; entropy < std::min(required_entropy_bits, sizeof(T) * 8); entropy += rng_bits) { if constexpr (rng_bits < (sizeof(T) * 8)) { constexpr unsigned int shift_bits = rng_bits % (sizeof(T) * 8); // silence utterly stupid MSVC and GCC warnings about shifting by too big amount (in which case this branch is not even taken however) result = (result << shift_bits) ^ static_cast(rng()); } else { result = static_cast(rng()); } } if (required_entropy_bits >= (sizeof(T) * 8)) { return static_cast(result); } else { return static_cast(result & ((static_cast(1) << required_entropy_bits) - static_cast(1))); } } template struct uniform_real_distribution { private: T a; T b; public: inline uniform_real_distribution(T a_, T b_) : a(a_) , b(b_) { return; } template inline T operator()(Trng & rng) const { const int mantissa_bits = std::numeric_limits::digits; return ((b - a) * static_cast(mpt::random(rng)) / static_cast((static_cast(1u) << mantissa_bits))) + a; } }; template inline T random(Trng & rng, T min, T max) { static_assert(!std::numeric_limits::is_integer); typedef mpt::uniform_real_distribution dis_type; dis_type dis(min, max); return static_cast(dis(rng)); } } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_RANDOM_RANDOM_HPP libopenmpt-0.6.1+release.autotools/src/mpt/random/seed.hpp0000644000175000017500000000141314051262177020560 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_RANDOM_SEED_HPP #define MPT_RANDOM_SEED_HPP #include "mpt/base/namespace.hpp" #include #include #include namespace mpt { inline namespace MPT_INLINE_NS { template class seed_seq_values { private: std::array seeds; public: template explicit seed_seq_values(Trd & rd) { std::uniform_int_distribution random_int{}; for (std::size_t i = 0; i < N; ++i) { seeds[i] = random_int(rd); } } const unsigned int * begin() const { return seeds.data(); } const unsigned int * end() const { return seeds.data() + N; } }; } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_RANDOM_SEED_HPP libopenmpt-0.6.1+release.autotools/src/mpt/string/0000755000175000017500000000000014175541574017246 500000000000000libopenmpt-0.6.1+release.autotools/src/mpt/string/tests/0000755000175000017500000000000014175541575020411 500000000000000libopenmpt-0.6.1+release.autotools/src/mpt/string/tests/tests_string_buffer.hpp0000644000175000017500000000377714044173026025124 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_STRING_TESTS_STRING_BUFFER_HPP #define MPT_STRING_TESTS_STRING_BUFFER_HPP #include "mpt/base/detect_compiler.hpp" #include "mpt/base/namespace.hpp" #include "mpt/string/buffer.hpp" #include "mpt/test/test.hpp" #include "mpt/test/test_macros.hpp" #include namespace mpt { inline namespace MPT_INLINE_NS { namespace tests { namespace string { namespace buffer { #if MPT_COMPILER_CLANG #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wglobal-constructors" #endif MPT_TEST_GROUP_INLINE("mpt/string/buffer") #if MPT_COMPILER_CLANG #pragma clang diagnostic pop #endif { { char buf[4] = {'x', 'x', 'x', 'x'}; mpt::WriteAutoBuf(buf) = std::string("foobar"); MPT_TEST_EXPECT_EQUAL(buf[0], 'f'); MPT_TEST_EXPECT_EQUAL(buf[1], 'o'); MPT_TEST_EXPECT_EQUAL(buf[2], 'o'); MPT_TEST_EXPECT_EQUAL(buf[3], '\0'); } { char buf[4] = {'x', 'x', 'x', 'x'}; char foobar[] = {'f', 'o', 'o', 'b', 'a', 'r', '\0'}; mpt::WriteTypedBuf(buf) = (char *)foobar; MPT_TEST_EXPECT_EQUAL(buf[0], 'f'); MPT_TEST_EXPECT_EQUAL(buf[1], 'o'); MPT_TEST_EXPECT_EQUAL(buf[2], 'o'); MPT_TEST_EXPECT_EQUAL(buf[3], '\0'); } { char buf[4] = {'x', 'x', 'x', 'x'}; mpt::WriteTypedBuf(buf) = (const char *)"foobar"; MPT_TEST_EXPECT_EQUAL(buf[0], 'f'); MPT_TEST_EXPECT_EQUAL(buf[1], 'o'); MPT_TEST_EXPECT_EQUAL(buf[2], 'o'); MPT_TEST_EXPECT_EQUAL(buf[3], '\0'); } { char buf[4] = {'x', 'x', 'x', 'x'}; mpt::WriteTypedBuf(buf) = "foobar"; MPT_TEST_EXPECT_EQUAL(buf[0], 'f'); MPT_TEST_EXPECT_EQUAL(buf[1], 'o'); MPT_TEST_EXPECT_EQUAL(buf[2], 'o'); MPT_TEST_EXPECT_EQUAL(buf[3], '\0'); } { const char buf[4] = {'f', 'o', 'o', 'b'}; std::string foo = mpt::ReadAutoBuf(buf); MPT_TEST_EXPECT_EQUAL(foo, std::string("foob")); } } } // namespace buffer } // namespace string } // namespace tests } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_STRING_TESTS_STRING_BUFFER_HPP libopenmpt-0.6.1+release.autotools/src/mpt/string/tests/tests_string_utility.hpp0000644000175000017500000000257414044173026025350 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_STRING_TESTS_STRING_UTILITY_HPP #define MPT_STRING_TESTS_STRING_UTILITY_HPP #include "mpt/base/detect.hpp" #include "mpt/base/namespace.hpp" #include "mpt/string/utility.hpp" #include "mpt/test/test.hpp" #include "mpt/test/test_macros.hpp" #include #include namespace mpt { inline namespace MPT_INLINE_NS { namespace tests { namespace string { namespace utility { #if MPT_COMPILER_CLANG #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wglobal-constructors" #endif MPT_TEST_GROUP_INLINE("mpt/string/utility") #if MPT_COMPILER_CLANG #pragma clang diagnostic pop #endif { MPT_TEST_EXPECT_EQUAL(mpt::trim_left(std::string(" ")), ""); MPT_TEST_EXPECT_EQUAL(mpt::trim_right(std::string(" ")), ""); MPT_TEST_EXPECT_EQUAL(mpt::trim(std::string(" ")), ""); // weird things with std::string containing \0 in the middle and trimming \0 MPT_TEST_EXPECT_EQUAL(std::string("\0\ta\0b ", 6).length(), (std::size_t)6); MPT_TEST_EXPECT_EQUAL(mpt::trim_right(std::string("\0\ta\0b ", 6)), std::string("\0\ta\0b", 5)); MPT_TEST_EXPECT_EQUAL(mpt::trim(std::string("\0\ta\0b\0", 6), std::string("\0", 1)), std::string("\ta\0b", 4)); } } // namespace utility } // namespace string } // namespace tests } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_STRING_TESTS_STRING_UTILITY_HPP libopenmpt-0.6.1+release.autotools/src/mpt/string/buffer.hpp0000644000175000017500000003202514044476756021156 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_STRING_BUFFER_HPP #define MPT_STRING_BUFFER_HPP #include "mpt/base/detect.hpp" #include "mpt/base/namespace.hpp" #include "mpt/detect/mfc.hpp" #include "mpt/string/types.hpp" #include #include #include #include #include #include #include #if MPT_DETECTED_MFC // cppcheck-suppress missingInclude #include #endif // MPT_DETECTED_MFC namespace mpt { inline namespace MPT_INLINE_NS { template class StringBufRefImpl { private: Tchar * buf; std::size_t size; public: // cppcheck false-positive // cppcheck-suppress uninitMemberVar explicit StringBufRefImpl(Tchar * buf_, std::size_t size_) : buf(buf_) , size(size_) { static_assert(sizeof(Tchar) == sizeof(typename Tstring::value_type)); assert(size > 0); } StringBufRefImpl(const StringBufRefImpl &) = delete; StringBufRefImpl(StringBufRefImpl &&) = default; StringBufRefImpl & operator=(const StringBufRefImpl &) = delete; StringBufRefImpl & operator=(StringBufRefImpl &&) = delete; operator Tstring() const { std::size_t len = std::find(buf, buf + size, Tchar('\0')) - buf; // terminate at \0 return Tstring(buf, buf + len); } explicit operator std::basic_string_view() const { std::size_t len = std::find(buf, buf + size, Tchar('\0')) - buf; // terminate at \0 return std::basic_string_view(buf, buf + len); } bool empty() const { return buf[0] == Tchar('\0'); } StringBufRefImpl & operator=(const Tstring & str) { std::copy(str.data(), str.data() + std::min(str.length(), size - 1), buf); std::fill(buf + std::min(str.length(), size - 1), buf + size, Tchar('\0')); return *this; } }; template class StringBufRefImpl { private: const Tchar * buf; std::size_t size; public: // cppcheck false-positive // cppcheck-suppress uninitMemberVar explicit StringBufRefImpl(const Tchar * buf_, std::size_t size_) : buf(buf_) , size(size_) { static_assert(sizeof(Tchar) == sizeof(typename Tstring::value_type)); assert(size > 0); } StringBufRefImpl(const StringBufRefImpl &) = delete; StringBufRefImpl(StringBufRefImpl &&) = default; StringBufRefImpl & operator=(const StringBufRefImpl &) = delete; StringBufRefImpl & operator=(StringBufRefImpl &&) = delete; operator Tstring() const { std::size_t len = std::find(buf, buf + size, Tchar('\0')) - buf; // terminate at \0 return Tstring(buf, buf + len); } explicit operator std::basic_string_view() const { std::size_t len = std::find(buf, buf + size, Tchar('\0')) - buf; // terminate at \0 return std::basic_string_view(buf, len); } bool empty() const { return buf[0] == Tchar('\0'); } }; template inline StringBufRefImpl::type> ReadTypedBuf(const std::array & buf) { return StringBufRefImpl::type>(buf.data(), size); } template inline StringBufRefImpl::type> ReadTypedBuf(const Tchar (&buf)[size]) { return StringBufRefImpl::type>(buf, size); } template inline StringBufRefImpl::type> ReadTypedBuf(const Tchar * buf, std::size_t size) { return StringBufRefImpl::type>(buf, size); } template inline StringBufRefImpl WriteTypedBuf(std::array & buf) { return StringBufRefImpl(buf.data(), size); } template inline StringBufRefImpl WriteTypedBuf(Tchar (&buf)[size]) { return StringBufRefImpl(buf, size); } template inline StringBufRefImpl WriteTypedBuf(Tchar * buf, std::size_t size) { return StringBufRefImpl(buf, size); } template inline StringBufRefImpl::type>, typename std::add_const::type> ReadAutoBuf(const std::array & buf) { return StringBufRefImpl::type>, typename std::add_const::type>(buf.data(), size); } template inline StringBufRefImpl::type>, typename std::add_const::type> ReadAutoBuf(const Tchar (&buf)[size]) { return StringBufRefImpl::type>, typename std::add_const::type>(buf, size); } template inline StringBufRefImpl::type>, typename std::add_const::type> ReadAutoBuf(const Tchar * buf, std::size_t size) { return StringBufRefImpl::type>, typename std::add_const::type>(buf, size); } template inline StringBufRefImpl::type>, Tchar> WriteAutoBuf(std::array & buf) { return StringBufRefImpl::type>, Tchar>(buf.data(), size); } template inline StringBufRefImpl::type>, Tchar> WriteAutoBuf(Tchar (&buf)[size]) { return StringBufRefImpl::type>, Tchar>(buf, size); } template inline StringBufRefImpl::type>, Tchar> WriteAutoBuf(Tchar * buf, std::size_t size) { return StringBufRefImpl::type>, Tchar>(buf, size); } #if MPT_OS_WINDOWS template inline StringBufRefImpl::type>::string_type, typename std::add_const::type> ReadWinBuf(const std::array & buf) { return StringBufRefImpl::type>::string_type, typename std::add_const::type>(buf.data(), size); } template inline StringBufRefImpl::type>::string_type, typename std::add_const::type> ReadWinBuf(const Tchar (&buf)[size]) { return StringBufRefImpl::type>::string_type, typename std::add_const::type>(buf, size); } template inline StringBufRefImpl::type>::string_type, typename std::add_const::type> ReadWinBuf(const Tchar * buf, std::size_t size) { return StringBufRefImpl::type>::string_type, typename std::add_const::type>(buf, size); } template inline StringBufRefImpl::type>::string_type, Tchar> WriteWinBuf(std::array & buf) { return StringBufRefImpl::type>::string_type, Tchar>(buf.data(), size); } template inline StringBufRefImpl::type>::string_type, Tchar> WriteWinBuf(Tchar (&buf)[size]) { return StringBufRefImpl::type>::string_type, Tchar>(buf, size); } template inline StringBufRefImpl::type>::string_type, Tchar> WriteWinBuf(Tchar * buf, std::size_t size) { return StringBufRefImpl::type>::string_type, Tchar>(buf, size); } #endif // MPT_OS_WINDOWS #if MPT_DETECTED_MFC template class CStringBufRefImpl { private: Tchar * buf; std::size_t size; public: // cppcheck false-positive // cppcheck-suppress uninitMemberVar explicit CStringBufRefImpl(Tchar * buf_, std::size_t size_) : buf(buf_) , size(size_) { assert(size > 0); } CStringBufRefImpl(const CStringBufRefImpl &) = delete; CStringBufRefImpl(CStringBufRefImpl &&) = default; CStringBufRefImpl & operator=(const CStringBufRefImpl &) = delete; CStringBufRefImpl & operator=(CStringBufRefImpl &&) = delete; operator CString() const { std::size_t len = std::find(buf, buf + size, Tchar('\0')) - buf; // terminate at \0 return CString(buf, mpt::saturate_cast(len)); } CStringBufRefImpl & operator=(const CString & str) { std::copy(str.GetString(), str.GetString() + std::min(static_cast(str.GetLength()), size - 1), buf); std::fill(buf + std::min(static_cast(str.GetLength()), size - 1), buf + size, Tchar('\0')); return *this; } }; template class CStringBufRefImpl { private: const Tchar * buf; std::size_t size; public: // cppcheck false-positive // cppcheck-suppress uninitMemberVar explicit CStringBufRefImpl(const Tchar * buf_, std::size_t size_) : buf(buf_) , size(size_) { assert(size > 0); } CStringBufRefImpl(const CStringBufRefImpl &) = delete; CStringBufRefImpl(CStringBufRefImpl &&) = default; CStringBufRefImpl & operator=(const CStringBufRefImpl &) = delete; CStringBufRefImpl & operator=(CStringBufRefImpl &&) = delete; operator CString() const { std::size_t len = std::find(buf, buf + size, Tchar('\0')) - buf; // terminate at \0 return CString(buf, mpt::saturate_cast(len)); } }; template inline CStringBufRefImpl::type> ReadCStringBuf(const std::array & buf) { return CStringBufRefImpl::type>(buf.data(), size); } template inline CStringBufRefImpl::type> ReadCStringBuf(const Tchar (&buf)[size]) { return CStringBufRefImpl::type>(buf, size); } template inline CStringBufRefImpl::type> ReadCStringBuf(const Tchar * buf, std::size_t size) { return CStringBufRefImpl::type>(buf, size); } template inline CStringBufRefImpl WriteCStringBuf(std::array & buf) { return CStringBufRefImpl(buf.data(), size); } template inline CStringBufRefImpl WriteCStringBuf(Tchar (&buf)[size]) { return CStringBufRefImpl(buf, size); } template inline CStringBufRefImpl WriteCStringBuf(Tchar * buf, std::size_t size) { return CStringBufRefImpl(buf, size); } #endif // MPT_DETECTED_MFC template struct charbuf { public: using Tchar = char; using char_type = Tchar; using string_type = std::basic_string; using string_view_type = std::basic_string_view; constexpr std::size_t static_length() const { return len; } public: Tchar buf[len]; public: charbuf() { std::fill(std::begin(buf), std::end(buf), Tchar('\0')); } charbuf(const charbuf &) = default; charbuf(charbuf &&) = default; charbuf & operator=(const charbuf &) = default; charbuf & operator=(charbuf &&) = default; const Tchar & operator[](std::size_t i) const { return buf[i]; } std::string str() const { return static_cast(*this); } operator string_type() const { return mpt::ReadAutoBuf(buf); } explicit operator string_view_type() const { return static_cast(mpt::ReadAutoBuf(buf)); } bool empty() const { return mpt::ReadAutoBuf(buf).empty(); } charbuf & operator=(const string_type & str) { mpt::WriteAutoBuf(buf) = str; return *this; } public: friend bool operator!=(const charbuf & a, const charbuf & b) { return static_cast(a) != static_cast(b); } friend bool operator!=(const std::string & a, const charbuf & b) { return a != static_cast(b); } friend bool operator!=(const charbuf & a, const std::string & b) { return static_cast(a) != b; } friend bool operator==(const charbuf & a, const charbuf & b) { return static_cast(a) == static_cast(b); } friend bool operator==(const std::string & a, const charbuf & b) { return a == static_cast(b); } friend bool operator==(const charbuf & a, const std::string & b) { return static_cast(a) == b; } }; } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_STRING_BUFFER_HPP libopenmpt-0.6.1+release.autotools/src/mpt/string/types.hpp0000644000175000017500000002522714072026467021046 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_STRING_TYPES_HPP #define MPT_STRING_TYPES_HPP #include "mpt/base/detect.hpp" #include "mpt/base/integer.hpp" #include "mpt/base/macros.hpp" #include "mpt/base/namespace.hpp" #include "mpt/detect/mfc.hpp" #include #include #include #if MPT_OS_WINDOWS #include #endif // MPT_OS_WINDOWS namespace mpt { inline namespace MPT_INLINE_NS { enum class common_encoding { utf8, ascii, // strictly 7-bit ASCII iso8859_1, iso8859_15, cp850, cp437, windows1252, }; enum class logical_encoding { locale, // CP_ACP on windows, system configured C locale otherwise active_locale, // active C/C++ global locale }; // source code / preprocessor (i.e. # token) inline constexpr auto source_encoding = common_encoding::ascii; // debug log files inline constexpr auto logfile_encoding = common_encoding::utf8; // std::clog / std::cout / std::cerr inline constexpr auto stdio_encoding = logical_encoding::locale; // getenv inline constexpr auto environment_encoding = logical_encoding::locale; // std::exception::what() inline constexpr auto exception_encoding = logical_encoding::active_locale; template struct is_character : public std::false_type { }; template <> struct is_character : public std::true_type { }; #if !defined(MPT_COMPILER_QUIRK_NO_WCHAR) template <> struct is_character : public std::true_type { }; #endif // !MPT_COMPILER_QUIRK_NO_WCHAR #if MPT_CXX_AT_LEAST(20) template <> struct is_character : public std::true_type { }; #endif // C++20 template <> struct is_character : public std::true_type { }; template <> struct is_character : public std::true_type { }; template ::value, bool>::type = true> MPT_CONSTEXPRINLINE typename std::make_unsigned::type char_value(T x) noexcept { return static_cast::type>(x); } template struct unsafe_char_converter { }; template <> struct unsafe_char_converter { static constexpr char32_t decode(char c) noexcept { return static_cast(static_cast(static_cast(c))); } static constexpr char encode(char32_t c) noexcept { return static_cast(static_cast(static_cast(c))); } }; #if !defined(MPT_COMPILER_QUIRK_NO_WCHAR) template <> struct unsafe_char_converter { static constexpr char32_t decode(wchar_t c) noexcept { return static_cast(static_cast(static_cast::type>(c))); } static constexpr wchar_t encode(char32_t c) noexcept { return static_cast(static_cast::type>(static_cast(c))); } }; #endif // !MPT_COMPILER_QUIRK_NO_WCHAR #if MPT_CXX_AT_LEAST(20) template <> struct unsafe_char_converter { static constexpr char32_t decode(char8_t c) noexcept { return static_cast(static_cast(static_cast(c))); } static constexpr char8_t encode(char32_t c) noexcept { return static_cast(static_cast(static_cast(c))); } }; #endif // C++20 template <> struct unsafe_char_converter { static constexpr char32_t decode(char16_t c) noexcept { return static_cast(static_cast(static_cast(c))); } static constexpr char16_t encode(char32_t c) noexcept { return static_cast(static_cast(static_cast(c))); } }; template <> struct unsafe_char_converter { static constexpr char32_t decode(char32_t c) noexcept { return c; } static constexpr char32_t encode(char32_t c) noexcept { return c; } }; template constexpr Tdstchar unsafe_char_convert(Tsrcchar src) noexcept { return mpt::unsafe_char_converter::encode(mpt::unsafe_char_converter::decode(src)); } #if !defined(MPT_COMPILER_QUIRK_NO_WCHAR) using widestring = std::wstring; using widechar = wchar_t; #define MPT_WIDECHAR(x) L##x #define MPT_WIDELITERAL(x) L##x #define MPT_WIDESTRING(x) std::wstring(L##x) #else // MPT_COMPILER_QUIRK_NO_WCHAR using widestring = std::u32string; using widechar = char32_t; #define MPT_WIDECHAR(x) U##x #define MPT_WIDELITERAL(x) U##x #define MPT_WIDESTRING(x) std::u32string(U##x) #endif // !MPT_COMPILER_QUIRK_NO_WCHAR template struct common_encoding_char_traits : std::char_traits { static constexpr auto encoding() noexcept { return common_encoding_tag; } }; template struct logical_encoding_char_traits : std::char_traits { static constexpr auto encoding() noexcept { return logical_encoding_tag; } }; using lstring = std::basic_string>; using source_string = std::basic_string>; using exception_string = std::basic_string>; #if MPT_OS_WINDOWS template struct windows_char_traits { }; template <> struct windows_char_traits { using string_type = mpt::lstring; }; template <> struct windows_char_traits { using string_type = std::wstring; }; using tstring = windows_char_traits::string_type; using winstring = mpt::tstring; #endif // MPT_OS_WINDOWS #if MPT_CXX_AT_LEAST(20) using u8string = std::u8string; using u8char = char8_t; #define MPT_U8CHAR(x) u8##x #define MPT_U8LITERAL(x) u8##x #define MPT_U8STRING(x) std::u8string(u8##x) #else // !C++20 using u8string = std::basic_string>; using u8char = char; #define MPT_U8CHAR(x) x #define MPT_U8LITERAL(x) x #define MPT_U8STRING(x) mpt::u8string(x) // mpt::u8string is a moderately type-safe string that is meant to contain // UTF-8 encoded char bytes. // // mpt::u8string is not implicitely convertible to/from std::string, but // it is convertible to/from C strings the same way as std::string is. // // The implementation of mpt::u8string is a compromise of compatibilty // with implementation-defined STL details, efficiency, source code size, // executable bloat, type-safety and simplicity. // // mpt::u8string is not meant to be used directly though. // mpt::u8string is meant as an alternative implementaion to std::wstring // for implementing the unicode string type mpt::ustring. #endif // C++20 #if !defined(MPT_USTRING_MODE_UTF8_FORCE) && (MPT_COMPILER_MSVC || (MPT_DETECTED_MFC && defined(UNICODE))) // Use wide strings for MSVC because this is the native encoding on // microsoft platforms. #define MPT_USTRING_MODE_WIDE 1 #define MPT_USTRING_MODE_UTF8 0 #else #define MPT_USTRING_MODE_WIDE 0 #define MPT_USTRING_MODE_UTF8 1 #endif // mpt::ustring // // mpt::ustring is a string type that can hold unicode strings. // It is implemented as a std::basic_string either based on wchar_t (i.e. the // same as std::wstring) or a custom-defined char_traits class that is derived // from std::char_traits. // The selection of the underlying implementation is done at compile-time. // MPT_UCHAR, MPT_ULITERAL and MPT_USTRING are macros that ease construction // of ustring char literals, ustring char array literals and ustring objects // from ustring char literals that work consistently in both modes. // Note that these are not supported for non-ASCII characters appearing in // the macro argument. // Also note that, as both UTF8 and UTF16 (it is less of an issue for UTF32) // are variable-length encodings and mpt::ustring is implemented as a // std::basic_string, all member functions that require individual character // access will not work consistently or even at all in a meaningful way. // This in particular affects operator[], find() and substr(). // The code makes no effort in preventing these or generating warnings when // these are used on mpt::ustring objects. However, compiling in the // respectively other mpt::ustring mode will catch most of these anyway. #if MPT_USTRING_MODE_WIDE #if MPT_USTRING_MODE_UTF8 #error "MPT_USTRING_MODE_WIDE and MPT_USTRING_MODE_UTF8 are mutually exclusive." #endif using ustring = std::wstring; using uchar = wchar_t; #define MPT_UCHAR(x) L##x #define MPT_ULITERAL(x) L##x #define MPT_USTRING(x) std::wstring(L##x) #endif // MPT_USTRING_MODE_WIDE #if MPT_USTRING_MODE_UTF8 #if MPT_USTRING_MODE_WIDE #error "MPT_USTRING_MODE_WIDE and MPT_USTRING_MODE_UTF8 are mutually exclusive." #endif using ustring = mpt::u8string; using uchar = mpt::u8char; #define MPT_UCHAR(x) MPT_U8CHAR(x) #define MPT_ULITERAL(x) MPT_U8LITERAL(x) #define MPT_USTRING(x) MPT_U8STRING(x) #endif // MPT_USTRING_MODE_UTF8 template struct make_string_type { }; template struct make_string_type> { using type = std::basic_string; }; template struct make_string_type { using type = std::basic_string; }; template struct make_string_type { using type = std::basic_string; }; template struct make_string_type { using type = typename make_string_type::type; }; #if MPT_DETECTED_MFC template <> struct make_string_type { using type = CStringW; }; template <> struct make_string_type { using type = CStringA; }; #endif // MPT_DETECTED_MFC template struct is_string_type : public std::false_type { }; template <> struct is_string_type : public std::true_type { }; #if !defined(MPT_COMPILER_QUIRK_NO_WCHAR) template <> struct is_string_type : public std::true_type { }; #endif // !MPT_COMPILER_QUIRK_NO_WCHAR #if MPT_CXX_AT_LEAST(20) template <> struct is_string_type : public std::true_type { }; #endif // C++20 template <> struct is_string_type : public std::true_type { }; template <> struct is_string_type : public std::true_type { }; #if MPT_DETECTED_MFC template <> struct is_string_type : public std::true_type { }; template <> struct is_string_type : public std::true_type { }; #endif // MPT_DETECTED_MFC template struct is_string_type> : public std::true_type { }; template inline typename mpt::make_string_type::type as_string(const T & str) { if constexpr (std::is_pointer::type>::value) { return str ? typename mpt::make_string_type::type{str} : typename mpt::make_string_type::type{}; } else { return str; } } } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_STRING_TYPES_HPP libopenmpt-0.6.1+release.autotools/src/mpt/string/utility.hpp0000644000175000017500000002377414172545374021416 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_STRING_UTILITY_HPP #define MPT_STRING_UTILITY_HPP #include "mpt/base/detect.hpp" #include "mpt/base/namespace.hpp" #include "mpt/detect/mfc.hpp" #include #include #include #if MPT_DETECTED_MFC // cppcheck-suppress missingInclude #include #endif // MPT_DETECTED_MFC namespace mpt { inline namespace MPT_INLINE_NS { // string_traits abstract the API of underlying string classes, in particular they allow adopting to CString without having to specialize for CString explicitly template struct string_traits { using string_type = Tstring; using size_type = typename string_type::size_type; using char_type = typename string_type::value_type; static inline std::size_t length(const string_type & str) { return str.length(); } static inline void reserve(string_type & str, std::size_t size) { str.reserve(size); } static inline string_type & append(string_type & str, const string_type & a) { return str.append(a); } static inline string_type & append(string_type & str, string_type && a) { return str.append(std::move(a)); } static inline string_type & append(string_type & str, std::size_t count, char_type c) { return str.append(count, c); } static inline string_type pad(string_type str, std::size_t left, std::size_t right) { str.insert(str.begin(), left, char_type(' ')); str.insert(str.end(), right, char_type(' ')); return str; } }; #if MPT_DETECTED_MFC template <> struct string_traits { using string_type = CStringA; using size_type = int; using char_type = typename CStringA::XCHAR; static inline size_type length(const string_type & str) { return str.GetLength(); } static inline void reserve(string_type & str, size_type size) { str.Preallocate(size); } static inline string_type & append(string_type & str, const string_type & a) { str += a; return str; } static inline string_type & append(string_type & str, size_type count, char_type c) { while (count--) { str.AppendChar(c); } return str; } static inline string_type pad(const string_type & str, size_type left, size_type right) { string_type tmp; while (left--) { tmp.AppendChar(char_type(' ')); } tmp += str; while (right--) { tmp.AppendChar(char_type(' ')); } return tmp; } }; template <> struct string_traits { using string_type = CStringW; using size_type = int; using char_type = typename CStringW::XCHAR; static inline size_type length(const string_type & str) { return str.GetLength(); } static inline void reserve(string_type & str, size_type size) { str.Preallocate(size); } static inline string_type & append(string_type & str, const string_type & a) { str += a; return str; } static inline string_type & append(string_type & str, size_type count, char_type c) { while (count--) { str.AppendChar(c); } return str; } static inline string_type pad(const string_type & str, size_type left, size_type right) { string_type tmp; while (left--) { tmp.AppendChar(char_type(' ')); } tmp += str; while (right--) { tmp.AppendChar(char_type(' ')); } return tmp; } }; #endif // MPT_DETECTED_MFC template struct char_constants { static inline constexpr Tchar space = ' '; static inline constexpr Tchar a = 'a'; static inline constexpr Tchar z = 'z'; static inline constexpr Tchar A = 'A'; static inline constexpr Tchar Z = 'Z'; static inline constexpr Tchar lf = '\n'; static inline constexpr Tchar cr = '\r'; static inline constexpr Tchar tab = '\t'; static inline constexpr Tchar comma = ','; }; template <> struct char_constants { static inline constexpr char space = ' '; static inline constexpr char a = 'a'; static inline constexpr char z = 'z'; static inline constexpr char A = 'A'; static inline constexpr char Z = 'Z'; static inline constexpr char lf = '\n'; static inline constexpr char cr = '\r'; static inline constexpr char tab = '\t'; static inline constexpr char comma = ','; }; #if !defined(MPT_COMPILER_QUIRK_NO_WCHAR) template <> struct char_constants { static inline constexpr wchar_t space = L' '; static inline constexpr wchar_t a = L'a'; static inline constexpr wchar_t z = L'z'; static inline constexpr wchar_t A = L'A'; static inline constexpr wchar_t Z = L'Z'; static inline constexpr wchar_t lf = L'\n'; static inline constexpr wchar_t cr = L'\r'; static inline constexpr wchar_t tab = L'\t'; static inline constexpr wchar_t comma = L','; }; #endif // !MPT_COMPILER_QUIRK_NO_WCHAR #if MPT_CXX_AT_LEAST(20) template <> struct char_constants { static inline constexpr char8_t space = u8' '; static inline constexpr char8_t a = u8'a'; static inline constexpr char8_t z = u8'z'; static inline constexpr char8_t A = u8'A'; static inline constexpr char8_t Z = u8'Z'; static inline constexpr char8_t lf = u8'\n'; static inline constexpr char8_t cr = u8'\r'; static inline constexpr char8_t tab = u8'\t'; static inline constexpr char8_t comma = u8','; }; #endif template <> struct char_constants { static inline constexpr char16_t space = u' '; static inline constexpr char16_t a = u'a'; static inline constexpr char16_t z = u'z'; static inline constexpr char16_t A = u'A'; static inline constexpr char16_t Z = u'Z'; static inline constexpr char16_t lf = u'\n'; static inline constexpr char16_t cr = u'\r'; static inline constexpr char16_t tab = u'\t'; static inline constexpr char16_t comma = u','; }; template <> struct char_constants { static inline constexpr char32_t space = U' '; static inline constexpr char32_t a = U'a'; static inline constexpr char32_t z = U'z'; static inline constexpr char32_t A = U'A'; static inline constexpr char32_t Z = U'Z'; static inline constexpr char32_t lf = U'\n'; static inline constexpr char32_t cr = U'\r'; static inline constexpr char32_t tab = U'\t'; static inline constexpr char32_t comma = U','; }; template constexpr bool is_any_line_ending(Tchar c) noexcept { return (c == char_constants::cr) || (c == char_constants::lf); } template inline Tstring default_whitespace() { Tstring result; result.reserve(4); result.push_back(char_constants::space); result.push_back(char_constants::lf); result.push_back(char_constants::cr); result.push_back(char_constants::tab); return result; } // Remove whitespace at start of string template inline Tstring trim_left(Tstring str, const Tstring & whitespace = default_whitespace()) { typename Tstring::size_type pos = str.find_first_not_of(whitespace); if (pos != Tstring::npos) { str.erase(str.begin(), str.begin() + pos); } else if (pos == Tstring::npos && str.length() > 0 && str.find_last_of(whitespace) == str.length() - 1) { return Tstring(); } return str; } // Remove whitespace at end of string template inline Tstring trim_right(Tstring str, const Tstring & whitespace = default_whitespace()) { typename Tstring::size_type pos = str.find_last_not_of(whitespace); if (pos != Tstring::npos) { str.erase(str.begin() + pos + 1, str.end()); } else if (pos == Tstring::npos && str.length() > 0 && str.find_first_of(whitespace) == 0) { return Tstring(); } return str; } // Remove whitespace at start and end of string template inline Tstring trim(Tstring str, const Tstring & whitespace = default_whitespace()) { return trim_right(trim_left(str, whitespace), whitespace); } template inline bool starts_with(const Tstring & str, const Tmatch & match) { return (str.find(typename mpt::make_string_type::type{match}) == 0); } template inline bool ends_with(const Tstring & str, const Tmatch & match) { return (str.rfind(typename mpt::make_string_type::type{match}) == (str.length() - typename mpt::make_string_type::type{match}.length())); } template inline Tstring replace(Tstring str, const Treplace & old_str, const Treplace & new_str) { std::size_t pos = 0; while ((pos = str.find(typename mpt::make_string_type::type{old_str}, pos)) != Tstring::npos) { str.replace(pos, typename mpt::make_string_type::type{old_str}.length(), typename mpt::make_string_type::type{new_str}); pos += typename mpt::make_string_type::type{new_str}.length(); } return str; } template inline Tstring truncate(Tstring str, std::size_t max_len) { if (str.length() > max_len) { str.resize(max_len); } return str; } template inline constexpr Tchar to_lower_ascii(Tchar c) noexcept { if (char_constants::A <= c && c <= char_constants::Z) { c += char_constants::a - char_constants::A; } return c; } template inline constexpr Tchar to_upper_ascii(Tchar c) noexcept { if (char_constants::a <= c && c <= char_constants::z) { c -= char_constants::a - char_constants::A; } return c; } template inline std::vector split(const Tstring & str, const Tstring & sep = Tstring(1, char_constants::comma)) { std::vector vals; std::size_t pos = 0; while (str.find(sep, pos) != std::string::npos) { vals.push_back(str.substr(pos, str.find(sep, pos) - pos)); pos = str.find(sep, pos) + sep.length(); } if (!vals.empty() || (str.substr(pos).length() > 0)) { vals.push_back(str.substr(pos)); } return vals; } template inline Tstring join(const std::vector & vals, const Tstring & sep = Tstring(1, char_constants::comma)) { Tstring str; for (std::size_t i = 0; i < vals.size(); ++i) { if (i > 0) { str += sep; } str += vals[i]; } return str; } } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_STRING_UTILITY_HPP libopenmpt-0.6.1+release.autotools/src/mpt/string_transcode/0000755000175000017500000000000014175541574021310 500000000000000libopenmpt-0.6.1+release.autotools/src/mpt/string_transcode/tests/0000755000175000017500000000000014175541575022453 500000000000000libopenmpt-0.6.1+release.autotools/src/mpt/string_transcode/tests/tests_string_transcode.hpp0000644000175000017500000004375514107230757027704 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_STRING_TRANSCODE_TESTS_STRING_TRANSCODE_HPP #define MPT_STRING_TRANSCODE_TESTS_STRING_TRANSCODE_HPP #include "mpt/base/detect.hpp" #include "mpt/base/namespace.hpp" #include "mpt/string_transcode/macros.hpp" #include "mpt/string_transcode/transcode.hpp" #include "mpt/test/test.hpp" #include "mpt/test/test_macros.hpp" namespace mpt { inline namespace MPT_INLINE_NS { namespace tests { namespace string_convert { #if MPT_COMPILER_CLANG #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wglobal-constructors" #endif MPT_TEST_GROUP_INLINE("mpt/string_transcode") #if MPT_COMPILER_CLANG #pragma clang diagnostic pop #endif { // MPT_UTF8_STRING version // Charset conversions (basic sanity checks) MPT_TEST_EXPECT_EQUAL(mpt::transcode(mpt::common_encoding::utf8, MPT_USTRING("a")), "a"); MPT_TEST_EXPECT_EQUAL(mpt::transcode(mpt::common_encoding::iso8859_1, MPT_USTRING("a")), "a"); MPT_TEST_EXPECT_EQUAL(mpt::transcode(mpt::common_encoding::ascii, MPT_USTRING("a")), "a"); MPT_TEST_EXPECT_EQUAL(mpt::transcode(mpt::common_encoding::utf8, "a"), MPT_USTRING("a")); MPT_TEST_EXPECT_EQUAL(mpt::transcode(mpt::common_encoding::iso8859_1, "a"), MPT_USTRING("a")); MPT_TEST_EXPECT_EQUAL(mpt::transcode(mpt::common_encoding::ascii, "a"), MPT_USTRING("a")); MPT_TEST_EXPECT_EQUAL(mpt::transcode(mpt::logical_encoding::locale, MPT_USTRING("a")), "a"); MPT_TEST_EXPECT_EQUAL(mpt::transcode(mpt::logical_encoding::locale, "a"), MPT_USTRING("a")); MPT_TEST_EXPECT_EQUAL(mpt::transcode(mpt::common_encoding::utf8, MPT_UTF8_STRING("a")), "a"); MPT_TEST_EXPECT_EQUAL(mpt::transcode(mpt::common_encoding::iso8859_1, MPT_UTF8_STRING("a")), "a"); MPT_TEST_EXPECT_EQUAL(mpt::transcode(mpt::common_encoding::ascii, MPT_UTF8_STRING("a")), "a"); MPT_TEST_EXPECT_EQUAL(mpt::transcode(mpt::common_encoding::utf8, "a"), MPT_UTF8_STRING("a")); MPT_TEST_EXPECT_EQUAL(mpt::transcode(mpt::common_encoding::iso8859_1, "a"), MPT_UTF8_STRING("a")); MPT_TEST_EXPECT_EQUAL(mpt::transcode(mpt::common_encoding::ascii, "a"), MPT_UTF8_STRING("a")); MPT_TEST_EXPECT_EQUAL(mpt::transcode(mpt::logical_encoding::locale, MPT_UTF8_STRING("a")), "a"); MPT_TEST_EXPECT_EQUAL(mpt::transcode(mpt::logical_encoding::locale, "a"), MPT_UTF8_STRING("a")); #if MPT_OS_EMSCRIPTEN MPT_TEST_EXPECT_EQUAL(mpt::transcode(mpt::logical_encoding::locale, MPT_UTF8_STRING("\xe2\x8c\x82")), "\xe2\x8c\x82"); #endif // MPT_OS_EMSCRIPTEN // Check that some character replacement is done (and not just empty strings or truncated strings are returned) // We test german umlaut-a (U+00E4) (\xC3\xA4) and CJK U+5BB6 (\xE5\xAE\xB6) MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode(mpt::common_encoding::ascii, MPT_UTF8_STRING("abc\xC3\xA4xyz")), "xyz")); MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode(mpt::common_encoding::iso8859_1, MPT_UTF8_STRING("abc\xC3\xA4xyz")), "xyz")); MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode(mpt::common_encoding::cp437, MPT_UTF8_STRING("abc\xC3\xA4xyz")), "xyz")); MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode(mpt::common_encoding::utf8, MPT_UTF8_STRING("abc\xC3\xA4xyz")), "xyz")); MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode(mpt::common_encoding::ascii, MPT_UTF8_STRING("abc\xC3\xA4xyz")), "abc")); MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode(mpt::common_encoding::iso8859_1, MPT_UTF8_STRING("abc\xC3\xA4xyz")), "abc")); MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode(mpt::common_encoding::cp437, MPT_UTF8_STRING("abc\xC3\xA4xyz")), "abc")); MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode(mpt::common_encoding::utf8, MPT_UTF8_STRING("abc\xC3\xA4xyz")), "abc")); MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode(mpt::logical_encoding::locale, MPT_UTF8_STRING("abc\xC3\xA4xyz")), "xyz")); MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode(mpt::logical_encoding::locale, MPT_UTF8_STRING("abc\xC3\xA4xyz")), "abc")); MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode(mpt::common_encoding::ascii, MPT_UTF8_STRING("abc\xE5\xAE\xB6xyz")), "xyz")); MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode(mpt::common_encoding::iso8859_1, MPT_UTF8_STRING("abc\xE5\xAE\xB6xyz")), "xyz")); MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode(mpt::common_encoding::cp437, MPT_UTF8_STRING("abc\xE5\xAE\xB6xyz")), "xyz")); MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode(mpt::common_encoding::utf8, MPT_UTF8_STRING("abc\xE5\xAE\xB6xyz")), "xyz")); MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode(mpt::common_encoding::ascii, MPT_UTF8_STRING("abc\xE5\xAE\xB6xyz")), "abc")); MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode(mpt::common_encoding::iso8859_1, MPT_UTF8_STRING("abc\xE5\xAE\xB6xyz")), "abc")); MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode(mpt::common_encoding::cp437, MPT_UTF8_STRING("abc\xE5\xAE\xB6xyz")), "abc")); MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode(mpt::common_encoding::utf8, MPT_UTF8_STRING("abc\xE5\xAE\xB6xyz")), "abc")); MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode(mpt::logical_encoding::locale, MPT_UTF8_STRING("abc\xE5\xAE\xB6xyz")), "xyz")); MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode(mpt::logical_encoding::locale, MPT_UTF8_STRING("abc\xE5\xAE\xB6xyz")), "abc")); MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode(mpt::common_encoding::ascii, "abc\xC3\xA4xyz"), MPT_USTRING("xyz"))); MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode(mpt::common_encoding::iso8859_1, "abc\xC3\xA4xyz"), MPT_USTRING("xyz"))); MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode(mpt::common_encoding::cp437, "abc\xC3\xA4xyz"), MPT_USTRING("xyz"))); MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode(mpt::common_encoding::utf8, "abc\xC3\xA4xyz"), MPT_USTRING("xyz"))); MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode(mpt::common_encoding::ascii, "abc\xC3\xA4xyz"), MPT_USTRING("abc"))); MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode(mpt::common_encoding::iso8859_1, "abc\xC3\xA4xyz"), MPT_USTRING("abc"))); MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode(mpt::common_encoding::cp437, "abc\xC3\xA4xyz"), MPT_USTRING("abc"))); MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode(mpt::common_encoding::utf8, "abc\xC3\xA4xyz"), MPT_USTRING("abc"))); MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode(mpt::logical_encoding::locale, "abc\xC3\xA4xyz"), MPT_USTRING("xyz"))); MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode(mpt::logical_encoding::locale, "abc\xC3\xA4xyz"), MPT_USTRING("abc"))); MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode(mpt::common_encoding::ascii, "abc\xE5\xAE\xB6xyz"), MPT_USTRING("xyz"))); MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode(mpt::common_encoding::iso8859_1, "abc\xE5\xAE\xB6xyz"), MPT_USTRING("xyz"))); MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode(mpt::common_encoding::cp437, "abc\xE5\xAE\xB6xyz"), MPT_USTRING("xyz"))); MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode(mpt::common_encoding::utf8, "abc\xE5\xAE\xB6xyz"), MPT_USTRING("xyz"))); MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode(mpt::common_encoding::ascii, "abc\xE5\xAE\xB6xyz"), MPT_USTRING("abc"))); MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode(mpt::common_encoding::iso8859_1, "abc\xE5\xAE\xB6xyz"), MPT_USTRING("abc"))); MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode(mpt::common_encoding::cp437, "abc\xE5\xAE\xB6xyz"), MPT_USTRING("abc"))); MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode(mpt::common_encoding::utf8, "abc\xE5\xAE\xB6xyz"), MPT_USTRING("abc"))); MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode(mpt::logical_encoding::locale, "abc\xE5\xAE\xB6xyz"), MPT_USTRING("xyz"))); MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode(mpt::logical_encoding::locale, "abc\xE5\xAE\xB6xyz"), MPT_USTRING("abc"))); // Check that characters are correctly converted // We test german umlaut-a (U+00E4) and CJK U+5BB6 // cp437 MPT_TEST_EXPECT_EQUAL(mpt::transcode(mpt::common_encoding::cp437, MPT_UTF8_STRING("abc\xC3\xA4xyz")), "abc\x84xyz"); MPT_TEST_EXPECT_EQUAL(MPT_UTF8_STRING("abc\xC3\xA4xyz"), mpt::transcode(mpt::common_encoding::cp437, "abc\x84xyz")); // iso8859 MPT_TEST_EXPECT_EQUAL(mpt::transcode(mpt::common_encoding::iso8859_1, MPT_UTF8_STRING("abc\xC3\xA4xyz")), "abc\xE4xyz"); MPT_TEST_EXPECT_EQUAL(MPT_UTF8_STRING("abc\xC3\xA4xyz"), mpt::transcode(mpt::common_encoding::iso8859_1, "abc\xE4xyz")); // utf8 MPT_TEST_EXPECT_EQUAL(mpt::transcode(mpt::common_encoding::utf8, MPT_UTF8_STRING("abc\xC3\xA4xyz")), "abc\xC3\xA4xyz"); MPT_TEST_EXPECT_EQUAL(MPT_UTF8_STRING("abc\xC3\xA4xyz"), mpt::transcode(mpt::common_encoding::utf8, "abc\xC3\xA4xyz")); MPT_TEST_EXPECT_EQUAL(mpt::transcode(mpt::common_encoding::utf8, MPT_UTF8_STRING("abc\xE5\xAE\xB6xyz")), "abc\xE5\xAE\xB6xyz"); MPT_TEST_EXPECT_EQUAL(MPT_UTF8_STRING("abc\xE5\xAE\xB6xyz"), mpt::transcode(mpt::common_encoding::utf8, "abc\xE5\xAE\xB6xyz")); // utf16 MPT_TEST_EXPECT_EQUAL(mpt::transcode(mpt::common_encoding::utf8, std::u16string(1, char16_t{0xe4})), "\xC3\xA4"); MPT_TEST_EXPECT_EQUAL(std::u16string(1, char16_t{0xe4}), mpt::transcode(mpt::common_encoding::utf8, "\xC3\xA4")); MPT_TEST_EXPECT_EQUAL(mpt::transcode(mpt::common_encoding::utf8, std::u16string(1, char16_t{0x5BB6})), "\xE5\xAE\xB6"); MPT_TEST_EXPECT_EQUAL(std::u16string(1, char16_t{0x5BB6}), mpt::transcode(mpt::common_encoding::utf8, "\xE5\xAE\xB6")); // utf32 MPT_TEST_EXPECT_EQUAL(mpt::transcode(mpt::common_encoding::utf8, std::u32string(1, char32_t{0xe4})), "\xC3\xA4"); MPT_TEST_EXPECT_EQUAL(std::u32string(1, char32_t{0xe4}), mpt::transcode(mpt::common_encoding::utf8, "\xC3\xA4")); MPT_TEST_EXPECT_EQUAL(mpt::transcode(mpt::common_encoding::utf8, std::u32string(1, char32_t{0x5BB6})), "\xE5\xAE\xB6"); MPT_TEST_EXPECT_EQUAL(std::u32string(1, char32_t{0x5BB6}), mpt::transcode(mpt::common_encoding::utf8, "\xE5\xAE\xB6")); #if !defined(MPT_COMPILER_QUIRK_NO_WCHAR) // wide L"" version // Charset conversions (basic sanity checks) MPT_TEST_EXPECT_EQUAL(mpt::transcode(mpt::common_encoding::utf8, L"a"), "a"); MPT_TEST_EXPECT_EQUAL(mpt::transcode(mpt::common_encoding::iso8859_1, L"a"), "a"); MPT_TEST_EXPECT_EQUAL(mpt::transcode(mpt::common_encoding::ascii, L"a"), "a"); MPT_TEST_EXPECT_EQUAL(mpt::transcode(mpt::common_encoding::utf8, "a"), L"a"); MPT_TEST_EXPECT_EQUAL(mpt::transcode(mpt::common_encoding::iso8859_1, "a"), L"a"); MPT_TEST_EXPECT_EQUAL(mpt::transcode(mpt::common_encoding::ascii, "a"), L"a"); MPT_TEST_EXPECT_EQUAL(mpt::transcode(mpt::logical_encoding::locale, L"a"), "a"); MPT_TEST_EXPECT_EQUAL(mpt::transcode(mpt::logical_encoding::locale, "a"), L"a"); // Check that some character replacement is done (and not just empty strings or truncated strings are returned) // We test german umlaut-a (U+00E4) and CJK U+5BB6 #if MPT_COMPILER_MSVC #pragma warning(push) #pragma warning(disable : 4428) // universal-character-name encountered in source #endif MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode(mpt::common_encoding::ascii, L"abc\u00E4xyz"), "xyz")); MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode(mpt::common_encoding::iso8859_1, L"abc\u00E4xyz"), "xyz")); MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode(mpt::common_encoding::cp437, L"abc\u00E4xyz"), "xyz")); MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode(mpt::common_encoding::utf8, L"abc\u00E4xyz"), "xyz")); MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode(mpt::common_encoding::ascii, L"abc\u00E4xyz"), "abc")); MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode(mpt::common_encoding::iso8859_1, L"abc\u00E4xyz"), "abc")); MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode(mpt::common_encoding::cp437, L"abc\u00E4xyz"), "abc")); MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode(mpt::common_encoding::utf8, L"abc\u00E4xyz"), "abc")); MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode(mpt::logical_encoding::locale, L"abc\u00E4xyz"), "xyz")); MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode(mpt::logical_encoding::locale, L"abc\u00E4xyz"), "abc")); MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode(mpt::common_encoding::ascii, L"abc\u5BB6xyz"), "xyz")); MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode(mpt::common_encoding::iso8859_1, L"abc\u5BB6xyz"), "xyz")); MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode(mpt::common_encoding::cp437, L"abc\u5BB6xyz"), "xyz")); MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode(mpt::common_encoding::utf8, L"abc\u5BB6xyz"), "xyz")); MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode(mpt::common_encoding::ascii, L"abc\u5BB6xyz"), "abc")); MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode(mpt::common_encoding::iso8859_1, L"abc\u5BB6xyz"), "abc")); MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode(mpt::common_encoding::cp437, L"abc\u5BB6xyz"), "abc")); MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode(mpt::common_encoding::utf8, L"abc\u5BB6xyz"), "abc")); MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode(mpt::logical_encoding::locale, L"abc\u5BB6xyz"), "xyz")); MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode(mpt::logical_encoding::locale, L"abc\u5BB6xyz"), "abc")); MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode(mpt::common_encoding::ascii, "abc\xC3\xA4xyz"), L"xyz")); MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode(mpt::common_encoding::iso8859_1, "abc\xC3\xA4xyz"), L"xyz")); MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode(mpt::common_encoding::cp437, "abc\xC3\xA4xyz"), L"xyz")); MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode(mpt::common_encoding::utf8, "abc\xC3\xA4xyz"), L"xyz")); MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode(mpt::common_encoding::ascii, "abc\xC3\xA4xyz"), L"abc")); MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode(mpt::common_encoding::iso8859_1, "abc\xC3\xA4xyz"), L"abc")); MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode(mpt::common_encoding::cp437, "abc\xC3\xA4xyz"), L"abc")); MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode(mpt::common_encoding::utf8, "abc\xC3\xA4xyz"), L"abc")); MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode(mpt::logical_encoding::locale, "abc\xC3\xA4xyz"), L"xyz")); MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode(mpt::logical_encoding::locale, "abc\xC3\xA4xyz"), L"abc")); MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode(mpt::common_encoding::ascii, "abc\xE5\xAE\xB6xyz"), L"xyz")); MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode(mpt::common_encoding::iso8859_1, "abc\xE5\xAE\xB6xyz"), L"xyz")); MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode(mpt::common_encoding::cp437, "abc\xE5\xAE\xB6xyz"), L"xyz")); MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode(mpt::common_encoding::utf8, "abc\xE5\xAE\xB6xyz"), L"xyz")); MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode(mpt::common_encoding::ascii, "abc\xE5\xAE\xB6xyz"), L"abc")); MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode(mpt::common_encoding::iso8859_1, "abc\xE5\xAE\xB6xyz"), L"abc")); MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode(mpt::common_encoding::cp437, "abc\xE5\xAE\xB6xyz"), L"abc")); MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode(mpt::common_encoding::utf8, "abc\xE5\xAE\xB6xyz"), L"abc")); MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode(mpt::logical_encoding::locale, "abc\xE5\xAE\xB6xyz"), L"xyz")); MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode(mpt::logical_encoding::locale, "abc\xE5\xAE\xB6xyz"), L"abc")); // Check that characters are correctly converted // We test german umlaut-a (U+00E4) and CJK U+5BB6 // cp437 MPT_TEST_EXPECT_EQUAL(mpt::transcode(mpt::common_encoding::cp437, L"abc\u00E4xyz"), "abc\x84xyz"); MPT_TEST_EXPECT_EQUAL(L"abc\u00E4xyz", mpt::transcode(mpt::common_encoding::cp437, "abc\x84xyz")); // iso8859 MPT_TEST_EXPECT_EQUAL(mpt::transcode(mpt::common_encoding::iso8859_1, L"abc\u00E4xyz"), "abc\xE4xyz"); MPT_TEST_EXPECT_EQUAL(L"abc\u00E4xyz", mpt::transcode(mpt::common_encoding::iso8859_1, "abc\xE4xyz")); // utf8 MPT_TEST_EXPECT_EQUAL(mpt::transcode(mpt::common_encoding::utf8, L"abc\u00E4xyz"), "abc\xC3\xA4xyz"); MPT_TEST_EXPECT_EQUAL(L"abc\u00E4xyz", mpt::transcode(mpt::common_encoding::utf8, "abc\xC3\xA4xyz")); MPT_TEST_EXPECT_EQUAL(mpt::transcode(mpt::common_encoding::utf8, L"abc\u5BB6xyz"), "abc\xE5\xAE\xB6xyz"); MPT_TEST_EXPECT_EQUAL(L"abc\u5BB6xyz", mpt::transcode(mpt::common_encoding::utf8, "abc\xE5\xAE\xB6xyz")); #if MPT_COMPILER_MSVC #pragma warning(pop) #endif #endif // !MPT_COMPILER_QUIRK_NO_WCHAR } } // namespace string_convert } // namespace tests } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_STRING_TRANSCODE_TESTS_STRING_TRANSCODE_HPP libopenmpt-0.6.1+release.autotools/src/mpt/string_transcode/transcode.hpp0000644000175000017500000012433514142660753023726 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_STRING_TRANSCODE_TRANSCODE_HPP #define MPT_STRING_TRANSCODE_TRANSCODE_HPP #include "mpt/base/detect.hpp" #include "mpt/base/integer.hpp" #include "mpt/base/macros.hpp" #include "mpt/base/namespace.hpp" #include "mpt/base/saturate_cast.hpp" #include "mpt/detect/mfc.hpp" #include "mpt/string/types.hpp" #include #if !defined(MPT_COMPILER_QUIRK_NO_WCHAR) #include #endif // !MPT_COMPILER_QUIRK_NO_WCHAR #include #include #if !defined(MPT_COMPILER_QUIRK_NO_WCHAR) #include #endif // !MPT_COMPILER_QUIRK_NO_WCHAR #include #if MPT_OS_DJGPP #include #endif // MPT_OS_DJGPP #if MPT_OS_WINDOWS #include #endif // MPT_OS_WINDOWS #if MPT_OS_DJGPP #include #endif // MPT_OS_DJGPP namespace mpt { inline namespace MPT_INLINE_NS { /* default 1:1 mapping inline constexpr char32_t CharsetTableISO8859_1[256] = { 0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x000e,0x000f, 0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f, 0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f, 0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f, 0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f, 0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f, 0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f, 0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x007f, 0x0080,0x0081,0x0082,0x0083,0x0084,0x0085,0x0086,0x0087,0x0088,0x0089,0x008a,0x008b,0x008c,0x008d,0x008e,0x008f, 0x0090,0x0091,0x0092,0x0093,0x0094,0x0095,0x0096,0x0097,0x0098,0x0099,0x009a,0x009b,0x009c,0x009d,0x009e,0x009f, 0x00a0,0x00a1,0x00a2,0x00a3,0x00a4,0x00a5,0x00a6,0x00a7,0x00a8,0x00a9,0x00aa,0x00ab,0x00ac,0x00ad,0x00ae,0x00af, 0x00b0,0x00b1,0x00b2,0x00b3,0x00b4,0x00b5,0x00b6,0x00b7,0x00b8,0x00b9,0x00ba,0x00bb,0x00bc,0x00bd,0x00be,0x00bf, 0x00c0,0x00c1,0x00c2,0x00c3,0x00c4,0x00c5,0x00c6,0x00c7,0x00c8,0x00c9,0x00ca,0x00cb,0x00cc,0x00cd,0x00ce,0x00cf, 0x00d0,0x00d1,0x00d2,0x00d3,0x00d4,0x00d5,0x00d6,0x00d7,0x00d8,0x00d9,0x00da,0x00db,0x00dc,0x00dd,0x00de,0x00df, 0x00e0,0x00e1,0x00e2,0x00e3,0x00e4,0x00e5,0x00e6,0x00e7,0x00e8,0x00e9,0x00ea,0x00eb,0x00ec,0x00ed,0x00ee,0x00ef, 0x00f0,0x00f1,0x00f2,0x00f3,0x00f4,0x00f5,0x00f6,0x00f7,0x00f8,0x00f9,0x00fa,0x00fb,0x00fc,0x00fd,0x00fe,0x00ff }; */ inline constexpr char32_t CharsetTableISO8859_15[256] = { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f, 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x20ac, 0x00a5, 0x0160, 0x00a7, 0x0161, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x017d, 0x00b5, 0x00b6, 0x00b7, 0x017e, 0x00b9, 0x00ba, 0x00bb, 0x0152, 0x0153, 0x0178, 0x00bf, 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df, 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff}; inline constexpr char32_t CharsetTableWindows1252[256] = { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x20ac, 0x0081, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021, 0x02c6, 0x2030, 0x0160, 0x2039, 0x0152, 0x008d, 0x017d, 0x008f, 0x0090, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014, 0x02dc, 0x2122, 0x0161, 0x203a, 0x0153, 0x009d, 0x017e, 0x0178, 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df, 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff}; inline constexpr char32_t CharsetTableCP850[256] = { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x2302, 0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7, 0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5, 0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9, 0x00ff, 0x00d6, 0x00dc, 0x00F8, 0x00a3, 0x00D8, 0x00D7, 0x0192, 0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00ba, 0x00bf, 0x00AE, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0, 0x00A9, 0x2563, 0x2551, 0x2557, 0x255d, 0x00A2, 0x00A5, 0x2510, 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x00E3, 0x00C3, 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x00A4, 0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x0131, 0x00CD, 0x00CE, 0x00CF, 0x2518, 0x250c, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580, 0x00D3, 0x00df, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00b5, 0x00FE, 0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4, 0x00AD, 0x00b1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00f7, 0x00B8, 0x00b0, 0x00A8, 0x00b7, 0x00B9, 0x00B3, 0x00b2, 0x25a0, 0x00a0}; inline constexpr char32_t CharsetTableCP437[256] = { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x2302, 0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7, 0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5, 0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9, 0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192, 0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00ba, 0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510, 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f, 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567, 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256b, 0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580, 0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4, 0x03a6, 0x0398, 0x03a9, 0x03b4, 0x221e, 0x03c6, 0x03b5, 0x2229, 0x2261, 0x00b1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00f7, 0x2248, 0x00b0, 0x2219, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x25a0, 0x00a0}; template inline mpt::widestring decode_8bit(const Tsrcstring & str, const char32_t (&table)[256], mpt::widechar replacement = MPT_WIDECHAR('\uFFFD')) { mpt::widestring res; res.reserve(str.length()); for (std::size_t i = 0; i < str.length(); ++i) { std::size_t c = static_cast(mpt::char_value(str[i])); if (c < std::size(table)) { res.push_back(static_cast(table[c])); } else { res.push_back(replacement); } } return res; } template inline Tdststring encode_8bit(const mpt::widestring & str, const char32_t (&table)[256], char replacement = '?') { Tdststring res; res.reserve(str.length()); for (std::size_t i = 0; i < str.length(); ++i) { char32_t c = static_cast(str[i]); bool found = false; // Try non-control characters first. // In cases where there are actual characters mirrored in this range (like in AMS/AMS2 character sets), // characters in the common range are preferred this way. for (std::size_t x = 0x20; x < std::size(table); ++x) { if (c == table[x]) { res.push_back(static_cast(static_cast(x))); found = true; break; } } if (!found) { // try control characters for (std::size_t x = 0x00; x < std::size(table) && x < 0x20; ++x) { if (c == table[x]) { res.push_back(static_cast(static_cast(x))); found = true; break; } } } if (!found) { res.push_back(replacement); } } return res; } template inline mpt::widestring decode_ascii(const Tsrcstring & str, mpt::widechar replacement = MPT_WIDECHAR('\uFFFD')) { mpt::widestring res; res.reserve(str.length()); for (std::size_t i = 0; i < str.length(); ++i) { uint8 c = str[i]; if (c <= 0x7f) { res.push_back(static_cast(static_cast(c))); } else { res.push_back(replacement); } } return res; } template inline Tdststring encode_ascii(const mpt::widestring & str, char replacement = '?') { Tdststring res; res.reserve(str.length()); for (std::size_t i = 0; i < str.length(); ++i) { char32_t c = static_cast(str[i]); if (c <= 0x7f) { res.push_back(static_cast(static_cast(c))); } else { res.push_back(replacement); } } return res; } template inline mpt::widestring decode_iso8859_1(const Tsrcstring & str, mpt::widechar replacement = MPT_WIDECHAR('\uFFFD')) { MPT_UNUSED(replacement); mpt::widestring res; res.reserve(str.length()); for (std::size_t i = 0; i < str.length(); ++i) { uint8 c = str[i]; res.push_back(static_cast(static_cast(c))); } return res; } template inline Tdststring encode_iso8859_1(const mpt::widestring & str, char replacement = '?') { Tdststring res; res.reserve(str.length()); for (std::size_t i = 0; i < str.length(); ++i) { char32_t c = static_cast(str[i]); if (c <= 0xff) { res.push_back(static_cast(static_cast(c))); } else { res.push_back(replacement); } } return res; } template inline mpt::widestring decode_utf8(const Tsrcstring & str, mpt::widechar replacement = MPT_WIDECHAR('\uFFFD')) { const Tsrcstring & in = str; mpt::widestring out; // state: std::size_t charsleft = 0; char32_t ucs4 = 0; for (uint8 c : in) { if (charsleft == 0) { if ((c & 0x80) == 0x00) { out.push_back(static_cast(c)); } else if ((c & 0xE0) == 0xC0) { ucs4 = c & 0x1F; charsleft = 1; } else if ((c & 0xF0) == 0xE0) { ucs4 = c & 0x0F; charsleft = 2; } else if ((c & 0xF8) == 0xF0) { ucs4 = c & 0x07; charsleft = 3; } else { out.push_back(replacement); ucs4 = 0; charsleft = 0; } } else { if ((c & 0xC0) != 0x80) { out.push_back(replacement); ucs4 = 0; charsleft = 0; } ucs4 <<= 6; ucs4 |= c & 0x3F; charsleft--; if (charsleft == 0) { if constexpr (sizeof(mpt::widechar) == 2) { if (ucs4 > 0x1fffff) { out.push_back(replacement); ucs4 = 0; charsleft = 0; } if (ucs4 <= 0xffff) { out.push_back(static_cast(ucs4)); } else { uint32 surrogate = static_cast(ucs4) - 0x10000; uint16 hi_sur = static_cast((0x36 << 10) | ((surrogate >> 10) & ((1 << 10) - 1))); uint16 lo_sur = static_cast((0x37 << 10) | ((surrogate >> 0) & ((1 << 10) - 1))); out.push_back(hi_sur); out.push_back(lo_sur); } } else { out.push_back(static_cast(ucs4)); } ucs4 = 0; } } } if (charsleft != 0) { out.push_back(replacement); ucs4 = 0; charsleft = 0; } return out; } template inline Tdststring encode_utf8(const mpt::widestring & str, typename Tdststring::value_type replacement = static_cast(mpt::char_value('?'))) { const mpt::widestring & in = str; Tdststring out; for (std::size_t i = 0; i < in.length(); i++) { mpt::widechar wc = in[i]; char32_t ucs4 = 0; if constexpr (sizeof(mpt::widechar) == 2) { uint16 c = static_cast(wc); if (i + 1 < in.length()) { // check for surrogate pair uint16 hi_sur = in[i + 0]; uint16 lo_sur = in[i + 1]; if (hi_sur >> 10 == 0x36 && lo_sur >> 10 == 0x37) { // surrogate pair ++i; hi_sur &= (1 << 10) - 1; lo_sur &= (1 << 10) - 1; ucs4 = (static_cast(hi_sur) << 10) | (static_cast(lo_sur) << 0); } else { // no surrogate pair ucs4 = static_cast(c); } } else { // no surrogate possible ucs4 = static_cast(c); } } else { ucs4 = static_cast(wc); } if (ucs4 > 0x1fffff) { out.push_back(replacement); continue; } uint8 utf8[6]; std::size_t numchars = 0; for (numchars = 0; numchars < 6; numchars++) { utf8[numchars] = ucs4 & 0x3F; ucs4 >>= 6; if (ucs4 == 0) { break; } } numchars++; if (numchars == 1) { out.push_back(utf8[0]); continue; } if (numchars == 2 && utf8[numchars - 1] == 0x01) { // generate shortest form out.push_back(utf8[0] | 0x40); continue; } std::size_t charsleft = numchars; while (charsleft > 0) { if (charsleft == numchars) { out.push_back(utf8[charsleft - 1] | (((1 << numchars) - 1) << (8 - numchars))); } else { // cppcheck false-positive // cppcheck-suppress arrayIndexOutOfBounds out.push_back(utf8[charsleft - 1] | 0x80); } charsleft--; } } return out; } template inline Tdststring utf32_from_utf16(const Tsrcstring & in, mpt::widechar replacement = MPT_WIDECHAR('\uFFFD')) { static_assert(sizeof(typename Tsrcstring::value_type) == 2); static_assert(sizeof(typename Tdststring::value_type) == 4); MPT_UNUSED(replacement); Tdststring out; out.reserve(in.length()); for (std::size_t i = 0; i < in.length(); i++) { char16_t wc = static_cast(static_cast(in[i])); char32_t ucs4 = 0; uint16 c = static_cast(wc); if (i + 1 < in.length()) { // check for surrogate pair uint16 hi_sur = in[i + 0]; uint16 lo_sur = in[i + 1]; if (hi_sur >> 10 == 0x36 && lo_sur >> 10 == 0x37) { // surrogate pair ++i; hi_sur &= (1 << 10) - 1; lo_sur &= (1 << 10) - 1; ucs4 = (static_cast(hi_sur) << 10) | (static_cast(lo_sur) << 0); } else { // no surrogate pair ucs4 = static_cast(c); } } else { // no surrogate possible ucs4 = static_cast(c); } out.push_back(static_cast(static_cast(ucs4))); } return out; } template inline Tdststring utf16_from_utf32(const Tsrcstring & in, mpt::widechar replacement = MPT_WIDECHAR('\uFFFD')) { static_assert(sizeof(typename Tsrcstring::value_type) == 4); static_assert(sizeof(typename Tdststring::value_type) == 2); Tdststring out; out.reserve(in.length()); for (std::size_t i = 0; i < in.length(); i++) { char32_t ucs4 = static_cast(static_cast(in[i])); if (ucs4 > 0x1fffff) { out.push_back(static_cast(static_cast(replacement))); ucs4 = 0; } if (ucs4 <= 0xffff) { out.push_back(static_cast(static_cast(ucs4))); } else { uint32 surrogate = static_cast(ucs4) - 0x10000; uint16 hi_sur = static_cast((0x36 << 10) | ((surrogate >> 10) & ((1 << 10) - 1))); uint16 lo_sur = static_cast((0x37 << 10) | ((surrogate >> 0) & ((1 << 10) - 1))); out.push_back(static_cast(hi_sur)); out.push_back(static_cast(lo_sur)); } } return out; } #if MPT_OS_WINDOWS inline bool has_codepage(UINT cp) { return IsValidCodePage(cp) ? true : false; } inline bool windows_has_encoding(common_encoding encoding) { bool result = false; switch (encoding) { case common_encoding::utf8: result = has_codepage(CP_UTF8); break; case common_encoding::ascii: result = has_codepage(20127); break; case common_encoding::iso8859_1: result = has_codepage(28591); break; case common_encoding::iso8859_15: result = has_codepage(28605); break; case common_encoding::cp850: result = has_codepage(850); break; case common_encoding::cp437: result = has_codepage(437); break; case common_encoding::windows1252: result = has_codepage(1252); break; } return result; } inline bool windows_has_encoding(logical_encoding encoding) { bool result = false; switch (encoding) { case logical_encoding::locale: result = true; break; case logical_encoding::active_locale: result = false; break; } return result; } inline UINT codepage_from_encoding(logical_encoding encoding) { UINT result = 0; switch (encoding) { case logical_encoding::locale: result = CP_ACP; break; case logical_encoding::active_locale: result = 0; break; } return result; } inline UINT codepage_from_encoding(common_encoding encoding) { UINT result = 0; switch (encoding) { case common_encoding::utf8: result = CP_UTF8; break; case common_encoding::ascii: result = 20127; break; case common_encoding::iso8859_1: result = 28591; break; case common_encoding::iso8859_15: result = 28605; break; case common_encoding::cp850: result = 850; break; case common_encoding::cp437: result = 437; break; case common_encoding::windows1252: result = 1252; break; } return result; } template inline Tdststring encode_codepage(UINT codepage, const mpt::widestring & src) { static_assert(sizeof(typename Tdststring::value_type) == sizeof(char)); static_assert(mpt::is_character::value); Tdststring encoded_string; int required_size = WideCharToMultiByte(codepage, 0, src.data(), mpt::saturate_cast(src.size()), nullptr, 0, nullptr, nullptr); if (required_size > 0) { encoded_string.resize(required_size); WideCharToMultiByte(codepage, 0, src.data(), mpt::saturate_cast(src.size()), reinterpret_cast(encoded_string.data()), required_size, nullptr, nullptr); } return encoded_string; } template inline mpt::widestring decode_codepage(UINT codepage, const Tsrcstring & src) { static_assert(sizeof(typename Tsrcstring::value_type) == sizeof(char)); static_assert(mpt::is_character::value); mpt::widestring decoded_string; int required_size = MultiByteToWideChar(codepage, 0, reinterpret_cast(src.data()), mpt::saturate_cast(src.size()), nullptr, 0); if (required_size > 0) { decoded_string.resize(required_size); MultiByteToWideChar(codepage, 0, reinterpret_cast(src.data()), mpt::saturate_cast(src.size()), decoded_string.data(), required_size); } return decoded_string; } #endif // MPT_OS_WINDOWS #if MPT_OS_DJGPP inline common_encoding djgpp_get_locale_encoding() { uint16 active_codepage = 437; uint16 system_codepage = 437; __dpmi_regs regs; std::memset(®s, 0, sizeof(__dpmi_regs)); regs.x.ax = 0x6601; if (__dpmi_int(0x21, ®s) == 0) { int cf = (regs.x.flags >> 0) & 1; if (cf == 0) { active_codepage = regs.x.bx; system_codepage = regs.x.dx; } } common_encoding result = common_encoding::cp437; if (active_codepage == 0) { result = common_encoding::cp437; } else if (active_codepage == 437) { result = common_encoding::cp437; } else if (active_codepage == 850) { result = common_encoding::cp850; } else if (system_codepage == 437) { result = common_encoding::cp437; } else if (system_codepage == 850) { result = common_encoding::cp850; } else { result = common_encoding::cp437; } return result; } #endif // MPT_OS_DJGPP #if !defined(MPT_COMPILER_QUIRK_NO_WCHAR) // Note: // // std::codecvt::out in LLVM libc++ does not advance in and out pointers when // running into a non-convertible character. This can happen when no locale is // set on FreeBSD or MacOSX. This behaviour violates the C++ standard. // // We apply the following (albeit costly, even on other platforms) work-around: // If the conversion errors out and does not advance the pointers at all, we // retry the conversion with a space character prepended to the string. If it // still does error out, we retry the whole conversion character by character. // This is costly even on other platforms in one single case: The first // character is an invalid Unicode code point or otherwise not convertible. Any // following non-convertible characters are not a problem. inline std::wstring decode_locale_impl(const std::string & str, const std::locale & locale, wchar_t replacement = L'\uFFFD', int retry = 0, bool * progress = nullptr) { if (str.empty()) { return std::wstring(); } std::vector out; using codecvt_type = std::codecvt; std::mbstate_t state = std::mbstate_t(); const codecvt_type & facet = std::use_facet(locale); codecvt_type::result result = codecvt_type::partial; const char * in_begin = str.data(); const char * in_end = in_begin + str.size(); out.resize((in_end - in_begin) * (mpt::saturate_cast(facet.max_length()) + 1)); wchar_t * out_begin = out.data(); wchar_t * out_end = out.data() + out.size(); const char * in_next = nullptr; wchar_t * out_next = nullptr; do { if (retry == 2) { for (;;) { in_next = nullptr; out_next = nullptr; result = facet.in(state, in_begin, in_begin + 1, in_next, out_begin, out_end, out_next); if (result == codecvt_type::partial && in_next == in_begin + 1) { in_begin = in_next; out_begin = out_next; continue; } else { break; } } } else { in_next = nullptr; out_next = nullptr; result = facet.in(state, in_begin, in_end, in_next, out_begin, out_end, out_next); } if (result == codecvt_type::partial || (result == codecvt_type::error && out_next == out_end)) { out.resize(out.size() * 2); in_begin = in_next; out_begin = out.data() + (out_next - out_begin); out_end = out.data() + out.size(); continue; } if (retry == 0) { if (result == codecvt_type::error && in_next == in_begin && out_next == out_begin) { bool made_progress = true; decode_locale_impl(std::string(" ") + str, locale, replacement, 1, &made_progress); if (!made_progress) { return decode_locale_impl(str, locale, replacement, 2); } } } else if (retry == 1) { if (result == codecvt_type::error && in_next == in_begin && out_next == out_begin) { *progress = false; } else { *progress = true; } return std::wstring(); } if (result == codecvt_type::error) { ++in_next; *out_next = replacement; ++out_next; } in_begin = in_next; out_begin = out_next; } while ((result == codecvt_type::error && in_next < in_end && out_next < out_end) || (retry == 2 && in_next < in_end)); return std::wstring(out.data(), out_next); } template inline mpt::widestring decode_locale(const std::locale & locale, const Tsrcstring & src) { if constexpr (std::is_same::value) { return decode_locale_impl(src, locale); } else { return decode_locale_impl(std::string(src.begin(), src.end()), locale); } } inline std::string encode_locale_impl(const std::wstring & str, const std::locale & locale, char replacement = '?', int retry = 0, bool * progress = nullptr) { if (str.empty()) { return std::string(); } std::vector out; using codecvt_type = std::codecvt; std::mbstate_t state = std::mbstate_t(); const codecvt_type & facet = std::use_facet(locale); codecvt_type::result result = codecvt_type::partial; const wchar_t * in_begin = str.data(); const wchar_t * in_end = in_begin + str.size(); out.resize((in_end - in_begin) * (mpt::saturate_cast(facet.max_length()) + 1)); char * out_begin = out.data(); char * out_end = out.data() + out.size(); const wchar_t * in_next = nullptr; char * out_next = nullptr; do { if (retry == 2) { for (;;) { in_next = nullptr; out_next = nullptr; result = facet.out(state, in_begin, in_begin + 1, in_next, out_begin, out_end, out_next); if (result == codecvt_type::partial && in_next == in_begin + 1) { in_begin = in_next; out_begin = out_next; continue; } else { break; } } } else { in_next = nullptr; out_next = nullptr; result = facet.out(state, in_begin, in_end, in_next, out_begin, out_end, out_next); } if (result == codecvt_type::partial || (result == codecvt_type::error && out_next == out_end)) { out.resize(out.size() * 2); in_begin = in_next; out_begin = out.data() + (out_next - out_begin); out_end = out.data() + out.size(); continue; } if (retry == 0) { if (result == codecvt_type::error && in_next == in_begin && out_next == out_begin) { bool made_progress = true; encode_locale_impl(std::wstring(L" ") + str, locale, replacement, 1, &made_progress); if (!made_progress) { return encode_locale_impl(str, locale, replacement, 2); } } } else if (retry == 1) { if (result == codecvt_type::error && in_next == in_begin && out_next == out_begin) { *progress = false; } else { *progress = true; } return std::string(); } if (result == codecvt_type::error) { ++in_next; *out_next = replacement; ++out_next; } in_begin = in_next; out_begin = out_next; } while ((result == codecvt_type::error && in_next < in_end && out_next < out_end) || (retry == 2 && in_next < in_end)); return std::string(out.data(), out_next); } template inline Tdststring encode_locale(const std::locale & locale, const mpt::widestring & src) { if constexpr (std::is_same::value) { return encode_locale_impl(src, locale); } else { const std::string tmp = encode_locale_impl(src, locale); return Tdststring(tmp.begin(), tmp.end()); } } #endif // !MPT_COMPILER_QUIRK_NO_WCHAR #if MPT_OS_WINDOWS template inline Tdststring encode(UINT codepage, const mpt::widestring & src) { static_assert(sizeof(typename Tdststring::value_type) == sizeof(char)); static_assert(mpt::is_character::value); return encode_codepage(codepage, src); } #endif // MPT_OS_WINDOWS #if !defined(MPT_COMPILER_QUIRK_NO_WCHAR) template inline Tdststring encode(const std::locale & locale, const mpt::widestring & src) { static_assert(sizeof(typename Tdststring::value_type) == sizeof(char)); static_assert(mpt::is_character::value); return encode_locale(src, locale); } #endif // !MPT_COMPILER_QUIRK_NO_WCHAR template inline Tdststring encode(const char32_t (&table)[256], const mpt::widestring & src) { static_assert(sizeof(typename Tdststring::value_type) == sizeof(char)); static_assert(mpt::is_character::value); return encode_8bit(src, table); } template inline Tdststring encode(common_encoding encoding, const mpt::widestring & src) { static_assert(sizeof(typename Tdststring::value_type) == sizeof(char)); static_assert(mpt::is_character::value); #if MPT_OS_WINDOWS if (windows_has_encoding(encoding)) { return encode_codepage(codepage_from_encoding(encoding), src); } #endif switch (encoding) { case common_encoding::utf8: return encode_utf8(src); break; case common_encoding::ascii: return encode_ascii(src); break; case common_encoding::iso8859_1: return encode_iso8859_1(src); break; case common_encoding::iso8859_15: return encode_8bit(src, CharsetTableISO8859_15); break; case common_encoding::cp437: return encode_8bit(src, CharsetTableCP437); break; case common_encoding::cp850: return encode_8bit(src, CharsetTableCP850); break; case common_encoding::windows1252: return encode_8bit(src, CharsetTableWindows1252); break; } throw std::domain_error("unsupported encoding"); } template inline Tdststring encode(logical_encoding encoding, const mpt::widestring & src) { static_assert(sizeof(typename Tdststring::value_type) == sizeof(char)); static_assert(mpt::is_character::value); #if MPT_OS_WINDOWS if (windows_has_encoding(encoding)) { return encode_codepage(codepage_from_encoding(encoding), src); } #endif #if MPT_OS_DJGPP switch (encoding) { case logical_encoding::locale: return encode(djgpp_get_locale_encoding(), src); break; case logical_encoding::active_locale: return encode(djgpp_get_locale_encoding(), src); break; } throw std::domain_error("unsupported encoding"); #elif !defined(MPT_COMPILER_QUIRK_NO_WCHAR) switch (encoding) { case logical_encoding::locale: return encode_locale(std::locale(""), src); break; case logical_encoding::active_locale: return encode_locale(std::locale(), src); break; } throw std::domain_error("unsupported encoding"); #else throw std::domain_error("unsupported encoding"); #endif } #if MPT_OS_WINDOWS template inline mpt::widestring decode(UINT codepage, const Tsrcstring & src) { static_assert(sizeof(typename Tsrcstring::value_type) == sizeof(char)); static_assert(mpt::is_character::value); return decode_codepage(codepage, src); } #endif // MPT_OS_WINDOWS #if !defined(MPT_COMPILER_QUIRK_NO_WCHAR) template inline mpt::widestring decode(const std::locale & locale, const Tsrcstring & src) { static_assert(sizeof(typename Tsrcstring::value_type) == sizeof(char)); static_assert(mpt::is_character::value); return decode_locale(src, locale); } #endif // !MPT_COMPILER_QUIRK_NO_WCHAR template inline mpt::widestring decode(const char32_t (&table)[256], const Tsrcstring & src) { static_assert(sizeof(typename Tsrcstring::value_type) == sizeof(char)); static_assert(mpt::is_character::value); return decode_8bit(src, table); } template inline mpt::widestring decode(common_encoding encoding, const Tsrcstring & src) { static_assert(sizeof(typename Tsrcstring::value_type) == sizeof(char)); static_assert(mpt::is_character::value); #if MPT_OS_WINDOWS if (windows_has_encoding(encoding)) { return decode_codepage(codepage_from_encoding(encoding), src); } #endif switch (encoding) { case common_encoding::utf8: return decode_utf8(src); break; case common_encoding::ascii: return decode_ascii(src); break; case common_encoding::iso8859_1: return decode_iso8859_1(src); break; case common_encoding::iso8859_15: return decode_8bit(src, CharsetTableISO8859_15); break; case common_encoding::cp437: return decode_8bit(src, CharsetTableCP437); break; case common_encoding::cp850: return decode_8bit(src, CharsetTableCP850); break; case common_encoding::windows1252: return decode_8bit(src, CharsetTableWindows1252); break; } throw std::domain_error("unsupported encoding"); } template inline mpt::widestring decode(logical_encoding encoding, const Tsrcstring & src) { static_assert(sizeof(typename Tsrcstring::value_type) == sizeof(char)); static_assert(mpt::is_character::value); #if MPT_OS_WINDOWS if (windows_has_encoding(encoding)) { return decode_codepage(codepage_from_encoding(encoding), src); } #endif #if MPT_OS_DJGPP switch (encoding) { case logical_encoding::locale: return decode(djgpp_get_locale_encoding(), src); break; case logical_encoding::active_locale: return decode(djgpp_get_locale_encoding(), src); break; } throw std::domain_error("unsupported encoding"); #elif !defined(MPT_COMPILER_QUIRK_NO_WCHAR) switch (encoding) { case logical_encoding::locale: return decode_locale(std::locale(""), src); break; case logical_encoding::active_locale: return decode_locale(std::locale(), src); break; } throw std::domain_error("unsupported encoding"); #else throw std::domain_error("unsupported encoding"); #endif } inline bool is_utf8(const std::string & str) { return (str == encode(common_encoding::utf8, decode(common_encoding::utf8, str))); } template struct string_transcoder { }; template struct string_transcoder>> { using string_type = std::basic_string>; static inline mpt::widestring decode(const string_type & src) { return mpt::decode(encoding, src); } static inline string_type encode(const mpt::widestring & src) { return mpt::encode(encoding, src); } }; template struct string_transcoder>> { using string_type = std::basic_string>; static inline mpt::widestring decode(const string_type & src) { return mpt::decode(encoding, src); } static inline string_type encode(const mpt::widestring & src) { return mpt::encode(encoding, src); } }; #if !defined(MPT_COMPILER_QUIRK_NO_WCHAR) template <> struct string_transcoder { using string_type = std::wstring; static inline mpt::widestring decode(const string_type & src) { return src; } static inline string_type encode(const mpt::widestring & src) { return src; } }; #endif // !MPT_COMPILER_QUIRK_NO_WCHAR #if MPT_CXX_AT_LEAST(20) template <> struct string_transcoder { using string_type = std::u8string; static inline mpt::widestring decode(const string_type & src) { return mpt::decode_utf8(src); } static inline string_type encode(const mpt::widestring & src) { return mpt::encode_utf8(src); } }; #endif // C++10 template <> struct string_transcoder { using string_type = std::u16string; static inline mpt::widestring decode(const string_type & src) { if constexpr (sizeof(mpt::widechar) == sizeof(char16_t)) { return mpt::widestring(src.begin(), src.end()); } else { return utf32_from_utf16(src); } } static inline string_type encode(const mpt::widestring & src) { if constexpr (sizeof(mpt::widechar) == sizeof(char16_t)) { return string_type(src.begin(), src.end()); } else { return utf16_from_utf32(src); } } }; #if defined(MPT_COMPILER_QUIRK_NO_WCHAR) template <> struct string_transcoder { using string_type = std::u32string; static inline mpt::widestring decode(const string_type & src) { return src; } static inline string_type encode(const mpt::widestring & src) { return src; } }; #else // !MPT_COMPILER_QUIRK_NO_WCHAR template <> struct string_transcoder { using string_type = std::u32string; static inline mpt::widestring decode(const string_type & src) { if constexpr (sizeof(mpt::widechar) == sizeof(char32_t)) { return mpt::widestring(src.begin(), src.end()); } else { return utf16_from_utf32(src); } } static inline string_type encode(const mpt::widestring & src) { if constexpr (sizeof(mpt::widechar) == sizeof(char32_t)) { return string_type(src.begin(), src.end()); } else { return utf32_from_utf16(src); } } }; #endif // MPT_COMPILER_QUIRK_NO_WCHAR #if MPT_DETECTED_MFC template <> struct string_transcoder { using string_type = CStringW; static inline mpt::widestring decode(const string_type & src) { return mpt::widestring(src.GetString()); } static inline string_type encode(const mpt::widestring & src) { return src.c_str(); } }; template <> struct string_transcoder { using string_type = CStringA; static inline mpt::widestring decode(const string_type & src) { return mpt::decode(mpt::logical_encoding::locale, std::string(src.GetString())); } static inline string_type encode(const mpt::widestring & src) { return mpt::encode(mpt::logical_encoding::locale, src).c_str(); } }; #endif // MPT_DETECTED_MFC template ::type>::value, bool> = true> inline Tdststring transcode(const Tsrcstring & src) { if constexpr (std::is_same::type>::value) { return mpt::as_string(src); } else { return string_transcoder::encode(string_transcoder::decode(mpt::as_string(src))); } } template ::value, bool> = true, std::enable_if_t::type>::value, bool> = true> inline Tdststring transcode(Tencoding to, const Tsrcstring & src) { return mpt::encode(to, string_transcoder::decode(mpt::as_string(src))); } template ::type, std::string>::value, bool> = true, std::enable_if_t::type>::value, bool> = true> inline Tdststring transcode(Tencoding from, const Tsrcstring & src) { return string_transcoder::encode(mpt::decode(from, mpt::as_string(src))); } template ::type>::value, bool> = true> inline Tdststring transcode(Tto to, Tfrom from, const Tsrcstring & src) { return mpt::encode(to, mpt::decode(from, mpt::as_string(src))); } } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_STRING_TRANSCODE_TRANSCODE_HPP libopenmpt-0.6.1+release.autotools/src/mpt/string_transcode/macros.hpp0000644000175000017500000000130414107230757023214 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_STRING_TRANSCODE_MACROS_HPP #define MPT_STRING_TRANSCODE_MACROS_HPP #include "mpt/string/types.hpp" #include "mpt/string_transcode/transcode.hpp" #include // The MPT_UTF8_STRING allows specifying UTF8 char arrays. // The resulting type is mpt::ustring and the construction might require runtime translation, // i.e. it is NOT generally available at compile time. // Use explicit UTF8 encoding, // i.e. U+00FC (LATIN SMALL LETTER U WITH DIAERESIS) would be written as "\xC3\xBC". #define MPT_UTF8_STRING(x) mpt::transcode(mpt::common_encoding::utf8, std::string{x}) #endif // MPT_STRING_TRANSCODE_MACROS_HPP libopenmpt-0.6.1+release.autotools/src/mpt/system_error/0000755000175000017500000000000014175541574020475 500000000000000libopenmpt-0.6.1+release.autotools/src/mpt/system_error/system_error.hpp0000644000175000017500000000713514107230757023662 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_SYSTEM_ERROR_SYSTEM_ERROR_HPP #define MPT_SYSTEM_ERROR_SYSTEM_ERROR_HPP #include "mpt/base/detect.hpp" #include "mpt/base/namespace.hpp" #include "mpt/format/message.hpp" #include "mpt/format/message_macros.hpp" #include "mpt/format/simple.hpp" #include "mpt/string/types.hpp" #include "mpt/string_transcode/transcode.hpp" #include "mpt/out_of_memory/out_of_memory.hpp" #if MPT_OS_WINDOWS #include #if MPT_OS_WINDOWS_WINRT #include #endif // MPT_OS_WINDOWS_WINRT #endif // MPT_OS_WINDOWS #if MPT_OS_WINDOWS #include #endif // MPT_OS_WINDOWS namespace mpt { inline namespace MPT_INLINE_NS { #if MPT_OS_WINDOWS namespace windows { inline mpt::ustring GetErrorMessage(DWORD errorCode, HANDLE hModule = NULL) { #if MPT_OS_WINDOWS_WINRT std::vector msgbuf(65536); if (FormatMessage( (hModule ? FORMAT_MESSAGE_FROM_HMODULE : 0) | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, hModule, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), msgbuf.data(), mpt::saturate_cast(msgbuf.size()), NULL) == 0) { DWORD e = GetLastError(); if ((e == ERROR_NOT_ENOUGH_MEMORY) || (e == ERROR_OUTOFMEMORY)) { mpt::throw_out_of_memory(); } return {}; } return mpt::transcode(mpt::winstring{msgbuf.data()}); #else mpt::ustring message; void * lpMsgBuf = nullptr; if (FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | (hModule ? FORMAT_MESSAGE_FROM_HMODULE : 0) | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, hModule, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL) == 0) { DWORD e = GetLastError(); if (lpMsgBuf) { LocalFree(lpMsgBuf); } if ((e == ERROR_NOT_ENOUGH_MEMORY) || (e == ERROR_OUTOFMEMORY)) { mpt::throw_out_of_memory(); } return {}; } if (!lpMsgBuf) { return {}; } try { message = mpt::transcode(mpt::winstring{static_cast(lpMsgBuf)}); } catch (mpt::out_of_memory e) { LocalFree(lpMsgBuf); mpt::rethrow_out_of_memory(e); } LocalFree(lpMsgBuf); return message; #endif } class error : public std::runtime_error { public: error(DWORD errorCode, HANDLE hModule = NULL) : std::runtime_error(mpt::transcode(mpt::exception_encoding, MPT_UFORMAT_MESSAGE("Windows Error: 0x{}: {}")(mpt::format::hex0<8>(errorCode), GetErrorMessage(errorCode, hModule)))) { return; } }; inline HANDLE CheckFileHANDLE(HANDLE handle) { if (handle == INVALID_HANDLE_VALUE) { DWORD err = ::GetLastError(); if ((err == ERROR_NOT_ENOUGH_MEMORY) || (err == ERROR_OUTOFMEMORY)) { mpt::throw_out_of_memory(); } throw windows::error(err); } return handle; } inline HANDLE CheckHANDLE(HANDLE handle) { if (handle == NULL) { DWORD err = ::GetLastError(); if ((err == ERROR_NOT_ENOUGH_MEMORY) || (err == ERROR_OUTOFMEMORY)) { mpt::throw_out_of_memory(); } throw windows::error(err); } return handle; } inline void CheckBOOL(BOOL result) { if (result == FALSE) { DWORD err = ::GetLastError(); if ((err == ERROR_NOT_ENOUGH_MEMORY) || (err == ERROR_OUTOFMEMORY)) { mpt::throw_out_of_memory(); } throw windows::error(err); } } inline void ExpectError(DWORD expected) { DWORD err = ::GetLastError(); if (err != expected) { if ((err == ERROR_NOT_ENOUGH_MEMORY) || (err == ERROR_OUTOFMEMORY)) { mpt::throw_out_of_memory(); } throw windows::error(err); } } } // namespace windows #endif // MPT_OS_WINDOWS } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_SYSTEM_ERROR_SYSTEM_ERROR_HPP libopenmpt-0.6.1+release.autotools/src/mpt/test/0000755000175000017500000000000014175541574016717 500000000000000libopenmpt-0.6.1+release.autotools/src/mpt/test/test.hpp0000644000175000017500000004547014075537720020336 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_TEST_TEST_HPP #define MPT_TEST_TEST_HPP #include "mpt/base/detect.hpp" #include "mpt/base/namespace.hpp" #include "mpt/base/source_location.hpp" #include #include #include #include #include #include #include #include #include #include #include namespace mpt { inline namespace MPT_INLINE_NS { namespace test { template struct is_to_stream_writable : std::false_type { }; template struct is_to_stream_writable() << std::declval())>> : std::true_type { }; template inline auto format(const T & x) -> typename std::enable_if::value, std::string>::type { std::ostringstream s; s << x; return s.str(); } template inline auto format(const T & x) -> typename std::enable_if::value, std::string>::type { return typeid(x).name(); } inline std::string get_exception_text() { std::string result; try { // cppcheck false-positive // cppcheck-suppress rethrowNoCurrentException throw; } catch (const std::exception & e) { result = e.what(); } catch (...) { result = "unknown exception"; } return result; } struct result_success { }; struct result_failure { std::string text{}; }; struct result_unexpected_exception { std::string text{}; }; struct result { std::variant info{std::monostate{}}; }; struct statistics_counters { std::size_t total{0}; std::size_t run{0}; std::size_t successes{0}; std::size_t failures{0}; std::size_t unexpected_exceptions{0}; std::size_t completed{0}; constexpr statistics_counters & operator+=(const statistics_counters & other) noexcept { total += other.total; run += other.run; successes += other.successes; failures += other.failures; unexpected_exceptions += other.unexpected_exceptions; completed += other.completed; return *this; } }; struct group_statistics { statistics_counters tests{}; statistics_counters cases{}; statistics_counters local_cases{}; }; struct global_statistics { statistics_counters groups{}; statistics_counters tests{}; statistics_counters cases{}; std::map individual_group_statistics{}; explicit constexpr operator bool() noexcept { return succeeded(); } constexpr bool operator!() noexcept { return failed(); } constexpr bool succeeded() noexcept { return groups.successes == groups.run; } constexpr bool failed() noexcept { return groups.failures > 0 || groups.unexpected_exceptions > 0; } }; class reporter_interface { protected: virtual ~reporter_interface() = default; public: virtual void run_begin(const mpt::source_location & loc) = 0; virtual void group_begin(const mpt::source_location & loc, const char * name) = 0; virtual void test_begin(const mpt::source_location & loc, const char * name) = 0; virtual void case_run(const mpt::source_location & loc) = 0; virtual void case_run(const mpt::source_location & loc, const char * text_e) = 0; virtual void case_run(const mpt::source_location & loc, const char * text_ex, const char * text_e) = 0; virtual void case_run(const mpt::source_location & loc, const char * text_a, const char * text_cmp, const char * text_b) = 0; virtual void case_result(const mpt::source_location & loc, const mpt::test::result & result) = 0; virtual void test_end(const mpt::source_location & loc, const char * name, const statistics_counters & counters) = 0; virtual void group_end(const mpt::source_location & loc, const char * name, const group_statistics & statistics) = 0; virtual void run_end(const mpt::source_location & loc, const global_statistics & statistics) = 0; virtual void immediate_breakpoint() = 0; }; class silent_reporter : public reporter_interface { public: silent_reporter() = default; ~silent_reporter() override = default; public: virtual void run_begin(const mpt::source_location &) override { } virtual void group_begin(const mpt::source_location &, const char *) override { } virtual void test_begin(const mpt::source_location &, const char *) override { } virtual void case_run(const mpt::source_location &) override { } virtual void case_run(const mpt::source_location &, const char *) override { } virtual void case_run(const mpt::source_location &, const char *, const char *) override { } virtual void case_run(const mpt::source_location &, const char *, const char *, const char *) override { } virtual void case_result(const mpt::source_location &, const mpt::test::result &) override { } virtual void test_end(const mpt::source_location &, const char *, const statistics_counters &) override { } virtual void group_end(const mpt::source_location &, const char *, const group_statistics &) override { } virtual void run_end(const mpt::source_location &, const global_statistics &) override { } virtual void immediate_breakpoint() override { } }; class simple_reporter : public reporter_interface { private: std::ostream & s; public: simple_reporter(std::ostream & s_) : s(s_) { s.flush(); } ~simple_reporter() override { s.flush(); } public: void run_begin(const mpt::source_location & loc) override { static_cast(loc); s << "Running test suite ..." << std::endl; } void group_begin(const mpt::source_location & loc, const char * name) override { static_cast(loc); s << "Running group '" << name << "' ..." << std::endl; } void test_begin(const mpt::source_location & loc, const char * name) override { static_cast(loc); s << " Running test '" << name << "' ..." << std::endl; } void case_run(const mpt::source_location & loc) override { static_cast(loc); s << " Checking ..." << std::endl; } void case_run(const mpt::source_location & loc, const char * text_e) override { static_cast(loc); s << " Checking '" << text_e << "' ..." << std::endl; } void case_run(const mpt::source_location & loc, const char * text_ex, const char * text_e) override { static_cast(loc); if (text_ex) { s << " Checking '" << text_e << " throws " << text_ex << "' ..." << std::endl; } else { s << " Checking '" << text_e << " throws' ..." << std::endl; } } void case_run(const mpt::source_location & loc, const char * text_a, const char * text_cmp, const char * text_b) override { static_cast(loc); s << " Checking '" << text_a << " " << text_cmp << " " << text_b << "' ..." << std::endl; } void case_result(const mpt::source_location & loc, const mpt::test::result & result) override { static_cast(loc); s << " Checking done: "; if (std::holds_alternative(result.info)) { s << "Success."; } else if (std::holds_alternative(result.info)) { s << "FAILURE: " << std::get(result.info).text; } else if (std::holds_alternative(result.info)) { s << "UNEXPECTED EXCEPTION: " << std::get(result.info).text; } s << std::endl; } void test_end(const mpt::source_location & loc, const char * name, const statistics_counters & counters) override { static_cast(loc); static_cast(counters); s << " Running test '" << name << "' done." << std::endl; } void group_end(const mpt::source_location & loc, const char * name, const group_statistics & statistics) override { static_cast(loc); static_cast(statistics); s << "Running group '" << name << "' done." << std::endl; } void run_end(const mpt::source_location & loc, const global_statistics & statistics) override { static_cast(loc); s << "Running test suite done." << std::endl; s << "groups: " << statistics.groups.total << " | " << statistics.groups.successes << " passed"; if (statistics.groups.failures || statistics.groups.unexpected_exceptions) { s << " | " << statistics.groups.failures << " FAILED"; if (statistics.groups.unexpected_exceptions) { s << " | " << statistics.groups.unexpected_exceptions << " UNEXPECTED EXCEPTIONS"; } } s << std::endl; s << "tests: " << statistics.tests.total << " | " << statistics.tests.successes << " passed"; if (statistics.tests.failures || statistics.tests.unexpected_exceptions) { s << " | " << statistics.tests.failures << " FAILED"; if (statistics.tests.unexpected_exceptions) { s << " | " << statistics.tests.unexpected_exceptions << " UNEXPECTED EXCEPTIONS"; } } s << std::endl; s << "checks: " << statistics.cases.total << " | " << statistics.cases.successes << " passed"; if (statistics.cases.failures || statistics.cases.unexpected_exceptions) { s << " | " << statistics.cases.failures << " FAILED"; if (statistics.cases.unexpected_exceptions) { s << " | " << statistics.cases.unexpected_exceptions << " UNEXPECTED EXCEPTIONS"; } } s << std::endl; } void immediate_breakpoint() override { return; } }; struct group; struct context { mpt::test::group & group; mpt::test::reporter_interface & reporter; mpt::test::group_statistics statistics{}; }; using void_context_function = void (*)(mpt::test::context &); struct group { group * next{nullptr}; const char * name{""}; void_context_function func{nullptr}; inline group(const char * name_, void_context_function f) : name(name_) , func(f) { next = group_list(); group_list() = this; } group_statistics run(mpt::test::reporter_interface & reporter, const mpt::source_location & loc = mpt::source_location::current()) { mpt::test::context context{*this, reporter}; context.reporter.group_begin(loc, name); if (func) { func(context); } context.reporter.group_end(loc, name, context.statistics); return context.statistics; } public: [[nodiscard]] static inline group *& group_list() noexcept { static group * group_list = nullptr; return group_list; } }; inline global_statistics run_all(mpt::test::reporter_interface & reporter, const mpt::source_location & loc = mpt::source_location::current()) { global_statistics statistics{}; reporter.run_begin(loc); for (group * g = group::group_list(); g; g = g->next) { statistics.groups.total++; statistics.groups.run++; group_statistics s = g->run(reporter, loc); if (s.tests.unexpected_exceptions) { statistics.groups.unexpected_exceptions++; } else if (s.tests.failures) { statistics.groups.failures++; } else { statistics.groups.successes++; } statistics.tests += s.tests; statistics.cases += s.cases; statistics.groups.completed++; statistics.individual_group_statistics[g->name] = s; } reporter.run_end(loc, statistics); return statistics; } struct test { mpt::test::context & context; const char * name{""}; mpt::source_location source_location{mpt::source_location::current()}; void (*breakpoint)(void){nullptr}; test(const test &) = delete; test & operator=(const test &) = delete; inline test(mpt::test::context & context_, void (*breakpoint_)(void) = nullptr, const mpt::source_location & source_location_ = mpt::source_location::current()) : context(context_) , source_location(source_location_) , breakpoint(breakpoint_) { report_test_begin(); } inline test(mpt::test::context & context_, const char * name_, void (*breakpoint_)(void) = nullptr, const mpt::source_location & source_location_ = mpt::source_location::current()) : context(context_) , name(name_) , source_location(source_location_) , breakpoint(breakpoint_) { report_test_begin(); } inline ~test() { report_test_end(); } inline void immediate_breakpoint() { if (breakpoint) { breakpoint(); } else { context.reporter.immediate_breakpoint(); } } void report_test_begin() { context.statistics.tests.total++; context.statistics.tests.run++; context.statistics.local_cases = statistics_counters{}; context.reporter.test_begin(source_location, name); } void report_run() { context.statistics.local_cases.total++; context.statistics.local_cases.run++; context.reporter.case_run(source_location); } void report_run(const char * text_e) { context.statistics.local_cases.total++; context.statistics.local_cases.run++; context.reporter.case_run(source_location, text_e); } void report_run(const char * text_ex, const char * text_e) { context.statistics.local_cases.total++; context.statistics.local_cases.run++; context.reporter.case_run(source_location, text_ex, text_e); } void report_run(const char * text_a, const char * text_cmp, const char * text_b) { context.statistics.local_cases.total++; context.statistics.local_cases.run++; context.reporter.case_run(source_location, text_a, text_cmp, text_b); } void report_result(mpt::test::result result) { if (std::holds_alternative(result.info)) { context.statistics.local_cases.successes++; } else if (std::holds_alternative(result.info)) { context.statistics.local_cases.failures++; } else if (std::holds_alternative(result.info)) { context.statistics.local_cases.unexpected_exceptions++; } context.statistics.local_cases.completed++; context.reporter.case_result(source_location, result); } void report_test_end() { context.statistics.cases += context.statistics.local_cases; if (context.statistics.local_cases.unexpected_exceptions) { context.statistics.tests.unexpected_exceptions++; } else if (context.statistics.local_cases.failures) { context.statistics.tests.failures++; } else { context.statistics.tests.successes++; } context.statistics.tests.completed++; context.reporter.test_end(source_location, name, context.statistics.local_cases); } template ::value, bool>::type = true> inline test & expect_throws(Tcallable c, const char * text_ex = nullptr, const char * text_e = nullptr) { const std::type_info & tiexception = typeid(Texception); const std::type_info & tic = typeid(decltype(c())); report_run(text_ex ? text_ex : tiexception.name(), text_e ? text_e : tic.name()); mpt::test::result result; try { c(); immediate_breakpoint(); result.info = mpt::test::result_failure{}; } catch (const Texception &) { result.info = mpt::test::result_success{}; } catch (...) { immediate_breakpoint(); result.info = mpt::test::result_unexpected_exception{mpt::test::get_exception_text()}; } report_result(result); return *this; } template ::value, bool>::type = true> inline test & expect_throws_any(Tcallable c, const char * text_e = nullptr) { const std::type_info & tic = typeid(decltype(c())); report_run(nullptr, text_e ? text_e : tic.name()); mpt::test::result result; try { c(); immediate_breakpoint(); result.info = mpt::test::result_failure{}; } catch (...) { result.info = mpt::test::result_success{}; } report_result(result); return *this; } template ::value, bool>::type = true> inline test & expect(Texpr e, const char * text_e = nullptr) { const std::type_info & tie = typeid(decltype(std::invoke(e))); report_run(text_e ? text_e : tie.name()); mpt::test::result result; try { const auto ve = std::invoke(e); if (!ve) { immediate_breakpoint(); result.info = mpt::test::result_failure{/*mpt::test::format(ve)*/}; } else { result.info = mpt::test::result_success{}; } } catch (...) { immediate_breakpoint(); result.info = mpt::test::result_unexpected_exception{mpt::test::get_exception_text()}; } report_result(result); return *this; } template ::value, bool>::type = true, typename std::enable_if::value, bool>::type = true> inline test & expect(Ta && a, Tcmp cmp, Tb && b, const char * text_a = nullptr, const char * text_cmp = nullptr, const char * text_b = nullptr) { const std::type_info & tia = typeid(decltype(std::invoke(a))); const std::type_info & ticmp = typeid(decltype(cmp)); const std::type_info & tib = typeid(decltype(std::invoke(b))); report_run(text_a ? text_a : tia.name(), text_cmp ? text_cmp : ticmp.name(), text_b ? text_b : tib.name()); mpt::test::result result; try { const auto va = std::invoke(a); const auto vb = std::invoke(b); if (!cmp(va, vb)) { immediate_breakpoint(); result.info = mpt::test::result_failure{mpt::test::format(va) + " " + mpt::test::format(cmp) + " " + mpt::test::format(vb)}; } else { result.info = mpt::test::result_success{}; } } catch (...) { immediate_breakpoint(); result.info = mpt::test::result_unexpected_exception{mpt::test::get_exception_text()}; } report_result(result); return *this; } template ::value, bool>::type = true> inline test & expect(Texpr && e, const char * text_e = nullptr) { const std::type_info & tie = typeid(decltype(std::forward(e))); report_run(text_e ? text_e : tie.name()); mpt::test::result result; try { const auto ve = std::forward(e); if (!ve) { immediate_breakpoint(); result.info = mpt::test::result_failure{/*mpt::test::format(ve)*/}; } else { result.info = mpt::test::result_success{}; } } catch (...) { immediate_breakpoint(); result.info = mpt::test::result_unexpected_exception{mpt::test::get_exception_text()}; } report_result(result); return *this; } template ::value, bool>::type = true, typename std::enable_if::value, bool>::type = true> inline test & expect(Ta && a, Tcmp cmp, Tb && b, const char * text_a = nullptr, const char * text_cmp = nullptr, const char * text_b = nullptr) { const std::type_info & tia = typeid(decltype(std::forward(a))); const std::type_info & ticmp = typeid(decltype(cmp)); const std::type_info & tib = typeid(decltype(std::forward(b))); report_run(text_a ? text_a : tia.name(), text_cmp ? text_cmp : ticmp.name(), text_b ? text_b : tib.name()); mpt::test::result result; try { const auto va = std::forward(a); const auto vb = std::forward(b); if (!cmp(va, vb)) { immediate_breakpoint(); result.info = mpt::test::result_failure{mpt::test::format(va) + " " + mpt::test::format(cmp) + " " + mpt::test::format(vb)}; } else { result.info = mpt::test::result_success{}; } } catch (...) { immediate_breakpoint(); result.info = mpt::test::result_unexpected_exception{mpt::test::get_exception_text()}; } report_result(result); return *this; } }; } // namespace test } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_TEST_TEST_HPP libopenmpt-0.6.1+release.autotools/src/mpt/test/test_macros.hpp0000644000175000017500000000544314044173557021676 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_TEST_TEST_MACROS_HPP #define MPT_TEST_TEST_MACROS_HPP #include "mpt/base/namespace.hpp" #include "mpt/base/preprocessor.hpp" #include "mpt/test/test.hpp" #include namespace mpt { inline namespace MPT_INLINE_NS { namespace test { #define MPT_TEST_GROUP_BEGIN(name) \ inline mpt::test::group MPT_PP_UNIQUE_IDENTIFIER(mpt_test_group_name) { \ name, [](mpt::test::context & mpt_test_context) { #define MPT_TEST_GROUP_END() \ } \ } \ ; #define MPT_TEST_GROUP_INLINE_IDENTIFIER(identifier, name) \ inline void MPT_PP_JOIN(mpt_test_group_func_, identifier)(mpt::test::context & mpt_test_context); \ inline mpt::test::group MPT_PP_JOIN(mpt_test_group_name_, identifier){ \ name, [](mpt::test::context & mpt_test_context) { \ MPT_PP_JOIN(mpt_test_group_func_, identifier) \ (mpt_test_context); \ }}; \ inline void MPT_PP_UNIQUE_IDENTIFIER(mpt_test_group_func)(mpt::test::context & mpt_test_context) #define MPT_TEST_GROUP_INLINE(name) \ inline void MPT_PP_UNIQUE_IDENTIFIER(mpt_test_group_func)(mpt::test::context & mpt_test_context); \ inline mpt::test::group MPT_PP_UNIQUE_IDENTIFIER(mpt_test_group_name){ \ name, [](mpt::test::context & mpt_test_context) { \ MPT_PP_UNIQUE_IDENTIFIER(mpt_test_group_func) \ (mpt_test_context); \ }}; \ inline void MPT_PP_UNIQUE_IDENTIFIER(mpt_test_group_func)(mpt::test::context & mpt_test_context) #define MPT_TEST_GROUP_STATIC(name) \ static void MPT_PP_UNIQUE_IDENTIFIER(mpt_test_group_func)(mpt::test::context & mpt_test_context); \ static mpt::test::group MPT_PP_UNIQUE_IDENTIFIER(mpt_test_group_name){ \ name, [](mpt::test::context & mpt_test_context) { \ MPT_PP_UNIQUE_IDENTIFIER(mpt_test_group_func) \ (mpt_test_context); \ }}; \ static void MPT_PP_UNIQUE_IDENTIFIER(mpt_test_group_func)(mpt::test::context & mpt_test_context) #define MPT_TEST_DELAYED(x) [&] { \ return x; \ } #define MPT_TEST_EXPECT mpt::test::test{context}.expect #define MPT_TEST_EXPECT_EXPR(e) mpt::test::test{mpt_test_context}.expect([&] { return e; }, #e) #define MPT_TEST_EXPECT_CMP(a, cmp, b) mpt::test::test{mpt_test_context}.expect([&] { return a; }, [](const auto & a_, const auto & b_) { return a_ cmp b_; }, [&] { return b; }, #a, #cmp, #b) #define MPT_TEST_EXPECT_THROWS_ANY(expr) mpt::test::test{mpt_test_context}.expect_throws_any([&] { expr; }, #expr) #define MPT_TEST_EXPECT_THROWS(exception, expr) mpt::test::test{mpt_test_context}.expect_throws([&] { expr; }, #exception, #expr) #define MPT_TEST_EXPECT_EQUAL(a, b) mpt::test::test{mpt_test_context}.expect([&] { return a; }, std::equal_to<>{}, [&] { return b; }, #a, "==", #b) } // namespace test } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_TEST_TEST_MACROS_HPP libopenmpt-0.6.1+release.autotools/src/mpt/uuid/0000755000175000017500000000000014175541574016706 500000000000000libopenmpt-0.6.1+release.autotools/src/mpt/uuid/tests/0000755000175000017500000000000014175541575020051 500000000000000libopenmpt-0.6.1+release.autotools/src/mpt/uuid/tests/tests_uuid.hpp0000644000175000017500000001010214044173026022647 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_BASE_TESTS_UUID_HPP #define MPT_BASE_TESTS_UUID_HPP #include "mpt/base/detect.hpp" #include "mpt/base/namespace.hpp" #include "mpt/random/default_engines.hpp" #include "mpt/random/device.hpp" #include "mpt/string/types.hpp" #include "mpt/test/test.hpp" #include "mpt/test/test_macros.hpp" #include "mpt/uuid/guid.hpp" #include "mpt/uuid/uuid.hpp" #include #include namespace mpt { inline namespace MPT_INLINE_NS { namespace tests { namespace uuid { #if MPT_COMPILER_CLANG #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wglobal-constructors" #endif MPT_TEST_GROUP_INLINE("mpt/uuid") #if MPT_COMPILER_CLANG #pragma clang diagnostic pop #endif { using namespace mpt::uuid_literals; MPT_TEST_EXPECT_EQUAL(mpt::UUID(0x2ed6593au, 0xdfe6, 0x4cf8, 0xb2e575ad7f600c32ull).ToUString(), MPT_USTRING("2ed6593a-dfe6-4cf8-b2e5-75ad7f600c32")); #if MPT_OS_WINDOWS constexpr mpt::UUID uuid_tmp = "2ed6593a-dfe6-4cf8-b2e5-75ad7f600c32"_uuid; MPT_TEST_EXPECT_EQUAL(mpt::UUID(0x2ed6593au, 0xdfe6, 0x4cf8, 0xb2e575ad7f600c32ull), uuid_tmp); MPT_TEST_EXPECT_EQUAL(mpt::UUID(0x2ed6593au, 0xdfe6, 0x4cf8, 0xb2e575ad7f600c32ull), mpt::UUID(mpt::StringToGUID(TEXT("{2ed6593a-dfe6-4cf8-b2e5-75ad7f600c32}")))); MPT_TEST_EXPECT_EQUAL(mpt::UUID(0x2ed6593au, 0xdfe6, 0x4cf8, 0xb2e575ad7f600c32ull), mpt::UUID(mpt::StringToCLSID(TEXT("{2ed6593a-dfe6-4cf8-b2e5-75ad7f600c32}")))); MPT_TEST_EXPECT_EQUAL(mpt::UUID(0x00112233u, 0x4455, 0x6677, 0x8899AABBCCDDEEFFull), mpt::UUID(mpt::StringToGUID(TEXT("{00112233-4455-6677-8899-AABBCCDDEEFF}")))); MPT_TEST_EXPECT_EQUAL(mpt::UUID(0x00112233u, 0x4455, 0x6677, 0xC899AABBCCDDEEFFull), mpt::UUID(mpt::StringToGUID(TEXT("{00112233-4455-6677-C899-AABBCCDDEEFF}")))); MPT_TEST_EXPECT_EQUAL(mpt::GUIDToString(mpt::UUID(0x00112233u, 0x4455, 0x6677, 0x8899AABBCCDDEEFFull)), TEXT("{00112233-4455-6677-8899-AABBCCDDEEFF}")); MPT_TEST_EXPECT_EQUAL(mpt::GUIDToString(mpt::UUID(0x00112233u, 0x4455, 0x6677, 0xC899AABBCCDDEEFFull)), TEXT("{00112233-4455-6677-C899-AABBCCDDEEFF}")); #endif // MPT_OS_WINDOWS mpt::sane_random_device rd; mpt::good_engine prng = mpt::make_prng(rd); #if MPT_OS_WINDOWS MPT_TEST_EXPECT_EQUAL(mpt::IsValid(mpt::CreateGUID()), true); { mpt::UUID uuid = mpt::UUID::Generate(prng); MPT_TEST_EXPECT_EQUAL(uuid, mpt::UUID::FromString(mpt::UUID(uuid).ToUString())); MPT_TEST_EXPECT_EQUAL(uuid, mpt::UUID(mpt::StringToGUID(mpt::GUIDToString(uuid)))); MPT_TEST_EXPECT_EQUAL(uuid, mpt::UUID(mpt::StringToIID(mpt::IIDToString(uuid)))); MPT_TEST_EXPECT_EQUAL(uuid, mpt::UUID(mpt::StringToCLSID(mpt::CLSIDToString(uuid)))); } { GUID guid = mpt::UUID::Generate(prng); MPT_TEST_EXPECT_EQUAL(IsEqualGUID(guid, static_cast(mpt::UUID::FromString(mpt::UUID(guid).ToUString()))), TRUE); MPT_TEST_EXPECT_EQUAL(IsEqualGUID(guid, mpt::StringToGUID(mpt::GUIDToString(guid))), TRUE); MPT_TEST_EXPECT_EQUAL(IsEqualGUID(guid, mpt::StringToIID(mpt::IIDToString(guid))), TRUE); MPT_TEST_EXPECT_EQUAL(IsEqualGUID(guid, mpt::StringToCLSID(mpt::CLSIDToString(guid))), TRUE); } #endif // MPT_OS_WINDOWS MPT_TEST_EXPECT_EQUAL(mpt::UUID::Generate(prng).IsValid(), true); MPT_TEST_EXPECT_EQUAL(mpt::UUID::GenerateLocalUseOnly(prng).IsValid(), true); MPT_TEST_EXPECT_EQUAL(mpt::UUID::Generate(prng) != mpt::UUID::Generate(prng), true); mpt::UUID a = mpt::UUID::Generate(prng); MPT_TEST_EXPECT_EQUAL(a, mpt::UUID::FromString(a.ToUString())); std::byte uuiddata[16]{}; for (std::size_t i = 0; i < 16; ++i) { uuiddata[i] = mpt::byte_cast(static_cast(i)); } static_assert(sizeof(mpt::UUID) == 16); mpt::UUIDbin uuid2; std::memcpy(&uuid2, uuiddata, 16); MPT_TEST_EXPECT_EQUAL(mpt::UUID(uuid2).ToUString(), MPT_USTRING("00010203-0405-0607-0809-0a0b0c0d0e0f")); constexpr mpt::UUID uuid3 = "2ed6593a-dfe6-4cf8-b2e5-75ad7f600c32"_uuid; MPT_TEST_EXPECT_EQUAL(mpt::UUID(0x2ed6593au, 0xdfe6, 0x4cf8, 0xb2e575ad7f600c32ull), uuid3); } } // namespace uuid } // namespace tests } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_BASE_TESTS_UUID_HPP libopenmpt-0.6.1+release.autotools/src/mpt/uuid/guid.hpp0000644000175000017500000001415314107230757020264 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_UUID_GUID_HPP #define MPT_UUID_GUID_HPP #include "mpt/base/detect.hpp" #include "mpt/base/namespace.hpp" #include "mpt/out_of_memory/out_of_memory.hpp" #include "mpt/string/types.hpp" #include "mpt/string_transcode/transcode.hpp" #include "mpt/uuid/uuid.hpp" #include #include #if MPT_OS_WINDOWS #if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0600) #include #endif // _WIN32_WINNT #include #include #endif // MPT_OS_WINDOWS namespace mpt { inline namespace MPT_INLINE_NS { #if MPT_OS_WINDOWS // COM CLSID<->string conversion // A CLSID string is not necessarily a standard UUID string, // it might also be a symbolic name for the interface. // (see CLSIDFromString ( http://msdn.microsoft.com/en-us/library/windows/desktop/ms680589%28v=vs.85%29.aspx )) inline mpt::winstring CLSIDToString(CLSID clsid) { std::wstring str; LPOLESTR tmp = nullptr; switch (::StringFromCLSID(clsid, &tmp)) { case S_OK: break; case E_OUTOFMEMORY: if (tmp) { ::CoTaskMemFree(tmp); tmp = nullptr; } mpt::throw_out_of_memory(); break; default: if (tmp) { ::CoTaskMemFree(tmp); tmp = nullptr; } throw std::logic_error("StringFromCLSID() failed."); break; } if (!tmp) { throw std::logic_error("StringFromCLSID() failed."); } try { str = tmp; } catch (mpt::out_of_memory e) { ::CoTaskMemFree(tmp); tmp = nullptr; mpt::rethrow_out_of_memory(e); } ::CoTaskMemFree(tmp); tmp = nullptr; return mpt::transcode(str); } inline CLSID StringToCLSID(const mpt::winstring & str_) { const std::wstring str = mpt::transcode(str_); CLSID clsid = CLSID(); std::vector tmp(str.c_str(), str.c_str() + str.length() + 1); switch (::CLSIDFromString(tmp.data(), &clsid)) { case NOERROR: // nothing break; case E_INVALIDARG: clsid = CLSID(); break; case CO_E_CLASSSTRING: clsid = CLSID(); break; case REGDB_E_CLASSNOTREG: clsid = CLSID(); break; case REGDB_E_READREGDB: clsid = CLSID(); throw std::runtime_error("CLSIDFromString() failed: REGDB_E_READREGDB."); break; default: clsid = CLSID(); throw std::logic_error("CLSIDFromString() failed."); break; } return clsid; } inline bool VerifyStringToCLSID(const mpt::winstring & str_, CLSID & clsid) { const std::wstring str = mpt::transcode(str_); bool result = false; clsid = CLSID(); std::vector tmp(str.c_str(), str.c_str() + str.length() + 1); switch (::CLSIDFromString(tmp.data(), &clsid)) { case NOERROR: result = true; break; case E_INVALIDARG: result = false; break; case CO_E_CLASSSTRING: result = false; break; case REGDB_E_CLASSNOTREG: result = false; break; case REGDB_E_READREGDB: throw std::runtime_error("CLSIDFromString() failed: REGDB_E_READREGDB."); break; default: throw std::logic_error("CLSIDFromString() failed."); break; } return result; } inline bool IsCLSID(const mpt::winstring & str_) { const std::wstring str = mpt::transcode(str_); bool result = false; CLSID clsid = CLSID(); std::vector tmp(str.c_str(), str.c_str() + str.length() + 1); switch (::CLSIDFromString(tmp.data(), &clsid)) { case NOERROR: result = true; break; case E_INVALIDARG: result = false; break; case CO_E_CLASSSTRING: result = false; break; case REGDB_E_CLASSNOTREG: result = false; break; case REGDB_E_READREGDB: result = false; throw std::runtime_error("CLSIDFromString() failed: REGDB_E_READREGDB."); break; default: result = false; throw std::logic_error("CLSIDFromString() failed."); break; } return result; } // COM IID<->string conversion inline IID StringToIID(const mpt::winstring & str_) { const std::wstring str = mpt::transcode(str_); IID iid = IID(); std::vector tmp(str.c_str(), str.c_str() + str.length() + 1); switch (::IIDFromString(tmp.data(), &iid)) { case S_OK: // nothing break; case E_OUTOFMEMORY: iid = IID(); mpt::throw_out_of_memory(); break; case E_INVALIDARG: iid = IID(); break; default: iid = IID(); throw std::logic_error("IIDFromString() failed."); break; } return iid; } inline mpt::winstring IIDToString(IID iid) { std::wstring str; LPOLESTR tmp = nullptr; switch (::StringFromIID(iid, &tmp)) { case S_OK: break; case E_OUTOFMEMORY: if (tmp) { ::CoTaskMemFree(tmp); tmp = nullptr; } mpt::throw_out_of_memory(); break; default: if (tmp) { ::CoTaskMemFree(tmp); tmp = nullptr; } throw std::logic_error("StringFromIID() failed."); break; } if (!tmp) { throw std::logic_error("StringFromIID() failed."); } try { str = tmp; } catch (mpt::out_of_memory e) { ::CoTaskMemFree(tmp); tmp = nullptr; mpt::rethrow_out_of_memory(e); } return mpt::transcode(str); } // General GUID<->string conversion. // The string must/will be in standard GUID format: {4F9A455D-E7EF-4367-B2F0-0C83A38A5C72} inline GUID StringToGUID(const mpt::winstring & str) { return StringToIID(str); } inline mpt::winstring GUIDToString(GUID guid) { std::vector tmp(256); if (::StringFromGUID2(guid, tmp.data(), static_cast(tmp.size())) <= 0) { throw std::logic_error("StringFromGUID2() failed."); } return mpt::transcode(tmp.data()); } // Create a COM GUID inline GUID CreateGUID() { GUID guid = GUID(); switch (::CoCreateGuid(&guid)) { case S_OK: // nothing break; default: guid = GUID(); throw std::runtime_error("CoCreateGuid() failed."); } return guid; } // Checks the UUID against the NULL UUID. Returns false if it is NULL, true otherwise. inline bool IsValid(::UUID uuid) { return false || uuid.Data1 != 0 || uuid.Data2 != 0 || uuid.Data3 != 0 || uuid.Data4[0] != 0 || uuid.Data4[1] != 0 || uuid.Data4[2] != 0 || uuid.Data4[3] != 0 || uuid.Data4[4] != 0 || uuid.Data4[5] != 0 || uuid.Data4[6] != 0 || uuid.Data4[7] != 0; } #endif // MPT_OS_WINDOWS } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_UUID_GUID_HPP libopenmpt-0.6.1+release.autotools/src/mpt/uuid/uuid.hpp0000644000175000017500000003146614107230757020310 00000000000000/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_UUID_UUID_HPP #define MPT_UUID_UUID_HPP #include "mpt/base/constexpr_throw.hpp" #include "mpt/base/macros.hpp" #include "mpt/base/integer.hpp" #include "mpt/base/memory.hpp" #include "mpt/base/namespace.hpp" #include "mpt/endian/integer.hpp" #include "mpt/format/default_formatter.hpp" #include "mpt/format/simple.hpp" #include "mpt/parse/parse.hpp" #include "mpt/random/random.hpp" #include "mpt/string/types.hpp" #include "mpt/string/utility.hpp" #if MPT_OS_WINDOWS #if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0600) #include #endif // _WIN32_WINNT #include #include #endif // MPT_OS_WINDOWS namespace mpt { inline namespace MPT_INLINE_NS { // Microsoft on-disk layout struct GUIDms { uint32le Data1; uint16le Data2; uint16le Data3; uint64be Data4; // yes, big endian here }; constexpr bool declare_binary_safe(const GUIDms &) { return true; } static_assert(mpt::check_binary_size(16)); // RFC binary format struct UUIDbin { uint32be Data1; uint16be Data2; uint16be Data3; uint64be Data4; }; constexpr bool declare_binary_safe(const UUIDbin &) { return true; } static_assert(mpt::check_binary_size(16)); struct UUID { private: uint32 Data1; uint16 Data2; uint16 Data3; uint64 Data4; public: MPT_CONSTEXPRINLINE uint32 GetData1() const noexcept { return Data1; } MPT_CONSTEXPRINLINE uint16 GetData2() const noexcept { return Data2; } MPT_CONSTEXPRINLINE uint16 GetData3() const noexcept { return Data3; } MPT_CONSTEXPRINLINE uint64 GetData4() const noexcept { return Data4; } public: MPT_CONSTEXPRINLINE uint64 GetData64_1() const noexcept { return (static_cast(Data1) << 32) | (static_cast(Data2) << 16) | (static_cast(Data3) << 0); } MPT_CONSTEXPRINLINE uint64 GetData64_2() const noexcept { return Data4; } public: // xxxxxxxx-xxxx-Mmxx-Nnxx-xxxxxxxxxxxx // <--32-->-<16>-<16>-<-------64------> MPT_CONSTEXPRINLINE bool IsNil() const noexcept { return (Data1 == 0) && (Data2 == 0) && (Data3 == 0) && (Data4 == 0); } MPT_CONSTEXPRINLINE bool IsValid() const noexcept { return (Data1 != 0) || (Data2 != 0) || (Data3 != 0) || (Data4 != 0); } MPT_CONSTEXPRINLINE uint8 Variant() const noexcept { return Nn() >> 4u; } MPT_CONSTEXPRINLINE uint8 Version() const noexcept { return Mm() >> 4u; } MPT_CONSTEXPRINLINE bool IsRFC4122() const noexcept { return (Variant() & 0xcu) == 0x8u; } private: MPT_CONSTEXPRINLINE uint8 Mm() const noexcept { return static_cast((Data3 >> 8) & 0xffu); } MPT_CONSTEXPRINLINE uint8 Nn() const noexcept { return static_cast((Data4 >> 56) & 0xffu); } void MakeRFC4122(uint8 version) noexcept { // variant uint8 Nn = static_cast((Data4 >> 56) & 0xffu); Data4 &= 0x00ffffffffffffffull; Nn &= ~(0xc0u); Nn |= 0x80u; Data4 |= static_cast(Nn) << 56; // version version &= 0x0fu; uint8 Mm = static_cast((Data3 >> 8) & 0xffu); Data3 &= 0x00ffu; Mm &= ~(0xf0u); Mm |= (version << 4u); Data3 |= static_cast(Mm) << 8; } #if MPT_OS_WINDOWS private: static mpt::UUID UUIDFromWin32(::UUID uuid) { return mpt::UUID(uuid.Data1, uuid.Data2, uuid.Data3, (static_cast(0) | (static_cast(uuid.Data4[0]) << 56) | (static_cast(uuid.Data4[1]) << 48) | (static_cast(uuid.Data4[2]) << 40) | (static_cast(uuid.Data4[3]) << 32) | (static_cast(uuid.Data4[4]) << 24) | (static_cast(uuid.Data4[5]) << 16) | (static_cast(uuid.Data4[6]) << 8) | (static_cast(uuid.Data4[7]) << 0))); } static ::UUID UUIDToWin32(mpt::UUID uuid) { ::UUID result = ::UUID(); result.Data1 = uuid.GetData1(); result.Data2 = uuid.GetData2(); result.Data3 = uuid.GetData3(); result.Data4[0] = static_cast(uuid.GetData4() >> 56); result.Data4[1] = static_cast(uuid.GetData4() >> 48); result.Data4[2] = static_cast(uuid.GetData4() >> 40); result.Data4[3] = static_cast(uuid.GetData4() >> 32); result.Data4[4] = static_cast(uuid.GetData4() >> 24); result.Data4[5] = static_cast(uuid.GetData4() >> 16); result.Data4[6] = static_cast(uuid.GetData4() >> 8); result.Data4[7] = static_cast(uuid.GetData4() >> 0); return result; } public: explicit UUID(::UUID uuid) { *this = UUIDFromWin32(uuid); } operator ::UUID() const { return UUIDToWin32(*this); } #endif // MPT_OS_WINDOWS private: static MPT_CONSTEXPRINLINE uint8 NibbleFromChar(char x) { return ('0' <= x && x <= '9') ? static_cast(x - '0' + 0) : ('a' <= x && x <= 'z') ? static_cast(x - 'a' + 10) : ('A' <= x && x <= 'Z') ? static_cast(x - 'A' + 10) : mpt::constexpr_throw(std::domain_error("")); } static MPT_CONSTEXPRINLINE uint8 ByteFromHex(char x, char y) { return static_cast(uint8(0) | (NibbleFromChar(x) << 4) | (NibbleFromChar(y) << 0)); } static MPT_CONSTEXPRINLINE uint16 ParseHex16(const char * str) { return static_cast(uint16(0) | (static_cast(ByteFromHex(str[0], str[1])) << 8) | (static_cast(ByteFromHex(str[2], str[3])) << 0)); } static MPT_CONSTEXPRINLINE uint32 ParseHex32(const char * str) { return static_cast(uint32(0) | (static_cast(ByteFromHex(str[0], str[1])) << 24) | (static_cast(ByteFromHex(str[2], str[3])) << 16) | (static_cast(ByteFromHex(str[4], str[5])) << 8) | (static_cast(ByteFromHex(str[6], str[7])) << 0)); } public: static MPT_CONSTEXPRINLINE UUID ParseLiteral(const char * str, std::size_t len) { return (len == 36 && str[8] == '-' && str[13] == '-' && str[18] == '-' && str[23] == '-') ? mpt::UUID( ParseHex32(str + 0), ParseHex16(str + 9), ParseHex16(str + 14), uint64(0) | (static_cast(ParseHex16(str + 19)) << 48) | (static_cast(ParseHex16(str + 24)) << 32) | (static_cast(ParseHex32(str + 28)) << 0)) : mpt::constexpr_throw(std::domain_error("")); } public: MPT_CONSTEXPRINLINE UUID() noexcept : Data1(0) , Data2(0) , Data3(0) , Data4(0) { return; } MPT_CONSTEXPRINLINE explicit UUID(uint32 Data1, uint16 Data2, uint16 Data3, uint64 Data4) noexcept : Data1(Data1) , Data2(Data2) , Data3(Data3) , Data4(Data4) { return; } explicit UUID(UUIDbin uuid) { Data1 = uuid.Data1.get(); Data2 = uuid.Data2.get(); Data3 = uuid.Data3.get(); Data4 = uuid.Data4.get(); } explicit UUID(GUIDms guid) { Data1 = guid.Data1.get(); Data2 = guid.Data2.get(); Data3 = guid.Data3.get(); Data4 = guid.Data4.get(); } operator UUIDbin() const { UUIDbin result{}; result.Data1 = GetData1(); result.Data2 = GetData2(); result.Data3 = GetData3(); result.Data4 = GetData4(); return result; } operator GUIDms() const { GUIDms result{}; result.Data1 = GetData1(); result.Data2 = GetData2(); result.Data3 = GetData3(); result.Data4 = GetData4(); return result; } public: // Create a UUID template static UUID Generate(Trng & rng) { #if MPT_OS_WINDOWS && MPT_OS_WINDOWS_WINRT #if (_WIN32_WINNT >= 0x0602) ::GUID guid = ::GUID(); HRESULT result = CoCreateGuid(&guid); if (result != S_OK) { return mpt::UUID::RFC4122Random(rng); } return mpt::UUID::UUIDFromWin32(guid); #else return mpt::UUID::RFC4122Random(rng); #endif #elif MPT_OS_WINDOWS && !MPT_OS_WINDOWS_WINRT ::UUID uuid = ::UUID(); RPC_STATUS status = ::UuidCreate(&uuid); if (status != RPC_S_OK && status != RPC_S_UUID_LOCAL_ONLY) { return mpt::UUID::RFC4122Random(rng); } status = RPC_S_OK; if (UuidIsNil(&uuid, &status) != FALSE) { return mpt::UUID::RFC4122Random(rng); } if (status != RPC_S_OK) { return mpt::UUID::RFC4122Random(rng); } return mpt::UUID::UUIDFromWin32(uuid); #else return RFC4122Random(rng); #endif } // Create a UUID that contains local, traceable information. // Safe for local use. May be faster. template static UUID GenerateLocalUseOnly(Trng & rng) { #if MPT_OS_WINDOWS && MPT_OS_WINDOWS_WINRT #if (_WIN32_WINNT >= 0x0602) ::GUID guid = ::GUID(); HRESULT result = CoCreateGuid(&guid); if (result != S_OK) { return mpt::UUID::RFC4122Random(rng); } return mpt::UUID::UUIDFromWin32(guid); #else return mpt::UUID::RFC4122Random(rng); #endif #elif MPT_OS_WINDOWS && !MPT_OS_WINDOWS_WINRT #if _WIN32_WINNT >= 0x0501 // Available since Win2000, but we check for WinXP in order to not use this // function in Win32old builds. It is not available on some non-fully // patched Win98SE installs in the wild. ::UUID uuid = ::UUID(); RPC_STATUS status = ::UuidCreateSequential(&uuid); if (status != RPC_S_OK && status != RPC_S_UUID_LOCAL_ONLY) { return Generate(rng); } status = RPC_S_OK; if (UuidIsNil(&uuid, &status) != FALSE) { return mpt::UUID::RFC4122Random(rng); } if (status != RPC_S_OK) { return mpt::UUID::RFC4122Random(rng); } return mpt::UUID::UUIDFromWin32(uuid); #else // Fallback to ::UuidCreate is safe as ::UuidCreateSequential is only a // tiny performance optimization. return Generate(rng); #endif #else return RFC4122Random(rng); #endif } // Create a RFC4122 Random UUID. template static UUID RFC4122Random(Trng & prng) { UUID result; result.Data1 = mpt::random(prng); result.Data2 = mpt::random(prng); result.Data3 = mpt::random(prng); result.Data4 = mpt::random(prng); result.MakeRFC4122(4); return result; } friend UUID UUIDRFC4122NamespaceV3(const UUID & ns, const mpt::ustring & name); friend UUID UUIDRFC4122NamespaceV5(const UUID & ns, const mpt::ustring & name); public: // General UUID<->string conversion. // The string must/will be in standard UUID format: 4f9a455d-e7ef-4367-b2f0-0c83a38a5c72 static UUID FromString(const mpt::ustring & str) { std::vector segments = mpt::split(str, MPT_ULITERAL("-")); if (segments.size() != 5) { return UUID(); } if (segments[0].length() != 8) { return UUID(); } if (segments[1].length() != 4) { return UUID(); } if (segments[2].length() != 4) { return UUID(); } if (segments[3].length() != 4) { return UUID(); } if (segments[4].length() != 12) { return UUID(); } UUID result; result.Data1 = mpt::ConvertHexStringTo(segments[0]); result.Data2 = mpt::ConvertHexStringTo(segments[1]); result.Data3 = mpt::ConvertHexStringTo(segments[2]); result.Data4 = mpt::ConvertHexStringTo(segments[3] + segments[4]); return result; } std::string ToAString() const { return std::string() + mpt::format::hex0<8>(GetData1()) + std::string("-") + mpt::format::hex0<4>(GetData2()) + std::string("-") + mpt::format::hex0<4>(GetData3()) + std::string("-") + mpt::format::hex0<4>(static_cast(GetData4() >> 48)) + std::string("-") + mpt::format::hex0<4>(static_cast(GetData4() >> 32)) + mpt::format::hex0<8>(static_cast(GetData4() >> 0)); } mpt::ustring ToUString() const { return mpt::ustring() + mpt::format::hex0<8>(GetData1()) + MPT_USTRING("-") + mpt::format::hex0<4>(GetData2()) + MPT_USTRING("-") + mpt::format::hex0<4>(GetData3()) + MPT_USTRING("-") + mpt::format::hex0<4>(static_cast(GetData4() >> 48)) + MPT_USTRING("-") + mpt::format::hex0<4>(static_cast(GetData4() >> 32)) + mpt::format::hex0<8>(static_cast(GetData4() >> 0)); } }; MPT_CONSTEXPRINLINE bool operator==(const mpt::UUID & a, const mpt::UUID & b) noexcept { return (a.GetData1() == b.GetData1()) && (a.GetData2() == b.GetData2()) && (a.GetData3() == b.GetData3()) && (a.GetData4() == b.GetData4()); } MPT_CONSTEXPRINLINE bool operator!=(const mpt::UUID & a, const mpt::UUID & b) noexcept { return (a.GetData1() != b.GetData1()) || (a.GetData2() != b.GetData2()) || (a.GetData3() != b.GetData3()) || (a.GetData4() != b.GetData4()); } namespace uuid_literals { MPT_CONSTEXPRINLINE mpt::UUID operator"" _uuid(const char * str, std::size_t len) { return mpt::UUID::ParseLiteral(str, len); } } // namespace uuid_literals template inline Tstring uuid_to_string(mpt::UUID uuid) { return mpt::transcode(uuid.ToUString()); } template <> inline std::string uuid_to_string(mpt::UUID uuid) { return uuid.ToAString(); } template ::value, bool> = true> inline Tstring format_value_default(const T & x) { return mpt::transcode(mpt::uuid_to_string::type>(x)); } } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_UUID_UUID_HPP libopenmpt-0.6.1+release.autotools/src/mpt/.clang-format0000644000175000017500000000727714044173026020234 00000000000000Language: Cpp Standard: c++17 AccessModifierOffset: -4 #? AlignAfterOpenBracket: AlwaysBreak AlignConsecutiveAssignments: false AlignConsecutiveBitFields: false AlignConsecutiveDeclarations: false AlignConsecutiveMacros: true AlignEscapedNewlines: DontAlign AlignOperands: DontAlign AlignTrailingComments: true AllowAllArgumentsOnNextLine: true AllowAllConstructorInitializersOnNextLine: true AllowAllParametersOfDeclarationOnNextLine: true AllowShortBlocksOnASingleLine: Never AllowShortCaseLabelsOnASingleLine: false AllowShortEnumsOnASingleLine: false AllowShortFunctionsOnASingleLine: Empty AllowShortIfStatementsOnASingleLine: false AllowShortLambdasOnASingleLine: Inline AllowShortLoopsOnASingleLine: false AlwaysBreakAfterReturnType: None AlwaysBreakBeforeMultilineStrings: true AlwaysBreakTemplateDeclarations: Yes BinPackArguments: true BinPackParameters: false BraceWrapping: AfterCaseLabel: false AfterClass: false AfterControlStatement: MultiLine AfterEnum: false AfterFunction: false AfterNamespace: false #AfterObjCDeclaration AfterStruct: false AfterUnion: false AfterExternBlock: false BeforeCatch: false BeforeElse: false BeforeLambdaBody: false BeforeWhile: false IndentBraces: false SplitEmptyFunction: true SplitEmptyRecord: false SplitEmptyNamespace: true #BreakAfterJavaFieldAnnotations BreakBeforeBinaryOperators: NonAssignment BreakBeforeBraces: Custom BreakBeforeTernaryOperators: true BreakConstructorInitializers: BeforeComma BreakInheritanceList: BeforeComma BreakStringLiterals: false ColumnLimit: 0 CommentPragmas: '' #? CompactNamespaces: false ConstructorInitializerAllOnOneLineOrOnePerLine: true ConstructorInitializerIndentWidth: 4 #? ContinuationIndentWidth: 4 #? Cpp11BracedListStyle: true DeriveLineEnding: true DerivePointerAlignment: false FixNamespaceComments: true ForEachMacros: [] IncludeBlocks: Preserve IncludeCategories: [] #? IncludeIsMainRegex: '' #? IncludeIsMainSourceRegex: '' #? IndentCaseLabels: true IndentCaseBlocks: true IndentExternBlock: NoIndent IndentGotoLabels: false IndentPPDirectives: None InsertTrailingCommas: None #BeforeHash IndentWidth: 4 IndentWrappedFunctionNames: true #JavaImportGroups #JavaScriptQuotes #JavaScriptWrapImports KeepEmptyLinesAtTheStartOfBlocks: true MacroBlockBegin: '^MPT_TEST_GROUP_BEGIN$' #? MacroBlockEnd: '^MPT_TEST_GROUP_END$' #? MaxEmptyLinesToKeep: 5 NamespaceIndentation: None NamespaceMacros: [] #? #ObjCBinPackProtocolList #ObjCBlockIndentWidth #ObjCBreakBeforeNestedBlockParam #ObjCSpaceAfterProperty #ObjCSpaceBeforeProtocolList #PenaltyBreakAssignment #PenaltyBreakBeforeFirstCallParameter #PenaltyBreakComment #PenaltyBreakFirstLessLess #PenaltyBreakString #PenaltyBreakTemplateDeclaration #PenaltyExcessCharacter #PenaltyReturnTypeOnItsOwnLine PointerAlignment: Middle #RawStringFormats ReflowComments: false SortIncludes: false SortUsingDeclarations: true SpaceAfterCStyleCast: false SpaceAfterLogicalNot: false SpaceAfterTemplateKeyword: true SpaceBeforeAssignmentOperators: true SpaceBeforeCpp11BracedList: false SpaceBeforeCtorInitializerColon: true SpaceBeforeInheritanceColon: true SpaceBeforeParens: ControlStatements SpaceBeforeRangeBasedForLoopColon: true SpaceBeforeSquareBrackets: false SpaceInEmptyBlock: true SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 1 SpacesInAngles: false SpacesInCStyleCastParentheses: false SpacesInConditionalStatement: false SpacesInContainerLiterals: true SpacesInParentheses: false SpacesInSquareBrackets: false StatementMacros: [ '_Pragma', '__pragma', 'MPT_WARNING', 'MPT_TEST_GROUP_INLINE_IDENTIFIER', 'MPT_TEST_GROUP_INLINE', 'MPT_TEST_GROUP_STATIC' ] #? TabWidth: 4 TypenameMacros: [] #? UseCRLF: false UseTab: ForContinuationAndIndentation WhitespaceSensitiveMacros: - MPT_PP_STRINGIFY libopenmpt-0.6.1+release.autotools/src/mpt/LICENSE.BSD-3-Clause.txt0000644000175000017500000000307414164006754021462 00000000000000Copyright (c) 2004-2022, OpenMPT Project Developers and Contributors Copyright (c) 1997-2003, Olivier Lapicque All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the OpenMPT project nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. libopenmpt-0.6.1+release.autotools/src/mpt/LICENSE.BSL-1.0.txt0000644000175000017500000000247214044173026020407 00000000000000Boost Software License - Version 1.0 - August 17th, 2003 Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. libopenmpt-0.6.1+release.autotools/src/openmpt/0000755000175000017500000000000014175541574016622 500000000000000libopenmpt-0.6.1+release.autotools/src/openmpt/all/0000755000175000017500000000000014175541575017373 500000000000000libopenmpt-0.6.1+release.autotools/src/openmpt/all/BuildSettings.hpp0000644000175000017500000000152114052666553022601 00000000000000/* SPDX-License-Identifier: BSD-3-Clause */ /* SPDX-FileCopyrightText: OpenMPT Project Developers and Contributors */ #pragma once #include "mpt/base/detect_compiler.hpp" #include "mpt/base/detect_os.hpp" #include "mpt/base/detect_quirks.hpp" #if defined(MODPLUG_TRACKER) || defined(LIBOPENMPT_BUILD) #include "BuildSettings.h" #else #include "mpt/base/namespace.hpp" #ifndef OPENMPT_NAMESPACE #define OPENMPT_NAMESPACE OpenMPT #endif #ifndef OPENMPT_NAMESPACE_BEGIN #define OPENMPT_NAMESPACE_BEGIN \ namespace OPENMPT_NAMESPACE \ { \ inline namespace MPT_INLINE_NS \ { #endif #ifndef OPENMPT_NAMESPACE_END #define OPENMPT_NAMESPACE_END \ } \ } #endif #ifdef __cplusplus OPENMPT_NAMESPACE_BEGIN namespace mpt { #ifndef MPT_NO_NAMESPACE using namespace ::mpt; #endif } // namespace mpt OPENMPT_NAMESPACE_END #endif #endif libopenmpt-0.6.1+release.autotools/src/openmpt/base/0000755000175000017500000000000014175541575017535 500000000000000libopenmpt-0.6.1+release.autotools/src/openmpt/base/Endian.hpp0000644000175000017500000000471014053114024021342 00000000000000/* SPDX-License-Identifier: BSD-3-Clause */ /* SPDX-FileCopyrightText: OpenMPT Project Developers and Contributors */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "mpt/base/bit.hpp" #include "mpt/base/memory.hpp" #include "mpt/endian/floatingpoint.hpp" #include "mpt/endian/integer.hpp" #include "openmpt/base/Types.hpp" OPENMPT_NAMESPACE_BEGIN using int64le = mpt::packed; using int32le = mpt::packed; using int16le = mpt::packed; using int8le = mpt::packed; using uint64le = mpt::packed; using uint32le = mpt::packed; using uint16le = mpt::packed; using uint8le = mpt::packed; using int64be = mpt::packed; using int32be = mpt::packed; using int16be = mpt::packed; using int8be = mpt::packed; using uint64be = mpt::packed; using uint32be = mpt::packed; using uint16be = mpt::packed; using uint8be = mpt::packed; using IEEE754binary32LE = mpt::IEEE754binary_types::is_ieee754_binary32ne, mpt::endian::native>::IEEE754binary32LE; using IEEE754binary32BE = mpt::IEEE754binary_types::is_ieee754_binary32ne, mpt::endian::native>::IEEE754binary32BE; using IEEE754binary64LE = mpt::IEEE754binary_types::is_ieee754_binary64ne, mpt::endian::native>::IEEE754binary64LE; using IEEE754binary64BE = mpt::IEEE754binary_types::is_ieee754_binary64ne, mpt::endian::native>::IEEE754binary64BE; // unaligned using float32le = mpt::IEEE754binary32EmulatedLE; using float32be = mpt::IEEE754binary32EmulatedBE; using float64le = mpt::IEEE754binary64EmulatedLE; using float64be = mpt::IEEE754binary64EmulatedBE; // potentially aligned using float32le_fast = mpt::IEEE754binary32LE; using float32be_fast = mpt::IEEE754binary32BE; using float64le_fast = mpt::IEEE754binary64LE; using float64be_fast = mpt::IEEE754binary64BE; #define MPT_BINARY_STRUCT(type, size) \ constexpr bool declare_binary_safe(const type &) { return true; } \ static_assert(mpt::check_binary_size(size)); \ /**/ OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/src/openmpt/base/FlagSet.hpp0000644000175000017500000004362514053120664021510 00000000000000/* SPDX-License-Identifier: BSD-3-Clause */ /* SPDX-FileCopyrightText: OpenMPT Project Developers and Contributors */ /* * Originally based on . * Rewritten to be standard-conforming. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "mpt/base/macros.hpp" #include #include OPENMPT_NAMESPACE_BEGIN // Type-safe wrapper around an enum, that can represent all enum values and bitwise compositions thereof. // Conversions to and from plain integers as well as conversions to the base enum are always explicit. template // cppcheck-suppress copyCtorAndEqOperator class enum_value_type { public: using enum_type = enum_t; using value_type = enum_value_type; using store_type = typename std::make_unsigned::type; private: store_type bits; public: MPT_CONSTEXPRINLINE enum_value_type() noexcept : bits(0) {} MPT_CONSTEXPRINLINE enum_value_type(const enum_value_type &x) noexcept : bits(x.bits) {} MPT_CONSTEXPRINLINE enum_value_type(enum_type x) noexcept : bits(static_cast(x)) {} private: explicit MPT_CONSTEXPRINLINE enum_value_type(store_type x) noexcept : bits(x) {} // private in order to prevent accidental conversions. use from_bits. MPT_CONSTEXPRINLINE operator store_type() const noexcept { return bits; } // private in order to prevent accidental conversions. use as_bits. public: static MPT_CONSTEXPRINLINE enum_value_type from_bits(store_type bits) noexcept { return value_type(bits); } MPT_CONSTEXPRINLINE enum_type as_enum() const noexcept { return static_cast(bits); } MPT_CONSTEXPRINLINE store_type as_bits() const noexcept { return bits; } public: MPT_CONSTEXPRINLINE operator bool() const noexcept { return bits != store_type(); } MPT_CONSTEXPRINLINE bool operator!() const noexcept { return bits == store_type(); } MPT_CONSTEXPRINLINE const enum_value_type operator~() const noexcept { return enum_value_type(~bits); } friend MPT_CONSTEXPRINLINE bool operator==(enum_value_type a, enum_value_type b) noexcept { return a.bits == b.bits; } friend MPT_CONSTEXPRINLINE bool operator!=(enum_value_type a, enum_value_type b) noexcept { return a.bits != b.bits; } friend MPT_CONSTEXPRINLINE bool operator==(enum_value_type a, enum_t b) noexcept { return a == enum_value_type(b); } friend MPT_CONSTEXPRINLINE bool operator!=(enum_value_type a, enum_t b) noexcept { return a != enum_value_type(b); } friend MPT_CONSTEXPRINLINE bool operator==(enum_t a, enum_value_type b) noexcept { return enum_value_type(a) == b; } friend MPT_CONSTEXPRINLINE bool operator!=(enum_t a, enum_value_type b) noexcept { return enum_value_type(a) != b; } friend MPT_CONSTEXPRINLINE const enum_value_type operator|(enum_value_type a, enum_value_type b) noexcept { return enum_value_type(a.bits | b.bits); } friend MPT_CONSTEXPRINLINE const enum_value_type operator&(enum_value_type a, enum_value_type b) noexcept { return enum_value_type(a.bits & b.bits); } friend MPT_CONSTEXPRINLINE const enum_value_type operator^(enum_value_type a, enum_value_type b) noexcept { return enum_value_type(a.bits ^ b.bits); } friend MPT_CONSTEXPRINLINE const enum_value_type operator|(enum_value_type a, enum_t b) noexcept { return a | enum_value_type(b); } friend MPT_CONSTEXPRINLINE const enum_value_type operator&(enum_value_type a, enum_t b) noexcept { return a & enum_value_type(b); } friend MPT_CONSTEXPRINLINE const enum_value_type operator^(enum_value_type a, enum_t b) noexcept { return a ^ enum_value_type(b); } friend MPT_CONSTEXPRINLINE const enum_value_type operator|(enum_t a, enum_value_type b) noexcept { return enum_value_type(a) | b; } friend MPT_CONSTEXPRINLINE const enum_value_type operator&(enum_t a, enum_value_type b) noexcept { return enum_value_type(a) & b; } friend MPT_CONSTEXPRINLINE const enum_value_type operator^(enum_t a, enum_value_type b) noexcept { return enum_value_type(a) ^ b; } MPT_CONSTEXPRINLINE enum_value_type &operator|=(enum_value_type b) noexcept { *this = *this | b; return *this; } MPT_CONSTEXPRINLINE enum_value_type &operator&=(enum_value_type b) noexcept { *this = *this & b; return *this; } MPT_CONSTEXPRINLINE enum_value_type &operator^=(enum_value_type b) noexcept { *this = *this ^ b; return *this; } MPT_CONSTEXPRINLINE enum_value_type &operator|=(enum_t b) noexcept { *this = *this | b; return *this; } MPT_CONSTEXPRINLINE enum_value_type &operator&=(enum_t b) noexcept { *this = *this & b; return *this; } MPT_CONSTEXPRINLINE enum_value_type &operator^=(enum_t b) noexcept { *this = *this ^ b; return *this; } }; // Type-safe enum wrapper that allows type-safe bitwise testing. template class Enum { public: using self_type = Enum; using enum_type = enum_t; using value_type = enum_value_type; using store_type = typename value_type::store_type; private: enum_type value; public: explicit MPT_CONSTEXPRINLINE Enum(enum_type val) noexcept : value(val) {} MPT_CONSTEXPRINLINE operator enum_type() const noexcept { return value; } MPT_CONSTEXPRINLINE Enum &operator=(enum_type val) noexcept { value = val; return *this; } public: MPT_CONSTEXPRINLINE const value_type operator~() const { return ~value_type(value); } friend MPT_CONSTEXPRINLINE bool operator==(self_type a, self_type b) noexcept { return value_type(a) == value_type(b); } friend MPT_CONSTEXPRINLINE bool operator!=(self_type a, self_type b) noexcept { return value_type(a) != value_type(b); } friend MPT_CONSTEXPRINLINE bool operator==(self_type a, value_type b) noexcept { return value_type(a) == value_type(b); } friend MPT_CONSTEXPRINLINE bool operator!=(self_type a, value_type b) noexcept { return value_type(a) != value_type(b); } friend MPT_CONSTEXPRINLINE bool operator==(value_type a, self_type b) noexcept { return value_type(a) == value_type(b); } friend MPT_CONSTEXPRINLINE bool operator!=(value_type a, self_type b) noexcept { return value_type(a) != value_type(b); } friend MPT_CONSTEXPRINLINE bool operator==(self_type a, enum_type b) noexcept { return value_type(a) == value_type(b); } friend MPT_CONSTEXPRINLINE bool operator!=(self_type a, enum_type b) noexcept { return value_type(a) != value_type(b); } friend MPT_CONSTEXPRINLINE bool operator==(enum_type a, self_type b) noexcept { return value_type(a) == value_type(b); } friend MPT_CONSTEXPRINLINE bool operator!=(enum_type a, self_type b) noexcept { return value_type(a) != value_type(b); } friend MPT_CONSTEXPRINLINE const value_type operator|(self_type a, self_type b) noexcept { return value_type(a) | value_type(b); } friend MPT_CONSTEXPRINLINE const value_type operator&(self_type a, self_type b) noexcept { return value_type(a) & value_type(b); } friend MPT_CONSTEXPRINLINE const value_type operator^(self_type a, self_type b) noexcept { return value_type(a) ^ value_type(b); } friend MPT_CONSTEXPRINLINE const value_type operator|(self_type a, value_type b) noexcept { return value_type(a) | value_type(b); } friend MPT_CONSTEXPRINLINE const value_type operator&(self_type a, value_type b) noexcept { return value_type(a) & value_type(b); } friend MPT_CONSTEXPRINLINE const value_type operator^(self_type a, value_type b) noexcept { return value_type(a) ^ value_type(b); } friend MPT_CONSTEXPRINLINE const value_type operator|(value_type a, self_type b) noexcept { return value_type(a) | value_type(b); } friend MPT_CONSTEXPRINLINE const value_type operator&(value_type a, self_type b) noexcept { return value_type(a) & value_type(b); } friend MPT_CONSTEXPRINLINE const value_type operator^(value_type a, self_type b) noexcept { return value_type(a) ^ value_type(b); } friend MPT_CONSTEXPRINLINE const value_type operator|(self_type a, enum_type b) noexcept { return value_type(a) | value_type(b); } friend MPT_CONSTEXPRINLINE const value_type operator&(self_type a, enum_type b) noexcept { return value_type(a) & value_type(b); } friend MPT_CONSTEXPRINLINE const value_type operator^(self_type a, enum_type b) noexcept { return value_type(a) ^ value_type(b); } friend MPT_CONSTEXPRINLINE const value_type operator|(enum_type a, self_type b) noexcept { return value_type(a) | value_type(b); } friend MPT_CONSTEXPRINLINE const value_type operator&(enum_type a, self_type b) noexcept { return value_type(a) & value_type(b); } friend MPT_CONSTEXPRINLINE const value_type operator^(enum_type a, self_type b) noexcept { return value_type(a) ^ value_type(b); } }; template ::store_type> // cppcheck-suppress copyCtorAndEqOperator class FlagSet { public: using self_type = FlagSet; using enum_type = enum_t; using value_type = enum_value_type; using store_type = store_t; private: // support truncated store_type ... : store_type bits_; static MPT_CONSTEXPRINLINE store_type store_from_value(value_type bits) noexcept { return static_cast(bits.as_bits()); } static MPT_CONSTEXPRINLINE value_type value_from_store(store_type bits) noexcept { return value_type::from_bits(static_cast(bits)); } MPT_CONSTEXPRINLINE FlagSet &store(value_type bits) noexcept { bits_ = store_from_value(bits); return *this; } MPT_CONSTEXPRINLINE value_type load() const noexcept { return value_from_store(bits_); } public: // Default constructor (no flags set) MPT_CONSTEXPRINLINE FlagSet() noexcept : bits_(store_from_value(value_type())) { } // Copy constructor MPT_CONSTEXPRINLINE FlagSet(const FlagSet &flags) noexcept : bits_(flags.bits_) { } // Value constructor MPT_CONSTEXPRINLINE FlagSet(value_type flags) noexcept : bits_(store_from_value(value_type(flags))) { } MPT_CONSTEXPRINLINE FlagSet(enum_type flag) noexcept : bits_(store_from_value(value_type(flag))) { } explicit MPT_CONSTEXPRINLINE FlagSet(store_type flags) noexcept : bits_(store_from_value(value_type::from_bits(flags))) { } MPT_CONSTEXPRINLINE explicit operator bool() const noexcept { return load(); } MPT_CONSTEXPRINLINE explicit operator store_type() const noexcept { return load().as_bits(); } MPT_CONSTEXPRINLINE value_type value() const noexcept { return load(); } MPT_CONSTEXPRINLINE operator value_type() const noexcept { return load(); } // Test if one or more flags are set. Returns true if at least one of the given flags is set. MPT_CONSTEXPRINLINE bool operator[](value_type flags) const noexcept { return test(flags); } // Set one or more flags. MPT_CONSTEXPRINLINE FlagSet &set(value_type flags) noexcept { return store(load() | flags); } // Set or clear one or more flags. MPT_CONSTEXPRINLINE FlagSet &set(value_type flags, bool val) noexcept { return store((val ? (load() | flags) : (load() & ~flags))); } // Clear or flags. MPT_CONSTEXPRINLINE FlagSet &reset() noexcept { return store(value_type()); } // Clear one or more flags. MPT_CONSTEXPRINLINE FlagSet &reset(value_type flags) noexcept { return store(load() & ~flags); } // Toggle all flags. MPT_CONSTEXPRINLINE FlagSet &flip() noexcept { return store(~load()); } // Toggle one or more flags. MPT_CONSTEXPRINLINE FlagSet &flip(value_type flags) noexcept { return store(load() ^ flags); } // Returns the size of the flag set in bytes MPT_CONSTEXPRINLINE std::size_t size() const noexcept { return sizeof(store_type); } // Returns the size of the flag set in bits MPT_CONSTEXPRINLINE std::size_t size_bits() const noexcept { return size() * 8; } // Test if one or more flags are set. Returns true if at least one of the given flags is set. MPT_CONSTEXPRINLINE bool test(value_type flags) const noexcept { return (load() & flags); } // Test if all specified flags are set. MPT_CONSTEXPRINLINE bool test_all(value_type flags) const noexcept { return (load() & flags) == flags; } // Test if any but the specified flags are set. MPT_CONSTEXPRINLINE bool test_any_except(value_type flags) const noexcept { return (load() & ~flags); } // Test if any flag is set. MPT_CONSTEXPRINLINE bool any() const noexcept { return load(); } // Test if no flags are set. MPT_CONSTEXPRINLINE bool none() const noexcept { return !load(); } MPT_CONSTEXPRINLINE store_type GetRaw() const noexcept { return bits_; } MPT_CONSTEXPRINLINE FlagSet &SetRaw(store_type flags) noexcept { bits_ = flags; return *this; } MPT_CONSTEXPRINLINE FlagSet &operator=(value_type flags) noexcept { return store(flags); } MPT_CONSTEXPRINLINE FlagSet &operator=(enum_type flag) noexcept { return store(flag); } MPT_CONSTEXPRINLINE FlagSet &operator=(FlagSet flags) noexcept { return store(flags.load()); } MPT_CONSTEXPRINLINE FlagSet &operator&=(value_type flags) noexcept { return store(load() & flags); } MPT_CONSTEXPRINLINE FlagSet &operator|=(value_type flags) noexcept { return store(load() | flags); } MPT_CONSTEXPRINLINE FlagSet &operator^=(value_type flags) noexcept { return store(load() ^ flags); } friend MPT_CONSTEXPRINLINE bool operator==(self_type a, self_type b) noexcept { return a.load() == b.load(); } friend MPT_CONSTEXPRINLINE bool operator!=(self_type a, self_type b) noexcept { return a.load() != b.load(); } friend MPT_CONSTEXPRINLINE bool operator==(self_type a, value_type b) noexcept { return a.load() == value_type(b); } friend MPT_CONSTEXPRINLINE bool operator!=(self_type a, value_type b) noexcept { return a.load() != value_type(b); } friend MPT_CONSTEXPRINLINE bool operator==(value_type a, self_type b) noexcept { return value_type(a) == b.load(); } friend MPT_CONSTEXPRINLINE bool operator!=(value_type a, self_type b) noexcept { return value_type(a) != b.load(); } friend MPT_CONSTEXPRINLINE bool operator==(self_type a, enum_type b) noexcept { return a.load() == value_type(b); } friend MPT_CONSTEXPRINLINE bool operator!=(self_type a, enum_type b) noexcept { return a.load() != value_type(b); } friend MPT_CONSTEXPRINLINE bool operator==(enum_type a, self_type b) noexcept { return value_type(a) == b.load(); } friend MPT_CONSTEXPRINLINE bool operator!=(enum_type a, self_type b) noexcept { return value_type(a) != b.load(); } friend MPT_CONSTEXPRINLINE bool operator==(self_type a, Enum b) noexcept { return a.load() == value_type(b); } friend MPT_CONSTEXPRINLINE bool operator!=(self_type a, Enum b) noexcept { return a.load() != value_type(b); } friend MPT_CONSTEXPRINLINE bool operator==(Enum a, self_type b) noexcept { return value_type(a) == b.load(); } friend MPT_CONSTEXPRINLINE bool operator!=(Enum a, self_type b) noexcept { return value_type(a) != b.load(); } friend MPT_CONSTEXPRINLINE const value_type operator|(self_type a, self_type b) noexcept { return a.load() | b.load(); } friend MPT_CONSTEXPRINLINE const value_type operator&(self_type a, self_type b) noexcept { return a.load() & b.load(); } friend MPT_CONSTEXPRINLINE const value_type operator^(self_type a, self_type b) noexcept { return a.load() ^ b.load(); } friend MPT_CONSTEXPRINLINE const value_type operator|(self_type a, value_type b) noexcept { return a.load() | value_type(b); } friend MPT_CONSTEXPRINLINE const value_type operator&(self_type a, value_type b) noexcept { return a.load() & value_type(b); } friend MPT_CONSTEXPRINLINE const value_type operator^(self_type a, value_type b) noexcept { return a.load() ^ value_type(b); } friend MPT_CONSTEXPRINLINE const value_type operator|(value_type a, self_type b) noexcept { return value_type(a) | b.load(); } friend MPT_CONSTEXPRINLINE const value_type operator&(value_type a, self_type b) noexcept { return value_type(a) & b.load(); } friend MPT_CONSTEXPRINLINE const value_type operator^(value_type a, self_type b) noexcept { return value_type(a) ^ b.load(); } friend MPT_CONSTEXPRINLINE const value_type operator|(self_type a, enum_type b) noexcept { return a.load() | value_type(b); } friend MPT_CONSTEXPRINLINE const value_type operator&(self_type a, enum_type b) noexcept { return a.load() & value_type(b); } friend MPT_CONSTEXPRINLINE const value_type operator^(self_type a, enum_type b) noexcept { return a.load() ^ value_type(b); } friend MPT_CONSTEXPRINLINE const value_type operator|(enum_type a, self_type b) noexcept { return value_type(a) | b.load(); } friend MPT_CONSTEXPRINLINE const value_type operator&(enum_type a, self_type b) noexcept { return value_type(a) & b.load(); } friend MPT_CONSTEXPRINLINE const value_type operator^(enum_type a, self_type b) noexcept { return value_type(a) ^ b.load(); } friend MPT_CONSTEXPRINLINE const value_type operator|(self_type a, Enum b) noexcept { return a.load() | value_type(b); } friend MPT_CONSTEXPRINLINE const value_type operator&(self_type a, Enum b) noexcept { return a.load() & value_type(b); } friend MPT_CONSTEXPRINLINE const value_type operator^(self_type a, Enum b) noexcept { return a.load() ^ value_type(b); } friend MPT_CONSTEXPRINLINE const value_type operator|(Enum a, self_type b) noexcept { return value_type(a) | b.load(); } friend MPT_CONSTEXPRINLINE const value_type operator&(Enum a, self_type b) noexcept { return value_type(a) & b.load(); } friend MPT_CONSTEXPRINLINE const value_type operator^(Enum a, self_type b) noexcept { return value_type(a) ^ b.load(); } }; // Declare typesafe logical operators for enum_t #define MPT_DECLARE_ENUM(enum_t) \ MPT_CONSTEXPRINLINE enum_value_type operator|(enum_t a, enum_t b) noexcept { return enum_value_type(a) | enum_value_type(b); } \ MPT_CONSTEXPRINLINE enum_value_type operator&(enum_t a, enum_t b) noexcept { return enum_value_type(a) & enum_value_type(b); } \ MPT_CONSTEXPRINLINE enum_value_type operator^(enum_t a, enum_t b) noexcept { return enum_value_type(a) ^ enum_value_type(b); } \ MPT_CONSTEXPRINLINE enum_value_type operator~(enum_t a) noexcept { return ~enum_value_type(a); } \ /**/ // backwards compatibility #define DECLARE_FLAGSET MPT_DECLARE_ENUM OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/src/openmpt/base/Int24.hpp0000644000175000017500000000132714053010430021041 00000000000000/* SPDX-License-Identifier: BSD-3-Clause */ /* SPDX-FileCopyrightText: OpenMPT Project Developers and Contributors */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "mpt/endian/int24.hpp" #include "openmpt/base/Types.hpp" #include OPENMPT_NAMESPACE_BEGIN using uint24 = mpt::uint24; static_assert(sizeof(uint24) == 3); inline constexpr uint32 uint24_min = std::numeric_limits::min(); inline constexpr uint32 uint24_max = std::numeric_limits::max(); using int24 = mpt::int24; static_assert(sizeof(int24) == 3); inline constexpr int32 int24_min = std::numeric_limits::min(); inline constexpr int32 int24_max = std::numeric_limits::max(); OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/src/openmpt/base/Types.hpp0000644000175000017500000000121114052666553021263 00000000000000/* SPDX-License-Identifier: BSD-3-Clause */ /* SPDX-FileCopyrightText: OpenMPT Project Developers and Contributors */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "mpt/base/integer.hpp" #include "mpt/base/floatingpoint.hpp" OPENMPT_NAMESPACE_BEGIN using int8 = mpt::int8; using int16 = mpt::int16; using int32 = mpt::int32; using int64 = mpt::int64; using uint8 = mpt::uint8; using uint16 = mpt::uint16; using uint32 = mpt::uint32; using uint64 = mpt::uint64; using nativefloat = mpt::nativefloat; using float32 = mpt::float32; using float64 = mpt::float64; using namespace mpt::float_literals; OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/src/openmpt/logging/0000755000175000017500000000000014175541575020251 500000000000000libopenmpt-0.6.1+release.autotools/src/openmpt/logging/Logger.hpp0000644000175000017500000000205214052666553022116 00000000000000/* SPDX-License-Identifier: BSD-3-Clause */ /* SPDX-FileCopyrightText: OpenMPT Project Developers and Contributors */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "mpt/base/source_location.hpp" #include "mpt/string/types.hpp" OPENMPT_NAMESPACE_BEGIN enum LogLevel { LogDebug = 5, LogInformation = 4, LogNotification = 3, LogWarning = 2, LogError = 1 }; class ILogger { protected: virtual ~ILogger() = default; public: virtual bool IsLevelActive(LogLevel level) const noexcept = 0; // facility: ASCII virtual bool IsFacilityActive(const char *facility) const noexcept = 0; // facility: ASCII virtual void SendLogMessage(const mpt::source_location &loc, LogLevel level, const char *facility, const mpt::ustring &text) const = 0; }; #define MPT_LOG(logger, level, facility, text) \ do \ { \ if((logger).IsLevelActive((level))) \ { \ if((logger).IsFacilityActive((facility))) \ { \ (logger).SendLogMessage(MPT_SOURCE_LOCATION_CURRENT(), (level), (facility), (text)); \ } \ } \ } while(0) OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/src/openmpt/random/0000755000175000017500000000000014175541575020103 500000000000000libopenmpt-0.6.1+release.autotools/src/openmpt/random/ModPlug.hpp0000644000175000017500000000335314053011010022052 00000000000000/* SPDX-License-Identifier: BSD-3-Clause */ /* SPDX-FileCopyrightText: Olivier Lapicque */ /* SPDX-FileCopyrightText: OpenMPT Project Developers and Contributors */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "mpt/base/bit.hpp" #include "mpt/random/random.hpp" #include "openmpt/base/Types.hpp" #include #include OPENMPT_NAMESPACE_BEGIN namespace mpt { namespace rng { template class modplug { public: typedef Tstate state_type; typedef Tvalue result_type; private: state_type state1; state_type state2; public: template explicit inline modplug(Trng &rd) : state1(mpt::random(rd)) , state2(mpt::random(rd)) { } explicit inline modplug(state_type seed1, state_type seed2) : state1(seed1) , state2(seed2) { } public: static MPT_CONSTEXPRINLINE result_type min() { return static_cast(0); } static MPT_CONSTEXPRINLINE result_type max() { return std::numeric_limits::max(); } static MPT_CONSTEXPRINLINE int result_bits() { static_assert(std::is_integral::value); static_assert(std::is_unsigned::value); return std::numeric_limits::digits; } inline result_type operator()() { state_type a = state1; state_type b = state2; a = mpt::rotl(a, rol1); a ^= x1; a += x2 + (b * x3); b += mpt::rotl(a, rol2) * x4; state1 = a; state2 = b; result_type result = static_cast(b); return result; } }; typedef modplug modplug_dither; } // namespace rng } // namespace mpt OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/src/openmpt/soundbase/0000755000175000017500000000000014175541575020606 500000000000000libopenmpt-0.6.1+release.autotools/src/openmpt/soundbase/Copy.hpp0000644000175000017500000000500714053010430022123 00000000000000/* SPDX-License-Identifier: BSD-3-Clause */ /* SPDX-FileCopyrightText: OpenMPT Project Developers and Contributors */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "mpt/audio/span.hpp" #include "mpt/base/macros.hpp" #include "openmpt/soundbase/SampleConvert.hpp" #include #include #include OPENMPT_NAMESPACE_BEGIN template void CopyAudio(TBufOut buf_out, TBufIn buf_in) { assert(buf_in.size_frames() == buf_out.size_frames()); assert(buf_in.size_channels() == buf_out.size_channels()); std::size_t countFrames = std::min(buf_in.size_frames(), buf_out.size_frames()); std::size_t channels = std::min(buf_in.size_channels(), buf_out.size_channels()); for(std::size_t frame = 0; frame < countFrames; ++frame) { for(std::size_t channel = 0; channel < channels; ++channel) { buf_out(channel, frame) = SC::sample_cast(buf_in(channel, frame)); } } } template void CopyAudio(TBufOut buf_out, TBufIn buf_in, std::size_t countFrames) { assert(countFrames <= buf_in.size_frames()); assert(countFrames <= buf_out.size_frames()); assert(buf_in.size_channels() == buf_out.size_channels()); std::size_t channels = std::min(buf_in.size_channels(), buf_out.size_channels()); for(std::size_t frame = 0; frame < countFrames; ++frame) { for(std::size_t channel = 0; channel < channels; ++channel) { buf_out(channel, frame) = SC::sample_cast(buf_in(channel, frame)); } } } template void CopyAudioChannels(TBufOut buf_out, TBufIn buf_in, std::size_t channels, std::size_t countFrames) { assert(countFrames <= buf_in.size_frames()); assert(countFrames <= buf_out.size_frames()); assert(channels <= buf_in.size_channels()); assert(channels <= buf_out.size_channels()); for(std::size_t frame = 0; frame < countFrames; ++frame) { for(std::size_t channel = 0; channel < channels; ++channel) { buf_out(channel, frame) = SC::sample_cast(buf_in(channel, frame)); } } } // Copy numChannels interleaved sample streams. template void CopyAudioChannelsInterleaved(Tout *MPT_RESTRICT outBuf, const Tin *MPT_RESTRICT inBuf, std::size_t numChannels, std::size_t countFrames) { CopyAudio(mpt::audio_span_interleaved(outBuf, numChannels, countFrames), mpt::audio_span_interleaved(inBuf, numChannels, countFrames)); } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/src/openmpt/soundbase/CopyMix.hpp0000644000175000017500000001014114053010430022574 00000000000000/* SPDX-License-Identifier: BSD-3-Clause */ /* SPDX-FileCopyrightText: OpenMPT Project Developers and Contributors */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "mpt/audio/span.hpp" #include "mpt/base/macros.hpp" #include "openmpt/soundbase/SampleClip.hpp" #include "openmpt/soundbase/SampleClipFixedPoint.hpp" #include "openmpt/soundbase/SampleConvert.hpp" #include "openmpt/soundbase/SampleConvertFixedPoint.hpp" #include "openmpt/soundbase/SampleFormat.hpp" #include #include #include OPENMPT_NAMESPACE_BEGIN template void ConvertBufferMixInternalFixedToBuffer(TOutBuf outBuf, TInBuf inBuf, Tdither &dither, std::size_t channels, std::size_t count) { using TOutSample = typename std::remove_const::type; using TInSample = typename std::remove_const::type; assert(inBuf.size_channels() >= channels); assert(outBuf.size_channels() >= channels); assert(inBuf.size_frames() >= count); assert(outBuf.size_frames() >= count); constexpr int ditherBits = SampleFormat(SampleFormatTraits::sampleFormat()).IsInt() ? SampleFormat(SampleFormatTraits::sampleFormat()).GetBitsPerSample() : 0; SC::ClipFixed clip; SC::ConvertFixedPoint conv; for(std::size_t i = 0; i < count; ++i) { for(std::size_t channel = 0; channel < channels; ++channel) { outBuf(channel, i) = conv(clip(dither.template process(channel, inBuf(channel, i)))); } } } template void ConvertBufferToBufferMixInternalFixed(TOutBuf outBuf, TInBuf inBuf, std::size_t channels, std::size_t count) { using TOutSample = typename std::remove_const::type; using TInSample = typename std::remove_const::type; assert(inBuf.size_channels() >= channels); assert(outBuf.size_channels() >= channels); assert(inBuf.size_frames() >= count); assert(outBuf.size_frames() >= count); SC::ConvertToFixedPoint conv; for(std::size_t i = 0; i < count; ++i) { for(std::size_t channel = 0; channel < channels; ++channel) { outBuf(channel, i) = conv(inBuf(channel, i)); } } } template void ConvertBufferMixInternalToBuffer(TOutBuf outBuf, TInBuf inBuf, Tdither &dither, std::size_t channels, std::size_t count) { using TOutSample = typename std::remove_const::type; using TInSample = typename std::remove_const::type; assert(inBuf.size_channels() >= channels); assert(outBuf.size_channels() >= channels); assert(inBuf.size_frames() >= count); assert(outBuf.size_frames() >= count); constexpr int ditherBits = SampleFormat(SampleFormatTraits::sampleFormat()).IsInt() ? SampleFormat(SampleFormatTraits::sampleFormat()).GetBitsPerSample() : 0; SC::Clip clip; SC::Convert conv; for(std::size_t i = 0; i < count; ++i) { for(std::size_t channel = 0; channel < channels; ++channel) { outBuf(channel, i) = conv(clip(dither.template process(channel, inBuf(channel, i)))); } } } template void ConvertBufferToBufferMixInternal(TOutBuf outBuf, TInBuf inBuf, std::size_t channels, std::size_t count) { using TOutSample = typename std::remove_const::type; using TInSample = typename std::remove_const::type; assert(inBuf.size_channels() >= channels); assert(outBuf.size_channels() >= channels); assert(inBuf.size_frames() >= count); assert(outBuf.size_frames() >= count); SC::Convert conv; for(std::size_t i = 0; i < count; ++i) { for(std::size_t channel = 0; channel < channels; ++channel) { outBuf(channel, i) = conv(inBuf(channel, i)); } } } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/src/openmpt/soundbase/Dither.hpp0000644000175000017500000000717114053107667022457 00000000000000/* SPDX-License-Identifier: BSD-3-Clause */ /* SPDX-FileCopyrightText: OpenMPT Project Developers and Contributors */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "mpt/base/macros.hpp" #include "mpt/random/default_engines.hpp" #include "mpt/random/engine.hpp" #include "openmpt/soundbase/MixSample.hpp" #include #include #include OPENMPT_NAMESPACE_BEGIN template class MultiChannelDither { private: std::vector DitherChannels; typename Tdither::prng_type prng; public: template MultiChannelDither(Trd &rd, std::size_t channels) : DitherChannels(channels) , prng(Tdither::prng_init(rd)) { return; } void Reset() { for(std::size_t channel = 0; channel < DitherChannels.size(); ++channel) { DitherChannels[channel] = Tdither{}; } } std::size_t GetChannels() const { return DitherChannels.size(); } template MPT_FORCEINLINE MixSampleInt process(std::size_t channel, MixSampleInt sample) { return DitherChannels[channel].template process(sample, prng); } template MPT_FORCEINLINE MixSampleFloat process(std::size_t channel, MixSampleFloat sample) { return DitherChannels[channel].template process(sample, prng); } }; template class Dithers : public DitherNames { public: static constexpr std::size_t NoDither = noDither; static constexpr std::size_t DefaultDither = defaultDither; static constexpr std::size_t DefaultChannels = defaultChannels; private: seeding_random_engine m_PRNG; AvailableDithers m_Dithers; public: template Dithers(Trd &rd, std::size_t mode = defaultDither, std::size_t channels = defaultChannels) : m_PRNG(mpt::make_prng(rd)) , m_Dithers(std::in_place_index, m_PRNG, channels) { SetMode(mode, channels); } AvailableDithers &Variant() { return m_Dithers; } static std::size_t GetNumDithers() { return std::variant_size::value; } static std::size_t GetDefaultDither() { return defaultDither; } static std::size_t GetNoDither() { return noDither; } private: template void set_mode(std::size_t mode, std::size_t channels) { if constexpr(i < std::variant_size::value) { if(mode == i) { m_Dithers.template emplace(m_PRNG, channels); } else { this->template set_mode(mode, channels); } } else { MPT_UNUSED(mode); m_Dithers.template emplace(m_PRNG, channels); } } public: void SetMode(std::size_t mode, std::size_t channels) { if((mode == GetMode()) && (channels == GetChannels())) { std::visit([](auto &dither) { return dither.Reset(); }, m_Dithers); return; } set_mode(mode, channels); } void SetMode(std::size_t mode) { if(mode == GetMode()) { std::visit([](auto &dither) { return dither.Reset(); }, m_Dithers); return; } set_mode(mode, GetChannels()); } void SetChannels(std::size_t channels) { if(channels == GetChannels()) { return; } set_mode(GetMode(), channels); } void Reset() { std::visit([](auto &dither) { return dither.Reset(); }, m_Dithers); } std::size_t GetMode() const { return m_Dithers.index(); } std::size_t GetChannels() const { return std::visit([](auto &dither) { return dither.GetChannels(); }, m_Dithers); } }; OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/src/openmpt/soundbase/DitherModPlug.hpp0000644000175000017500000000271514053010430023723 00000000000000/* SPDX-License-Identifier: BSD-3-Clause */ /* SPDX-FileCopyrightText: Olivier Lapicque */ /* SPDX-FileCopyrightText: OpenMPT Project Developers and Contributors */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "mpt/base/arithmetic_shift.hpp" #include "mpt/base/macros.hpp" #include "mpt/random/random.hpp" #include "openmpt/base/Types.hpp" #include "openmpt/random/ModPlug.hpp" #include "openmpt/soundbase/MixSample.hpp" #include "openmpt/soundbase/MixSampleConvert.hpp" OPENMPT_NAMESPACE_BEGIN struct Dither_ModPlug { public: using prng_type = mpt::rng::modplug_dither; template static prng_type prng_init(Trd &) { return prng_type{0, 0}; } public: template MPT_FORCEINLINE MixSampleInt process(MixSampleInt sample, Trng &rng) { if constexpr(targetbits == 0) { MPT_UNREFERENCED_PARAMETER(rng); return sample; } else if constexpr(targetbits + MixSampleIntTraits::mix_headroom_bits + 1 >= 32) { MPT_UNREFERENCED_PARAMETER(rng); return sample; } else { sample += mpt::rshift_signed(static_cast(mpt::random(rng)), (targetbits + MixSampleIntTraits::mix_headroom_bits + 1)); return sample; } } template MPT_FORCEINLINE MixSampleFloat process(MixSampleFloat sample, Trng &prng) { return mix_sample_cast(process(mix_sample_cast(sample), prng)); } }; OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/src/openmpt/soundbase/DitherNone.hpp0000644000175000017500000000140414053010430023245 00000000000000/* SPDX-License-Identifier: BSD-3-Clause */ /* SPDX-FileCopyrightText: OpenMPT Project Developers and Contributors */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "mpt/base/macros.hpp" #include "openmpt/base/Types.hpp" #include "openmpt/soundbase/MixSample.hpp" OPENMPT_NAMESPACE_BEGIN struct Dither_None { public: using prng_type = struct { }; template static prng_type prng_init(Trd &) { return prng_type{}; } public: template MPT_FORCEINLINE MixSampleInt process(MixSampleInt sample, Trng &) { return sample; } template MPT_FORCEINLINE MixSampleFloat process(MixSampleFloat sample, Trng &) { return sample; } }; OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/src/openmpt/soundbase/DitherSimple.hpp0000644000175000017500000000471014053107667023625 00000000000000/* SPDX-License-Identifier: BSD-3-Clause */ /* SPDX-FileCopyrightText: OpenMPT Project Developers and Contributors */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "mpt/base/macros.hpp" #include "mpt/random/engine.hpp" #include "mpt/random/default_engines.hpp" #include "mpt/random/random.hpp" #include "openmpt/base/Types.hpp" #include "openmpt/soundbase/MixSample.hpp" #include "openmpt/soundbase/MixSampleConvert.hpp" OPENMPT_NAMESPACE_BEGIN template struct Dither_SimpleImpl { public: using prng_type = mpt::fast_engine; template static prng_type prng_init(Trd &rd) { return mpt::make_prng(rd); } private: int32 error = 0; public: template MPT_FORCEINLINE MixSampleInt process(MixSampleInt sample, Trng &prng) { if constexpr(targetbits == 0) { MPT_UNREFERENCED_PARAMETER(prng); return sample; } else { static_assert(sizeof(MixSampleInt) == 4); constexpr int rshift = (32 - targetbits) - MixSampleIntTraits::mix_headroom_bits; if constexpr(rshift <= 1) { MPT_UNREFERENCED_PARAMETER(prng); // nothing to dither return sample; } else { constexpr int rshiftpositive = (rshift > 1) ? rshift : 1; // work-around warnings about negative shift with C++14 compilers constexpr int round_mask = ~((1 << rshiftpositive) - 1); constexpr int round_offset = 1 << (rshiftpositive - 1); constexpr int noise_bits = rshiftpositive + (ditherdepth - 1); constexpr int noise_bias = (1 << (noise_bits - 1)); int32 e = error; unsigned int unoise = 0; if constexpr(triangular) { unoise = (mpt::random(prng, noise_bits) + mpt::random(prng, noise_bits)) >> 1; } else { unoise = mpt::random(prng, noise_bits); } int noise = static_cast(unoise) - noise_bias; // un-bias int val = sample; if constexpr(shaped) { val += (e >> 1); } int rounded = (val + noise + round_offset) & round_mask; e = val - rounded; sample = rounded; error = e; return sample; } } } template MPT_FORCEINLINE MixSampleFloat process(MixSampleFloat sample, Trng &prng) { return mix_sample_cast(process(mix_sample_cast(sample), prng)); } }; using Dither_Simple = Dither_SimpleImpl<>; OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/src/openmpt/soundbase/MixSample.hpp0000644000175000017500000000414614053010430023113 00000000000000/* SPDX-License-Identifier: BSD-3-Clause */ /* SPDX-FileCopyrightText: Olivier Lapicque */ /* SPDX-FileCopyrightText: OpenMPT Project Developers and Contributors */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "mpt/audio/sample.hpp" #include "mpt/base/floatingpoint.hpp" #include #include OPENMPT_NAMESPACE_BEGIN template struct FixedPointSampleTraits { static_assert(std::is_integral::value); static_assert(std::is_signed::value); static_assert((sizeof(Tsample) * 8u) - 1 > MIX_HEADROOM_BITS); static_assert((sizeof(Tsample) * 8u) - 1 > FILTER_HEADROOM_BITS); using sample_type = Tsample; enum class sample_type_strong : sample_type { }; static constexpr int mix_headroom_bits = static_cast(MIX_HEADROOM_BITS); static constexpr int mix_precision_bits = static_cast((sizeof(Tsample) * 8) - MIX_HEADROOM_BITS); // including sign bit static constexpr int mix_fractional_bits = static_cast((sizeof(Tsample) * 8) - 1 - MIX_HEADROOM_BITS); // excluding sign bit static constexpr sample_type mix_clip_max = ((sample_type(1) << mix_fractional_bits) - sample_type(1)); static constexpr sample_type mix_clip_min = -((sample_type(1) << mix_fractional_bits) - sample_type(1)); static constexpr int filter_headroom_bits = static_cast(FILTER_HEADROOM_BITS); static constexpr int filter_precision_bits = static_cast((sizeof(Tsample) * 8) - FILTER_HEADROOM_BITS); // including sign bit static constexpr int filter_fractional_bits = static_cast((sizeof(Tsample) * 8) - 1 - FILTER_HEADROOM_BITS); // excluding sign bit template static constexpr Tfloat mix_scale = static_cast(sample_type(1) << mix_fractional_bits); }; using MixSampleIntTraits = FixedPointSampleTraits; using MixSampleInt = MixSampleIntTraits::sample_type; using MixSampleFloat = mpt::audio_sample_float; using MixSample = std::conditional::is_hard, MixSampleFloat, MixSampleInt>::type; OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/src/openmpt/soundbase/MixSampleConvert.hpp0000644000175000017500000000424414056615252024473 00000000000000/* SPDX-License-Identifier: BSD-3-Clause */ /* SPDX-FileCopyrightText: OpenMPT Project Developers and Contributors */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "mpt/base/macros.hpp" #include "openmpt/soundbase/MixSample.hpp" #include "openmpt/soundbase/SampleConvert.hpp" #include "openmpt/soundbase/SampleConvertFixedPoint.hpp" OPENMPT_NAMESPACE_BEGIN template struct ConvertMixSample; template <> struct ConvertMixSample { MPT_FORCEINLINE MixSampleInt conv(MixSampleInt src) { return src; } }; template <> struct ConvertMixSample { MPT_FORCEINLINE MixSampleFloat conv(MixSampleFloat src) { return src; } }; template struct ConvertMixSample { MPT_FORCEINLINE MixSampleInt conv(Tsrc src) { return SC::ConvertToFixedPoint{}(src); } }; template struct ConvertMixSample { MPT_FORCEINLINE Tdst conv(MixSampleInt src) { return SC::ConvertFixedPoint{}(src); } }; template struct ConvertMixSample { MPT_FORCEINLINE MixSampleFloat conv(Tsrc src) { return SC::Convert{}(src); } }; template struct ConvertMixSample { MPT_FORCEINLINE Tdst conv(MixSampleFloat src) { return SC::Convert{}(src); } }; template <> struct ConvertMixSample { MPT_FORCEINLINE MixSampleInt conv(MixSampleFloat src) { return SC::ConvertToFixedPoint{}(src); } }; template <> struct ConvertMixSample { MPT_FORCEINLINE MixSampleFloat conv(MixSampleInt src) { return SC::ConvertFixedPoint{}(src); } }; template MPT_FORCEINLINE Tdst mix_sample_cast(Tsrc src) { return ConvertMixSample{}.conv(src); } OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/src/openmpt/soundbase/SampleClip.hpp0000644000175000017500000000411514154771621023263 00000000000000/* SPDX-License-Identifier: BSD-3-Clause */ /* SPDX-FileCopyrightText: OpenMPT Project Developers and Contributors */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "mpt/base/macros.hpp" #include "openmpt/base/Int24.hpp" #include "openmpt/base/Types.hpp" OPENMPT_NAMESPACE_BEGIN namespace SC { // SC = _S_ample_C_onversion template struct Clip; template struct Clip { using input_t = uint8; using output_t = uint8; MPT_FORCEINLINE uint8 operator()(uint8 val) { return val; } }; template struct Clip { using input_t = int8; using output_t = int8; MPT_FORCEINLINE int8 operator()(int8 val) { return val; } }; template struct Clip { using input_t = int16; using output_t = int16; MPT_FORCEINLINE int16 operator()(int16 val) { return val; } }; template struct Clip { using input_t = int24; using output_t = int24; MPT_FORCEINLINE int24 operator()(int24 val) { return val; } }; template struct Clip { using input_t = int32; using output_t = int32; MPT_FORCEINLINE int32 operator()(int32 val) { return val; } }; template struct Clip { using input_t = int64; using output_t = int64; MPT_FORCEINLINE int64 operator()(int64 val) { return val; } }; template struct Clip { using input_t = float; using output_t = float; MPT_FORCEINLINE float operator()(float val) { if constexpr(clipOutput) { if(val < -1.0f) val = -1.0f; if(val > 1.0f) val = 1.0f; return val; } else { return val; } } }; template struct Clip { using input_t = double; using output_t = double; MPT_FORCEINLINE double operator()(double val) { if constexpr(clipOutput) { if(val < -1.0) val = -1.0; if(val > 1.0) val = 1.0; return val; } else { return val; } } }; } // namespace SC OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/src/openmpt/soundbase/SampleClipFixedPoint.hpp0000644000175000017500000000160714154771621025260 00000000000000/* SPDX-License-Identifier: BSD-3-Clause */ /* SPDX-FileCopyrightText: OpenMPT Project Developers and Contributors */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "mpt/base/macros.hpp" OPENMPT_NAMESPACE_BEGIN namespace SC { // SC = _S_ample_C_onversion template struct ClipFixed { using input_t = Tfixed; using output_t = Tfixed; MPT_FORCEINLINE Tfixed operator()(Tfixed val) { static_assert(fractionalBits >= 0 && fractionalBits <= sizeof(output_t) * 8 - 1); if constexpr(clipOutput) { constexpr Tfixed clip_max = (Tfixed(1) << fractionalBits) - Tfixed(1); constexpr Tfixed clip_min = Tfixed(0) - (Tfixed(1) << fractionalBits); if(val < clip_min) val = clip_min; if(val > clip_max) val = clip_max; return val; } else { return val; } } }; } // namespace SC OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/src/openmpt/soundbase/SampleConvert.hpp0000644000175000017500000003636614155121631024020 00000000000000/* SPDX-License-Identifier: BSD-3-Clause */ /* SPDX-FileCopyrightText: OpenMPT Project Developers and Contributors */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "mpt/base/arithmetic_shift.hpp" #include "mpt/base/macros.hpp" #include "mpt/base/math.hpp" #include "mpt/base/saturate_cast.hpp" #include "openmpt/base/Int24.hpp" #include "openmpt/base/Types.hpp" #include "openmpt/soundbase/SampleConvert.hpp" #include #include #include #include OPENMPT_NAMESPACE_BEGIN namespace SC { // SC = _S_ample_C_onversion #if MPT_COMPILER_MSVC template MPT_FORCEINLINE Tfloat fastround(Tfloat x) { static_assert(std::is_floating_point::value); return std::floor(x + static_cast(0.5)); } #else template MPT_FORCEINLINE Tfloat fastround(Tfloat x) { static_assert(std::is_floating_point::value); return mpt::round(x); } #endif // Shift input_t down by shift and saturate to output_t. template struct ConvertShift { using input_t = Tsrc; using output_t = Tdst; MPT_FORCEINLINE output_t operator()(input_t val) { return mpt::saturate_cast(mpt::rshift_signed(val, shift)); } }; // Shift input_t up by shift and saturate to output_t. template struct ConvertShiftUp { using input_t = Tsrc; using output_t = Tdst; MPT_FORCEINLINE output_t operator()(input_t val) { return mpt::saturate_cast(mpt::lshift_signed(val, shift)); } }; // Every sample conversion functor has to typedef its input_t and output_t. // The input_t argument is taken by value because we only deal with per-single-sample conversions here. // straight forward type conversions, clamping when converting from floating point. template struct Convert; template struct Convert { using input_t = Tid; using output_t = Tid; MPT_FORCEINLINE output_t operator()(input_t val) { return val; } }; template <> struct Convert { using input_t = int8; using output_t = uint8; MPT_FORCEINLINE output_t operator()(input_t val) { return static_cast(val + 0x80); } }; template <> struct Convert { using input_t = int16; using output_t = uint8; MPT_FORCEINLINE output_t operator()(input_t val) { return static_cast(static_cast(mpt::rshift_signed(val, 8)) + 0x80); } }; template <> struct Convert { using input_t = int24; using output_t = uint8; MPT_FORCEINLINE output_t operator()(input_t val) { return static_cast(static_cast(mpt::rshift_signed(static_cast(val), 16)) + 0x80); } }; template <> struct Convert { using input_t = int32; using output_t = uint8; MPT_FORCEINLINE output_t operator()(input_t val) { return static_cast(static_cast(mpt::rshift_signed(val, 24)) + 0x80); } }; template <> struct Convert { using input_t = int64; using output_t = uint8; MPT_FORCEINLINE output_t operator()(input_t val) { return static_cast(static_cast(mpt::rshift_signed(val, 56)) + 0x80); } }; template <> struct Convert { using input_t = float32; using output_t = uint8; MPT_FORCEINLINE output_t operator()(input_t val) { val = mpt::safe_clamp(val, -1.0f, 1.0f); val *= 128.0f; return static_cast(mpt::saturate_cast(static_cast(SC::fastround(val))) + 0x80); } }; template <> struct Convert { using input_t = double; using output_t = uint8; MPT_FORCEINLINE output_t operator()(input_t val) { val = std::clamp(val, -1.0, 1.0); val *= 128.0; return static_cast(mpt::saturate_cast(static_cast(SC::fastround(val))) + 0x80); } }; template <> struct Convert { using input_t = uint8; using output_t = int8; MPT_FORCEINLINE output_t operator()(input_t val) { return static_cast(static_cast(val) - 0x80); } }; template <> struct Convert { using input_t = int16; using output_t = int8; MPT_FORCEINLINE output_t operator()(input_t val) { return static_cast(mpt::rshift_signed(val, 8)); } }; template <> struct Convert { using input_t = int24; using output_t = int8; MPT_FORCEINLINE output_t operator()(input_t val) { return static_cast(mpt::rshift_signed(static_cast(val), 16)); } }; template <> struct Convert { using input_t = int32; using output_t = int8; MPT_FORCEINLINE output_t operator()(input_t val) { return static_cast(mpt::rshift_signed(val, 24)); } }; template <> struct Convert { using input_t = int64; using output_t = int8; MPT_FORCEINLINE output_t operator()(input_t val) { return static_cast(mpt::rshift_signed(val, 56)); } }; template <> struct Convert { using input_t = float32; using output_t = int8; MPT_FORCEINLINE output_t operator()(input_t val) { val = mpt::safe_clamp(val, -1.0f, 1.0f); val *= 128.0f; return mpt::saturate_cast(static_cast(SC::fastround(val))); } }; template <> struct Convert { using input_t = double; using output_t = int8; MPT_FORCEINLINE output_t operator()(input_t val) { val = std::clamp(val, -1.0, 1.0); val *= 128.0; return mpt::saturate_cast(static_cast(SC::fastround(val))); } }; template <> struct Convert { using input_t = uint8; using output_t = int16; MPT_FORCEINLINE output_t operator()(input_t val) { return static_cast(mpt::lshift_signed(static_cast(val) - 0x80, 8)); } }; template <> struct Convert { using input_t = int8; using output_t = int16; MPT_FORCEINLINE output_t operator()(input_t val) { return static_cast(mpt::lshift_signed(val, 8)); } }; template <> struct Convert { using input_t = int24; using output_t = int16; MPT_FORCEINLINE output_t operator()(input_t val) { return static_cast(mpt::rshift_signed(static_cast(val), 8)); } }; template <> struct Convert { using input_t = int32; using output_t = int16; MPT_FORCEINLINE output_t operator()(input_t val) { return static_cast(mpt::rshift_signed(val, 16)); } }; template <> struct Convert { using input_t = int64; using output_t = int16; MPT_FORCEINLINE output_t operator()(input_t val) { return static_cast(mpt::rshift_signed(val, 48)); } }; template <> struct Convert { using input_t = float32; using output_t = int16; MPT_FORCEINLINE output_t operator()(input_t val) { val = mpt::safe_clamp(val, -1.0f, 1.0f); val *= 32768.0f; return mpt::saturate_cast(static_cast(SC::fastround(val))); } }; template <> struct Convert { using input_t = double; using output_t = int16; MPT_FORCEINLINE output_t operator()(input_t val) { val = std::clamp(val, -1.0, 1.0); val *= 32768.0; return mpt::saturate_cast(static_cast(SC::fastround(val))); } }; template <> struct Convert { using input_t = uint8; using output_t = int24; MPT_FORCEINLINE output_t operator()(input_t val) { return static_cast(mpt::lshift_signed(static_cast(val) - 0x80, 16)); } }; template <> struct Convert { using input_t = int8; using output_t = int24; MPT_FORCEINLINE output_t operator()(input_t val) { return static_cast(mpt::lshift_signed(val, 16)); } }; template <> struct Convert { using input_t = int16; using output_t = int24; MPT_FORCEINLINE output_t operator()(input_t val) { return static_cast(mpt::lshift_signed(val, 8)); } }; template <> struct Convert { using input_t = int32; using output_t = int24; MPT_FORCEINLINE output_t operator()(input_t val) { return static_cast(mpt::rshift_signed(val, 8)); } }; template <> struct Convert { using input_t = int64; using output_t = int24; MPT_FORCEINLINE output_t operator()(input_t val) { return static_cast(mpt::rshift_signed(val, 40)); } }; template <> struct Convert { using input_t = float32; using output_t = int24; MPT_FORCEINLINE output_t operator()(input_t val) { val = mpt::safe_clamp(val, -1.0f, 1.0f); val *= 2147483648.0f; return static_cast(mpt::rshift_signed(mpt::saturate_cast(static_cast(SC::fastround(val))), 8)); } }; template <> struct Convert { using input_t = double; using output_t = int24; MPT_FORCEINLINE output_t operator()(input_t val) { val = std::clamp(val, -1.0, 1.0); val *= 2147483648.0; return static_cast(mpt::rshift_signed(mpt::saturate_cast(static_cast(SC::fastround(val))), 8)); } }; template <> struct Convert { using input_t = uint8; using output_t = int32; MPT_FORCEINLINE output_t operator()(input_t val) { return static_cast(mpt::lshift_signed(static_cast(val) - 0x80, 24)); } }; template <> struct Convert { using input_t = int8; using output_t = int32; MPT_FORCEINLINE output_t operator()(input_t val) { return static_cast(mpt::lshift_signed(val, 24)); } }; template <> struct Convert { using input_t = int16; using output_t = int32; MPT_FORCEINLINE output_t operator()(input_t val) { return static_cast(mpt::lshift_signed(val, 16)); } }; template <> struct Convert { using input_t = int24; using output_t = int32; MPT_FORCEINLINE output_t operator()(input_t val) { return static_cast(mpt::lshift_signed(static_cast(val), 8)); } }; template <> struct Convert { using input_t = int64; using output_t = int32; MPT_FORCEINLINE output_t operator()(input_t val) { return static_cast(mpt::rshift_signed(val, 32)); } }; template <> struct Convert { using input_t = float32; using output_t = int32; MPT_FORCEINLINE output_t operator()(input_t val) { val = mpt::safe_clamp(val, -1.0f, 1.0f); val *= 2147483648.0f; return mpt::saturate_cast(static_cast(SC::fastround(val))); } }; template <> struct Convert { using input_t = double; using output_t = int32; MPT_FORCEINLINE output_t operator()(input_t val) { val = std::clamp(val, -1.0, 1.0); val *= 2147483648.0; return mpt::saturate_cast(static_cast(SC::fastround(val))); } }; template <> struct Convert { using input_t = uint8; using output_t = int64; MPT_FORCEINLINE output_t operator()(input_t val) { return mpt::lshift_signed(static_cast(val) - 0x80, 56); } }; template <> struct Convert { using input_t = int8; using output_t = int64; MPT_FORCEINLINE output_t operator()(input_t val) { return mpt::lshift_signed(static_cast(val), 56); } }; template <> struct Convert { using input_t = int16; using output_t = int64; MPT_FORCEINLINE output_t operator()(input_t val) { return mpt::lshift_signed(static_cast(val), 48); } }; template <> struct Convert { using input_t = int24; using output_t = int64; MPT_FORCEINLINE output_t operator()(input_t val) { return mpt::lshift_signed(static_cast(val), 40); } }; template <> struct Convert { using input_t = int32; using output_t = int64; MPT_FORCEINLINE output_t operator()(input_t val) { return mpt::lshift_signed(static_cast(val), 32); } }; template <> struct Convert { using input_t = float32; using output_t = int64; MPT_FORCEINLINE output_t operator()(input_t val) { val = mpt::safe_clamp(val, -1.0f, 1.0f); val *= static_cast(uint64(1) << 63); return mpt::saturate_cast(SC::fastround(val)); } }; template <> struct Convert { using input_t = double; using output_t = int64; MPT_FORCEINLINE output_t operator()(input_t val) { val = std::clamp(val, -1.0, 1.0); val *= static_cast(uint64(1) << 63); return mpt::saturate_cast(SC::fastround(val)); } }; template <> struct Convert { using input_t = uint8; using output_t = float32; MPT_FORCEINLINE output_t operator()(input_t val) { return (static_cast(val) - 0x80) * (1.0f / static_cast(static_cast(1) << 7)); } }; template <> struct Convert { using input_t = int8; using output_t = float32; MPT_FORCEINLINE output_t operator()(input_t val) { return val * (1.0f / static_cast(static_cast(1) << 7)); } }; template <> struct Convert { using input_t = int16; using output_t = float32; MPT_FORCEINLINE output_t operator()(input_t val) { return val * (1.0f / static_cast(static_cast(1) << 15)); } }; template <> struct Convert { using input_t = int24; using output_t = float32; MPT_FORCEINLINE output_t operator()(input_t val) { return val * (1.0f / static_cast(static_cast(1) << 23)); } }; template <> struct Convert { using input_t = int32; using output_t = float32; MPT_FORCEINLINE output_t operator()(input_t val) { return val * (1.0f / static_cast(static_cast(1) << 31)); } }; template <> struct Convert { using input_t = int64; using output_t = float32; MPT_FORCEINLINE output_t operator()(input_t val) { return val * (1.0f / static_cast(static_cast(1) << 63)); } }; template <> struct Convert { using input_t = uint8; using output_t = double; MPT_FORCEINLINE output_t operator()(input_t val) { return (static_cast(val) - 0x80) * (1.0 / static_cast(static_cast(1) << 7)); } }; template <> struct Convert { using input_t = int8; using output_t = double; MPT_FORCEINLINE output_t operator()(input_t val) { return val * (1.0 / static_cast(static_cast(1) << 7)); } }; template <> struct Convert { using input_t = int16; using output_t = double; MPT_FORCEINLINE output_t operator()(input_t val) { return val * (1.0 / static_cast(static_cast(1) << 15)); } }; template <> struct Convert { using input_t = int24; using output_t = double; MPT_FORCEINLINE output_t operator()(input_t val) { return val * (1.0 / static_cast(static_cast(1) << 23)); } }; template <> struct Convert { using input_t = int32; using output_t = double; MPT_FORCEINLINE output_t operator()(input_t val) { return val * (1.0 / static_cast(static_cast(1) << 31)); } }; template <> struct Convert { using input_t = int64; using output_t = double; MPT_FORCEINLINE output_t operator()(input_t val) { return val * (1.0 / static_cast(static_cast(1) << 63)); } }; template <> struct Convert { using input_t = float; using output_t = double; MPT_FORCEINLINE output_t operator()(input_t val) { return static_cast(val); } }; template <> struct Convert { using input_t = double; using output_t = float; MPT_FORCEINLINE output_t operator()(input_t val) { return static_cast(val); } }; template MPT_FORCEINLINE Tdst sample_cast(Tsrc src) { return SC::Convert{}(src); } } // namespace SC OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/src/openmpt/soundbase/SampleConvertFixedPoint.hpp0000644000175000017500000001764714155121631026013 00000000000000/* SPDX-License-Identifier: BSD-3-Clause */ /* SPDX-FileCopyrightText: OpenMPT Project Developers and Contributors */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "mpt/base/arithmetic_shift.hpp" #include "mpt/base/macros.hpp" #include "mpt/base/math.hpp" #include "mpt/base/saturate_cast.hpp" #include "openmpt/base/Int24.hpp" #include "openmpt/base/Types.hpp" #include "openmpt/soundbase/SampleConvert.hpp" #include #include OPENMPT_NAMESPACE_BEGIN namespace SC { // SC = _S_ample_C_onversion template struct ConvertFixedPoint; template struct ConvertFixedPoint { using input_t = int32; using output_t = uint8; static constexpr int shiftBits = fractionalBits + 1 - sizeof(output_t) * 8; MPT_FORCEINLINE output_t operator()(input_t val) { static_assert(fractionalBits >= 0 && fractionalBits <= sizeof(input_t) * 8 - 1); static_assert(shiftBits >= 1); val = mpt::rshift_signed((val + (1 << (shiftBits - 1))), shiftBits); // round if(val < std::numeric_limits::min()) val = std::numeric_limits::min(); if(val > std::numeric_limits::max()) val = std::numeric_limits::max(); return static_cast(val + 0x80); // unsigned } }; template struct ConvertFixedPoint { using input_t = int32; using output_t = int8; static constexpr int shiftBits = fractionalBits + 1 - sizeof(output_t) * 8; MPT_FORCEINLINE output_t operator()(input_t val) { static_assert(fractionalBits >= 0 && fractionalBits <= sizeof(input_t) * 8 - 1); static_assert(shiftBits >= 1); val = mpt::rshift_signed((val + (1 << (shiftBits - 1))), shiftBits); // round if(val < std::numeric_limits::min()) val = std::numeric_limits::min(); if(val > std::numeric_limits::max()) val = std::numeric_limits::max(); return static_cast(val); } }; template struct ConvertFixedPoint { using input_t = int32; using output_t = int16; static constexpr int shiftBits = fractionalBits + 1 - sizeof(output_t) * 8; MPT_FORCEINLINE output_t operator()(input_t val) { static_assert(fractionalBits >= 0 && fractionalBits <= sizeof(input_t) * 8 - 1); static_assert(shiftBits >= 1); val = mpt::rshift_signed((val + (1 << (shiftBits - 1))), shiftBits); // round if(val < std::numeric_limits::min()) val = std::numeric_limits::min(); if(val > std::numeric_limits::max()) val = std::numeric_limits::max(); return static_cast(val); } }; template struct ConvertFixedPoint { using input_t = int32; using output_t = int24; static constexpr int shiftBits = fractionalBits + 1 - sizeof(output_t) * 8; MPT_FORCEINLINE output_t operator()(input_t val) { static_assert(fractionalBits >= 0 && fractionalBits <= sizeof(input_t) * 8 - 1); static_assert(shiftBits >= 1); val = mpt::rshift_signed((val + (1 << (shiftBits - 1))), shiftBits); // round if(val < std::numeric_limits::min()) val = std::numeric_limits::min(); if(val > std::numeric_limits::max()) val = std::numeric_limits::max(); return static_cast(val); } }; template struct ConvertFixedPoint { using input_t = int32; using output_t = int32; MPT_FORCEINLINE output_t operator()(input_t val) { static_assert(fractionalBits >= 0 && fractionalBits <= sizeof(input_t) * 8 - 1); return static_cast(std::clamp(val, static_cast(-((1 << fractionalBits) - 1)), static_cast(1 << fractionalBits) - 1)) << (sizeof(input_t) * 8 - 1 - fractionalBits); } }; template struct ConvertFixedPoint { using input_t = int32; using output_t = float32; const float factor; MPT_FORCEINLINE ConvertFixedPoint() : factor(1.0f / static_cast(1 << fractionalBits)) { return; } MPT_FORCEINLINE output_t operator()(input_t val) { static_assert(fractionalBits >= 0 && fractionalBits <= sizeof(input_t) * 8 - 1); return val * factor; } }; template struct ConvertFixedPoint { using input_t = int32; using output_t = float64; const double factor; MPT_FORCEINLINE ConvertFixedPoint() : factor(1.0 / static_cast(1 << fractionalBits)) { return; } MPT_FORCEINLINE output_t operator()(input_t val) { static_assert(fractionalBits >= 0 && fractionalBits <= sizeof(input_t) * 8 - 1); return val * factor; } }; template struct ConvertToFixedPoint; template struct ConvertToFixedPoint { using input_t = uint8; using output_t = int32; static constexpr int shiftBits = fractionalBits + 1 - sizeof(input_t) * 8; MPT_FORCEINLINE output_t operator()(input_t val) { static_assert(fractionalBits >= 0 && fractionalBits <= sizeof(output_t) * 8 - 1); static_assert(shiftBits >= 1); return mpt::lshift_signed(static_cast(static_cast(val) - 0x80), shiftBits); } }; template struct ConvertToFixedPoint { using input_t = int8; using output_t = int32; static constexpr int shiftBits = fractionalBits + 1 - sizeof(input_t) * 8; MPT_FORCEINLINE output_t operator()(input_t val) { static_assert(fractionalBits >= 0 && fractionalBits <= sizeof(output_t) * 8 - 1); static_assert(shiftBits >= 1); return mpt::lshift_signed(static_cast(val), shiftBits); } }; template struct ConvertToFixedPoint { using input_t = int16; using output_t = int32; static constexpr int shiftBits = fractionalBits + 1 - sizeof(input_t) * 8; MPT_FORCEINLINE output_t operator()(input_t val) { static_assert(fractionalBits >= 0 && fractionalBits <= sizeof(output_t) * 8 - 1); static_assert(shiftBits >= 1); return mpt::lshift_signed(static_cast(val), shiftBits); } }; template struct ConvertToFixedPoint { using input_t = int24; using output_t = int32; static constexpr int shiftBits = fractionalBits + 1 - sizeof(input_t) * 8; MPT_FORCEINLINE output_t operator()(input_t val) { static_assert(fractionalBits >= 0 && fractionalBits <= sizeof(output_t) * 8 - 1); static_assert(shiftBits >= 1); return mpt::lshift_signed(static_cast(val), shiftBits); } }; template struct ConvertToFixedPoint { using input_t = int32; using output_t = int32; MPT_FORCEINLINE output_t operator()(input_t val) { static_assert(fractionalBits >= 0 && fractionalBits <= sizeof(output_t) * 8 - 1); return mpt::rshift_signed(static_cast(val), (sizeof(input_t) * 8 - 1 - fractionalBits)); } }; template struct ConvertToFixedPoint { using input_t = float32; using output_t = int32; const float factor; MPT_FORCEINLINE ConvertToFixedPoint() : factor(static_cast(1 << fractionalBits)) { return; } MPT_FORCEINLINE output_t operator()(input_t val) { static_assert(fractionalBits >= 0 && fractionalBits <= sizeof(input_t) * 8 - 1); val = mpt::sanitize_nan(val); return mpt::saturate_cast(SC::fastround(val * factor)); } }; template struct ConvertToFixedPoint { using input_t = float64; using output_t = int32; const double factor; MPT_FORCEINLINE ConvertToFixedPoint() : factor(static_cast(1 << fractionalBits)) { return; } MPT_FORCEINLINE output_t operator()(input_t val) { static_assert(fractionalBits >= 0 && fractionalBits <= sizeof(input_t) * 8 - 1); val = mpt::sanitize_nan(val); return mpt::saturate_cast(SC::fastround(val * factor)); } }; } // namespace SC OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/src/openmpt/soundbase/SampleDecode.hpp0000644000175000017500000003014514155121631023550 00000000000000/* SPDX-License-Identifier: BSD-3-Clause */ /* SPDX-FileCopyrightText: OpenMPT Project Developers and Contributors */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "mpt/base/floatingpoint.hpp" #include "mpt/base/macros.hpp" #include "mpt/base/memory.hpp" #include "openmpt/base/Endian.hpp" #include "openmpt/base/Types.hpp" #include #include #include OPENMPT_NAMESPACE_BEGIN // Byte offsets, from lowest significant to highest significant byte (for various functor template parameters) #define littleEndian64 0, 1, 2, 3, 4, 5, 6, 7 #define littleEndian32 0, 1, 2, 3 #define littleEndian24 0, 1, 2 #define littleEndian16 0, 1 #define bigEndian64 7, 6, 5, 4, 3, 2, 1, 0 #define bigEndian32 3, 2, 1, 0 #define bigEndian24 2, 1, 0 #define bigEndian16 1, 0 namespace SC { // SC = _S_ample_C_onversion // Every sample decoding functor has to typedef its input_t and output_t // and has to provide a static constexpr input_inc member // which describes by how many input_t elements inBuf has to be incremented between invocations. // input_inc is normally 1 except when decoding e.g. bigger sample values // from multiple std::byte values. struct DecodeInt8 { using input_t = std::byte; using output_t = int8; static constexpr std::size_t input_inc = 1; MPT_FORCEINLINE output_t operator()(const input_t *inBuf) { return mpt::byte_cast(*inBuf); } }; struct DecodeUint8 { using input_t = std::byte; using output_t = int8; static constexpr std::size_t input_inc = 1; MPT_FORCEINLINE output_t operator()(const input_t *inBuf) { return static_cast(static_cast(mpt::byte_cast(*inBuf)) - 128); } }; struct DecodeInt8Delta { using input_t = std::byte; using output_t = int8; static constexpr std::size_t input_inc = 1; uint8 delta; DecodeInt8Delta() : delta(0) {} MPT_FORCEINLINE output_t operator()(const input_t *inBuf) { delta += mpt::byte_cast(*inBuf); return static_cast(delta); } }; struct DecodeInt16uLaw { using input_t = std::byte; using output_t = int16; static constexpr std::size_t input_inc = 1; // clang-format off static constexpr std::array uLawTable = { -32124,-31100,-30076,-29052,-28028,-27004,-25980,-24956, -23932,-22908,-21884,-20860,-19836,-18812,-17788,-16764, -15996,-15484,-14972,-14460,-13948,-13436,-12924,-12412, -11900,-11388,-10876,-10364, -9852, -9340, -8828, -8316, -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140, -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092, -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004, -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980, -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436, -1372, -1308, -1244, -1180, -1116, -1052, -988, -924, -876, -844, -812, -780, -748, -716, -684, -652, -620, -588, -556, -524, -492, -460, -428, -396, -372, -356, -340, -324, -308, -292, -276, -260, -244, -228, -212, -196, -180, -164, -148, -132, -120, -112, -104, -96, -88, -80, -72, -64, -56, -48, -40, -32, -24, -16, -8, -1, 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956, 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764, 15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412, 11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316, 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140, 5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092, 3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004, 2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980, 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436, 1372, 1308, 1244, 1180, 1116, 1052, 988, 924, 876, 844, 812, 780, 748, 716, 684, 652, 620, 588, 556, 524, 492, 460, 428, 396, 372, 356, 340, 324, 308, 292, 276, 260, 244, 228, 212, 196, 180, 164, 148, 132, 120, 112, 104, 96, 88, 80, 72, 64, 56, 48, 40, 32, 24, 16, 8, 0 }; // clang-format on MPT_FORCEINLINE output_t operator()(const input_t *inBuf) { return uLawTable[mpt::byte_cast(*inBuf)]; } }; struct DecodeInt16ALaw { using input_t = std::byte; using output_t = int16; static constexpr std::size_t input_inc = 1; // clang-format off static constexpr std::array ALawTable = { -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736, -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784, -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368, -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392, -22016,-20992,-24064,-23040,-17920,-16896,-19968,-18944, -30208,-29184,-32256,-31232,-26112,-25088,-28160,-27136, -11008,-10496,-12032,-11520, -8960, -8448, -9984, -9472, -15104,-14592,-16128,-15616,-13056,-12544,-14080,-13568, -344, -328, -376, -360, -280, -264, -312, -296, -472, -456, -504, -488, -408, -392, -440, -424, -88, -72, -120, -104, -24, -8, -56, -40, -216, -200, -248, -232, -152, -136, -184, -168, -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184, -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696, -688, -656, -752, -720, -560, -528, -624, -592, -944, -912, -1008, -976, -816, -784, -880, -848, 5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736, 7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784, 2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368, 3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392, 22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944, 30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136, 11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472, 15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568, 344, 328, 376, 360, 280, 264, 312, 296, 472, 456, 504, 488, 408, 392, 440, 424, 88, 72, 120, 104, 24, 8, 56, 40, 216, 200, 248, 232, 152, 136, 184, 168, 1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184, 1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696, 688, 656, 752, 720, 560, 528, 624, 592, 944, 912, 1008, 976, 816, 784, 880, 848 }; // clang-format on MPT_FORCEINLINE output_t operator()(const input_t *inBuf) { return ALawTable[mpt::byte_cast(*inBuf)]; } }; template struct DecodeInt16 { using input_t = std::byte; using output_t = int16; static constexpr std::size_t input_inc = 2; MPT_FORCEINLINE output_t operator()(const input_t *inBuf) { return (mpt::byte_cast(inBuf[loByteIndex]) | (mpt::byte_cast(inBuf[hiByteIndex]) << 8)) - offset; } }; template struct DecodeInt16Delta { using input_t = std::byte; using output_t = int16; static constexpr std::size_t input_inc = 2; uint16 delta; DecodeInt16Delta() : delta(0) {} MPT_FORCEINLINE output_t operator()(const input_t *inBuf) { delta += mpt::byte_cast(inBuf[loByteIndex]) | (mpt::byte_cast(inBuf[hiByteIndex]) << 8); return static_cast(delta); } }; struct DecodeInt16Delta8 { using input_t = std::byte; using output_t = int16; static constexpr std::size_t input_inc = 2; uint16 delta; DecodeInt16Delta8() : delta(0) {} MPT_FORCEINLINE output_t operator()(const input_t *inBuf) { delta += mpt::byte_cast(inBuf[0]); int16 result = delta & 0xFF; delta += mpt::byte_cast(inBuf[1]); result |= (delta << 8); return result; } }; template struct DecodeInt24 { using input_t = std::byte; using output_t = int32; static constexpr std::size_t input_inc = 3; MPT_FORCEINLINE output_t operator()(const input_t *inBuf) { return ((mpt::byte_cast(inBuf[loByteIndex]) << 8) | (mpt::byte_cast(inBuf[midByteIndex]) << 16) | (mpt::byte_cast(inBuf[hiByteIndex]) << 24)) - offset; } }; template struct DecodeInt32 { using input_t = std::byte; using output_t = int32; static constexpr std::size_t input_inc = 4; MPT_FORCEINLINE output_t operator()(const input_t *inBuf) { return (mpt::byte_cast(inBuf[loLoByteIndex]) | (mpt::byte_cast(inBuf[loHiByteIndex]) << 8) | (mpt::byte_cast(inBuf[hiLoByteIndex]) << 16) | (mpt::byte_cast(inBuf[hiHiByteIndex]) << 24)) - offset; } }; template struct DecodeInt64 { using input_t = std::byte; using output_t = int64; static constexpr std::size_t input_inc = 8; MPT_FORCEINLINE output_t operator()(const input_t *inBuf) { return (uint64(0) | (static_cast(mpt::byte_cast(inBuf[b0])) << 0) | (static_cast(mpt::byte_cast(inBuf[b1])) << 8) | (static_cast(mpt::byte_cast(inBuf[b2])) << 16) | (static_cast(mpt::byte_cast(inBuf[b3])) << 24) | (static_cast(mpt::byte_cast(inBuf[b4])) << 32) | (static_cast(mpt::byte_cast(inBuf[b5])) << 40) | (static_cast(mpt::byte_cast(inBuf[b6])) << 48) | (static_cast(mpt::byte_cast(inBuf[b7])) << 56)) - offset; } }; template struct DecodeFloat32 { using input_t = std::byte; using output_t = float32; static constexpr std::size_t input_inc = 4; MPT_FORCEINLINE output_t operator()(const input_t *inBuf) { float32 val = IEEE754binary32LE(inBuf[loLoByteIndex], inBuf[loHiByteIndex], inBuf[hiLoByteIndex], inBuf[hiHiByteIndex]); val = mpt::sanitize_nan(val); if(std::isinf(val)) { if(val >= 0.0f) { val = 1.0f; } else { val = -1.0f; } } return val; } }; template struct DecodeScaledFloat32 { using input_t = std::byte; using output_t = float32; static constexpr std::size_t input_inc = 4; float factor; MPT_FORCEINLINE output_t operator()(const input_t *inBuf) { float32 val = IEEE754binary32LE(inBuf[loLoByteIndex], inBuf[loHiByteIndex], inBuf[hiLoByteIndex], inBuf[hiHiByteIndex]); val = mpt::sanitize_nan(val); if(std::isinf(val)) { if(val >= 0.0f) { val = 1.0f; } else { val = -1.0f; } } return factor * val; } MPT_FORCEINLINE DecodeScaledFloat32(float scaleFactor) : factor(scaleFactor) { return; } }; template struct DecodeFloat64 { using input_t = std::byte; using output_t = float64; static constexpr std::size_t input_inc = 8; MPT_FORCEINLINE output_t operator()(const input_t *inBuf) { float64 val = IEEE754binary64LE(inBuf[b0], inBuf[b1], inBuf[b2], inBuf[b3], inBuf[b4], inBuf[b5], inBuf[b6], inBuf[b7]); val = mpt::sanitize_nan(val); if(std::isinf(val)) { if(val >= 0.0) { val = 1.0; } else { val = -1.0; } } return val; } }; template struct DecodeIdentity { using input_t = Tsample; using output_t = Tsample; static constexpr std::size_t input_inc = 1; MPT_FORCEINLINE output_t operator()(const input_t *inBuf) { return *inBuf; } }; // Reads sample data with Func and passes it directly to Func2. // Func1::output_t and Func2::input_t must be identical template struct ConversionChain { using input_t = typename Func1::input_t; using output_t = typename Func2::output_t; static constexpr std::size_t input_inc = Func1::input_inc; Func1 func1; Func2 func2; MPT_FORCEINLINE output_t operator()(const input_t *inBuf) { return func2(func1(inBuf)); } MPT_FORCEINLINE ConversionChain(Func2 f2 = Func2(), Func1 f1 = Func1()) : func1(f1) , func2(f2) { return; } }; } // namespace SC OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/src/openmpt/soundbase/SampleEncode.hpp0000644000175000017500000000375714154771621023604 00000000000000/* SPDX-License-Identifier: BSD-3-Clause */ /* SPDX-FileCopyrightText: OpenMPT Project Developers and Contributors */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "mpt/base/bit.hpp" #include "mpt/base/macros.hpp" #include "mpt/base/memory.hpp" #include "openmpt/base/Types.hpp" #include #include #include #include OPENMPT_NAMESPACE_BEGIN namespace SC { // SC = _S_ample_C_onversion struct EncodeuLaw { using input_t = int16; using output_t = std::byte; static constexpr uint8 exp_table[17] = {0, 7 << 4, 6 << 4, 5 << 4, 4 << 4, 3 << 4, 2 << 4, 1 << 4, 0 << 4, 0, 0, 0, 0, 0, 0, 0, 0}; static constexpr uint8 mant_table[17] = {0, 10, 9, 8, 7, 6, 5, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3}; MPT_FORCEINLINE output_t operator()(input_t val) { uint16 x = static_cast(val); uint8 out = (x >> 8) & 0x80; uint32 abs = x & 0x7fff; if(x & 0x8000) { abs ^= 0x7fff; abs += 1; } x = static_cast(std::clamp(static_cast(abs + (33 << 2)), static_cast(0), static_cast(0x7fff))); int index = mpt::countl_zero(x); out |= exp_table[index]; out |= (x >> mant_table[index]) & 0x0f; out ^= 0xff; return mpt::byte_cast(out); } }; struct EncodeALaw { using input_t = int16; using output_t = std::byte; static constexpr uint8 exp_table[17] = {0, 7 << 4, 6 << 4, 5 << 4, 4 << 4, 3 << 4, 2 << 4, 1 << 4, 0 << 4, 0, 0, 0, 0, 0, 0, 0, 0}; static constexpr uint8 mant_table[17] = {0, 10, 9, 8, 7, 6, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}; MPT_FORCEINLINE output_t operator()(input_t val) { int16 sx = std::clamp(val, static_cast(-32767), static_cast(32767)); uint16 x = static_cast(sx); uint8 out = ((x & 0x8000) ^ 0x8000) >> 8; x = static_cast(std::abs(sx)); int index = mpt::countl_zero(x); out |= exp_table[index]; out |= (x >> mant_table[index]) & 0x0f; out ^= 0x55; return mpt::byte_cast(out); } }; } // namespace SC OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/src/openmpt/soundbase/SampleFormat.hpp0000644000175000017500000002351514175314105023622 00000000000000/* SPDX-License-Identifier: BSD-3-Clause */ /* SPDX-FileCopyrightText: OpenMPT Project Developers and Contributors */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "mpt/base/macros.hpp" #include "mpt/base/utility.hpp" #include "openmpt/base/Int24.hpp" #include "openmpt/base/Types.hpp" #include OPENMPT_NAMESPACE_BEGIN class SampleFormat { public: enum class Enum : uint8 { Unsigned8 = 9, // do not change value (for compatibility with old configuration settings) Int8 = 8, // do not change value (for compatibility with old configuration settings) Int16 = 16, // do not change value (for compatibility with old configuration settings) Int24 = 24, // do not change value (for compatibility with old configuration settings) Int32 = 32, // do not change value (for compatibility with old configuration settings) Float32 = 32 + 128, // do not change value (for compatibility with old configuration settings) Float64 = 64 + 128, // do not change value (for compatibility with old configuration settings) Invalid = 0 }; static constexpr SampleFormat::Enum Unsigned8 = SampleFormat::Enum::Unsigned8; static constexpr SampleFormat::Enum Int8 = SampleFormat::Enum::Int8; static constexpr SampleFormat::Enum Int16 = SampleFormat::Enum::Int16; static constexpr SampleFormat::Enum Int24 = SampleFormat::Enum::Int24; static constexpr SampleFormat::Enum Int32 = SampleFormat::Enum::Int32; static constexpr SampleFormat::Enum Float32 = SampleFormat::Enum::Float32; static constexpr SampleFormat::Enum Float64 = SampleFormat::Enum::Float64; static constexpr SampleFormat::Enum Invalid = SampleFormat::Enum::Invalid; private: SampleFormat::Enum value; template static MPT_CONSTEXPRINLINE SampleFormat::Enum Sanitize(T x) noexcept { using uT = typename std::make_unsigned::type; uT val = static_cast(x); if(!val) { return SampleFormat::Enum::Invalid; } if(val == static_cast(-8)) { val = 8 + 1; } // float|64|32|16|8|?|?|unsigned val &= 0b1'1111'00'1; auto is_float = [](uT val) -> bool { return (val & 0b1'0000'00'0) ? true : false; }; auto is_unsigned = [](uT val) -> bool { return (val & 0b0'0000'00'1) ? true : false; }; auto has_size = [](uT val) -> bool { return (val & 0b0'1111'00'0) ? true : false; }; auto unique_size = [](uT val) -> bool { val &= 0b0'1111'00'0; if(val == 0b0'0001'00'0) { return true; } else if(val == 0b0'0010'00'0) { return true; } else if(val == 0b0'0011'00'0) { return true; } else if(val == 0b0'0100'00'0) { return true; } else if(val == 0b0'1000'00'0) { return true; } else { return false; } }; auto get_size = [](uT val) -> std::size_t { val &= 0b0'1111'00'0; if(val == 0b0'0001'00'0) { return 1; } else if(val == 0b0'0010'00'0) { return 2; } else if(val == 0b0'0011'00'0) { return 3; } else if(val == 0b0'0100'00'0) { return 4; } else if(val == 0b0'1000'00'0) { return 8; } else { return 2; // default to 16bit } }; if(!has_size(val)) { if(is_unsigned(val) && is_float(val)) { val = mpt::to_underlying(Enum::Invalid); } else if(is_unsigned(val)) { val = mpt::to_underlying(Enum::Unsigned8); } else if(is_float(val)) { val = mpt::to_underlying(Enum::Float32); } else { val = mpt::to_underlying(Enum::Invalid); } } else if(!unique_size(val)) { // order of size check matters if((val & 0b0'0011'00'0) == 0b0'0011'00'0) { val = mpt::to_underlying(Enum::Int24); } else if(val & 0b0'0010'00'0) { val = mpt::to_underlying(Enum::Int16); } else if(val & 0b0'0100'00'0) { if(is_float(val)) { val = mpt::to_underlying(Enum::Float32); } else { val = mpt::to_underlying(Enum::Int32); } } else if(val & 0b0'1000'00'0) { val = mpt::to_underlying(Enum::Float64); } else if(val & 0b0'0001'00'0) { if(is_unsigned(val)) { val = mpt::to_underlying(Enum::Unsigned8); } else { val = mpt::to_underlying(Enum::Int8); } } } else { if(is_unsigned(val) && (get_size(val) > 1)) { val &= ~0b0'0000'00'1; // remove unsigned } if(is_float(val) && (get_size(val) < 4)) { val &= ~0b1'0000'00'0; // remove float } if(!is_float(val) && (get_size(val) == 8)) { val |= 0b1'0000'00'0; // add float } } return static_cast(val); } public: MPT_CONSTEXPRINLINE SampleFormat() noexcept : value(SampleFormat::Invalid) { } MPT_CONSTEXPRINLINE SampleFormat(SampleFormat::Enum v) noexcept : value(Sanitize(v)) { } friend MPT_CONSTEXPRINLINE bool operator==(const SampleFormat &a, const SampleFormat &b) noexcept { return a.value == b.value; } friend MPT_CONSTEXPRINLINE bool operator!=(const SampleFormat &a, const SampleFormat &b) noexcept { return a.value != b.value; } friend MPT_CONSTEXPRINLINE bool operator==(const SampleFormat::Enum &a, const SampleFormat &b) noexcept { return a == b.value; } friend MPT_CONSTEXPRINLINE bool operator!=(const SampleFormat::Enum &a, const SampleFormat &b) noexcept { return a != b.value; } friend MPT_CONSTEXPRINLINE bool operator==(const SampleFormat &a, const SampleFormat::Enum &b) noexcept { return a.value == b; } friend MPT_CONSTEXPRINLINE bool operator!=(const SampleFormat &a, const SampleFormat::Enum &b) noexcept { return a.value != b; } MPT_CONSTEXPRINLINE bool IsValid() const noexcept { return false || (value == SampleFormat::Unsigned8) || (value == SampleFormat::Int8) || (value == SampleFormat::Int16) || (value == SampleFormat::Int24) || (value == SampleFormat::Int32) || (value == SampleFormat::Float32) || (value == SampleFormat::Float64); } MPT_CONSTEXPRINLINE bool IsUnsigned() const noexcept { return false || (value == SampleFormat::Unsigned8); } MPT_CONSTEXPRINLINE bool IsFloat() const noexcept { return false || (value == SampleFormat::Float32) || (value == SampleFormat::Float64); } MPT_CONSTEXPRINLINE bool IsInt() const noexcept { return false || (value == SampleFormat::Unsigned8) || (value == SampleFormat::Int8) || (value == SampleFormat::Int16) || (value == SampleFormat::Int24) || (value == SampleFormat::Int32); } MPT_CONSTEXPRINLINE uint8 GetSampleSize() const noexcept { return !IsValid() ? 0 : (value == SampleFormat::Unsigned8) ? 1 : (value == SampleFormat::Int8) ? 1 : (value == SampleFormat::Int16) ? 2 : (value == SampleFormat::Int24) ? 3 : (value == SampleFormat::Int32) ? 4 : (value == SampleFormat::Float32) ? 4 : (value == SampleFormat::Float64) ? 8 : 0; } MPT_CONSTEXPRINLINE uint8 GetBitsPerSample() const noexcept { return !IsValid() ? 0 : (value == SampleFormat::Unsigned8) ? 8 : (value == SampleFormat::Int8) ? 8 : (value == SampleFormat::Int16) ? 16 : (value == SampleFormat::Int24) ? 24 : (value == SampleFormat::Int32) ? 32 : (value == SampleFormat::Float32) ? 32 : (value == SampleFormat::Float64) ? 64 : 0; } MPT_CONSTEXPRINLINE operator SampleFormat::Enum() const noexcept { return value; } explicit MPT_CONSTEXPRINLINE operator std::underlying_type::type() const noexcept { return mpt::to_underlying(value); } // backward compatibility, conversion to/from integers static MPT_CONSTEXPRINLINE SampleFormat FromInt(int x) noexcept { return SampleFormat(Sanitize(x)); } static MPT_CONSTEXPRINLINE int ToInt(SampleFormat x) noexcept { return mpt::to_underlying(x.value); } MPT_CONSTEXPRINLINE int AsInt() const noexcept { return mpt::to_underlying(value); } }; template Container AllSampleFormats() { return {SampleFormat::Float64, SampleFormat::Float32, SampleFormat::Int32, SampleFormat::Int24, SampleFormat::Int16, SampleFormat::Int8, SampleFormat::Unsigned8}; } template Container DefaultSampleFormats() { return {SampleFormat::Float32, SampleFormat::Int32, SampleFormat::Int24, SampleFormat::Int16, SampleFormat::Int8}; } template struct SampleFormatTraits; template <> struct SampleFormatTraits { static MPT_CONSTEXPRINLINE SampleFormat sampleFormat() { return SampleFormat::Unsigned8; } }; template <> struct SampleFormatTraits { static MPT_CONSTEXPRINLINE SampleFormat sampleFormat() { return SampleFormat::Int8; } }; template <> struct SampleFormatTraits { static MPT_CONSTEXPRINLINE SampleFormat sampleFormat() { return SampleFormat::Int16; } }; template <> struct SampleFormatTraits { static MPT_CONSTEXPRINLINE SampleFormat sampleFormat() { return SampleFormat::Int24; } }; template <> struct SampleFormatTraits { static MPT_CONSTEXPRINLINE SampleFormat sampleFormat() { return SampleFormat::Int32; } }; template <> struct SampleFormatTraits { static MPT_CONSTEXPRINLINE SampleFormat sampleFormat() { return SampleFormat::Float32; } }; template <> struct SampleFormatTraits { static MPT_CONSTEXPRINLINE SampleFormat sampleFormat() { return SampleFormat::Float64; } }; template struct SampleFormatToType; template <> struct SampleFormatToType { typedef uint8 type; }; template <> struct SampleFormatToType { typedef int8 type; }; template <> struct SampleFormatToType { typedef int16 type; }; template <> struct SampleFormatToType { typedef int24 type; }; template <> struct SampleFormatToType { typedef int32 type; }; template <> struct SampleFormatToType { typedef float type; }; template <> struct SampleFormatToType { typedef double type; }; OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/test/0000755000175000017500000000000014175541576015332 500000000000000libopenmpt-0.6.1+release.autotools/test/mpt_tests_base.cpp0000644000175000017500000000056014044173026020755 00000000000000 #include "stdafx.h" #ifdef ENABLE_TESTS #include "mpt/base/tests/tests_base_arithmetic_shift.hpp" #include "mpt/base/tests/tests_base_bit.hpp" #include "mpt/base/tests/tests_base_math.hpp" #include "mpt/base/tests/tests_base_saturate_cast.hpp" #include "mpt/base/tests/tests_base_saturate_round.hpp" #include "mpt/base/tests/tests_base_wrapping_divide.hpp" #endif libopenmpt-0.6.1+release.autotools/test/mpt_tests_binary.cpp0000644000175000017500000000014014044173026021321 00000000000000 #include "stdafx.h" #ifdef ENABLE_TESTS #include "mpt/binary/tests/tests_binary.hpp" #endif libopenmpt-0.6.1+release.autotools/test/mpt_tests_crc.cpp0000644000175000017500000000013214044173026020605 00000000000000 #include "stdafx.h" #ifdef ENABLE_TESTS #include "mpt/crc/tests/tests_crc.hpp" #endif libopenmpt-0.6.1+release.autotools/test/mpt_tests_endian.cpp0000644000175000017500000000024314044173026021277 00000000000000 #include "stdafx.h" #ifdef ENABLE_TESTS #include "mpt/endian/tests/tests_endian_floatingpoint.hpp" #include "mpt/endian/tests/tests_endian_integer.hpp" #endif libopenmpt-0.6.1+release.autotools/test/mpt_tests_format.cpp0000644000175000017500000000023414044173026021331 00000000000000 #include "stdafx.h" #ifdef ENABLE_TESTS #include "mpt/format/tests/tests_format_message.hpp" #include "mpt/format/tests/tests_format_simple.hpp" #endif libopenmpt-0.6.1+release.autotools/test/mpt_tests_io.cpp0000644000175000017500000000013014056406265020452 00000000000000 #include "stdafx.h" #ifdef ENABLE_TESTS #include "mpt/io/tests/tests_io.hpp" #endif libopenmpt-0.6.1+release.autotools/test/mpt_tests_parse.cpp0000644000175000017500000000013614044173026021154 00000000000000 #include "stdafx.h" #ifdef ENABLE_TESTS #include "mpt/parse/tests/tests_parse.hpp" #endif libopenmpt-0.6.1+release.autotools/test/mpt_tests_random.cpp0000644000175000017500000000014014044173026021315 00000000000000 #include "stdafx.h" #ifdef ENABLE_TESTS #include "mpt/random/tests/tests_random.hpp" #endif libopenmpt-0.6.1+release.autotools/test/mpt_tests_string.cpp0000644000175000017500000000023414044173026021347 00000000000000 #include "stdafx.h" #ifdef ENABLE_TESTS #include "mpt/string/tests/tests_string_buffer.hpp" #include "mpt/string/tests/tests_string_utility.hpp" #endif libopenmpt-0.6.1+release.autotools/test/mpt_tests_string_transcode.cpp0000644000175000017500000000016414107230757023420 00000000000000 #include "stdafx.h" #ifdef ENABLE_TESTS #include "mpt/string_transcode/tests/tests_string_transcode.hpp" #endif libopenmpt-0.6.1+release.autotools/test/mpt_tests_uuid.cpp0000644000175000017500000000013414044173026021006 00000000000000 #include "stdafx.h" #ifdef ENABLE_TESTS #include "mpt/uuid/tests/tests_uuid.hpp" #endif libopenmpt-0.6.1+release.autotools/test/test.cpp0000644000175000017500000044573714175304371016747 00000000000000/* * test.cpp * -------- * Purpose: Unit tests for OpenMPT. * Notes : We need FAAAAAAAR more unit tests! * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "test.h" #ifdef ENABLE_TESTS #include "mpt/base/numbers.hpp" #include "mpt/crc/crc.hpp" #include "mpt/environment/environment.hpp" #include "mpt/io/base.hpp" #include "mpt/io/io.hpp" #include "mpt/io/io_stdstream.hpp" #include "mpt/io_read/filecursor_stdstream.hpp" #include "mpt/test/test.hpp" #include "mpt/test/test_macros.hpp" #include "mpt/uuid/uuid.hpp" #include "../common/version.h" #include "../common/misc_util.h" #include "../common/mptStringBuffer.h" #include "../common/serialization_utils.h" #include "../soundlib/Sndfile.h" #include "../common/FileReader.h" #include "../soundlib/mod_specifications.h" #include "../soundlib/MIDIEvents.h" #include "../soundlib/MIDIMacros.h" #include "openmpt/soundbase/Copy.hpp" #include "openmpt/soundbase/SampleConvert.hpp" #include "openmpt/soundbase/SampleDecode.hpp" #include "openmpt/soundbase/SampleEncode.hpp" #include "openmpt/soundbase/SampleFormat.hpp" #include "../soundlib/SampleCopy.h" #include "../soundlib/SampleNormalize.h" #include "../soundlib/ModSampleCopy.h" #include "../soundlib/ITCompression.h" #include "../soundlib/tuningcollection.h" #include "../soundlib/tuning.h" #include "openmpt/soundbase/Dither.hpp" #include "../common/Dither.h" #ifdef MODPLUG_TRACKER #include "../mptrack/Mptrack.h" #include "../mptrack/Moddoc.h" #include "../mptrack/ModDocTemplate.h" #include "../mptrack/Mainfrm.h" #include "../mptrack/Settings.h" #include "../mptrack/HTTP.h" #endif // MODPLUG_TRACKER #include "../common/mptFileIO.h" #ifdef MODPLUG_TRACKER #include "mpt/crypto/hash.hpp" #include "mpt/crypto/jwk.hpp" #include "mpt/uuid_namespace/uuid_namespace.hpp" #endif // MODPLUG_TRACKER #ifdef LIBOPENMPT_BUILD #include "../libopenmpt/libopenmpt_version.h" #endif // LIBOPENMPT_BUILD #ifndef NO_PLUGINS #include "../soundlib/plugins/PlugInterface.h" #endif #include #include #ifdef LIBOPENMPT_BUILD #include #endif // LIBOPENMPT_BUILD #include #include #include #if MPT_COMPILER_MSVC #include #endif #if MPT_OS_WINDOWS #include #endif #if defined(MPT_WITH_ZLIB) #include #elif defined(MPT_WITH_MINIZ) #define MINIZ_NO_ZLIB_COMPATIBLE_NAMES #include #endif #define MPT_TEST_HAS_FILESYSTEM 1 #if MPT_OS_DJGPP #undef MPT_TEST_HAS_FILESYSTEM #define MPT_TEST_HAS_FILESYSTEM 0 #endif #include "TestTools.h" // enable tests which may fail spuriously //#define FLAKY_TESTS OPENMPT_NAMESPACE_BEGIN namespace Test { static MPT_NOINLINE void TestVersion(); static MPT_NOINLINE void TestTypes(); static MPT_NOINLINE void TestMisc1(); static MPT_NOINLINE void TestMisc2(); static MPT_NOINLINE void TestRandom(); static MPT_NOINLINE void TestCharsets(); static MPT_NOINLINE void TestStringFormatting(); static MPT_NOINLINE void TestSettings(); static MPT_NOINLINE void TestStringIO(); static MPT_NOINLINE void TestMIDIEvents(); static MPT_NOINLINE void TestSampleConversion(); static MPT_NOINLINE void TestITCompression(); static MPT_NOINLINE void TestPCnoteSerialization(); static MPT_NOINLINE void TestLoadSaveFile(); static MPT_NOINLINE void TestEditing(); static mpt::PathString *PathPrefix = nullptr; static mpt::default_prng * s_PRNG = nullptr; mpt::PathString GetPathPrefix() { if((*PathPrefix).empty()) { return P_(""); } return *PathPrefix + P_("/"); } void DoTests() { #ifdef LIBOPENMPT_BUILD std::cout << std::flush; std::clog << std::flush; std::cerr << std::flush; std::cout << "libopenmpt test suite" << std::endl; std::cout << "Version.: " << mpt::ToCharset(mpt::Charset::ASCII, Build::GetVersionString(Build::StringVersion | Build::StringRevision | Build::StringSourceInfo | Build::StringBuildFlags | Build::StringBuildFeatures)) << std::endl; std::cout << "Compiler: " << mpt::ToCharset(mpt::Charset::ASCII, Build::GetBuildCompilerString()) << std::endl; std::cout << std::flush; #endif #if MPT_OS_WINDOWS // prefix for test suite std::wstring pathprefix = std::wstring(); bool libopenmpt = false; #ifdef LIBOPENMPT_BUILD libopenmpt = true; #endif #if !MPT_OS_WINDOWS_WINRT // set path prefix for test files (if provided) std::vector buf(GetEnvironmentVariableW(L"srcdir", NULL, 0) + 1); if(GetEnvironmentVariableW(L"srcdir", buf.data(), static_cast(buf.size())) > 0) { pathprefix = buf.data(); } else #endif if(libopenmpt && IsDebuggerPresent()) { pathprefix = L"../../"; } PathPrefix = new mpt::PathString(mpt::PathString::FromWide(pathprefix)); #else // prefix for test suite mpt::ustring pathprefix = mpt::ustring(); // set path prefix for test files (if provided) mpt::ustring env_srcdir = mpt::getenv( U_("srcdir") ).value_or( U_("") ); if ( !env_srcdir.empty() ) { pathprefix = env_srcdir; } PathPrefix = new mpt::PathString(mpt::PathString::FromUnicode(pathprefix)); #endif void (*do_mpt_test)(void) = []() { mpt_test_reporter reporter{}; mpt::test::run_all(reporter); }; DO_TEST(do_mpt_test); mpt::random_device rd; s_PRNG = new mpt::default_prng(mpt::make_prng(rd)); DO_TEST(TestVersion); DO_TEST(TestTypes); DO_TEST(TestMisc1); DO_TEST(TestMisc2); DO_TEST(TestRandom); DO_TEST(TestCharsets); DO_TEST(TestStringFormatting); DO_TEST(TestSettings); DO_TEST(TestStringIO); DO_TEST(TestMIDIEvents); DO_TEST(TestSampleConversion); DO_TEST(TestITCompression); // slower tests, require opening a CModDoc DO_TEST(TestPCnoteSerialization); DO_TEST(TestLoadSaveFile); DO_TEST(TestEditing); delete s_PRNG; s_PRNG = nullptr; delete PathPrefix; PathPrefix = nullptr; } static mpt::PathString GetTempFilenameBase(); static void RemoveFile(const mpt::PathString &filename) { #if MPT_OS_WINDOWS for(int retry=0; retry<10; retry++) { if(DeleteFile(filename.AsNative().c_str()) != FALSE) { break; } // wait for windows virus scanners Sleep(10); } #else remove(filename.AsNative().c_str()); #endif } // Test if functions related to program version data work static MPT_NOINLINE void TestVersion() { //Verify that macros and functions work. { VERIFY_EQUAL( Version::Parse(Version::Current().ToUString()), Version::Current() ); VERIFY_EQUAL( Version::Parse(Version::Current().ToUString()).ToUString(), Version::Current().ToUString() ); VERIFY_EQUAL( Version(18285096).ToUString(), U_("1.17.02.28") ); VERIFY_EQUAL( Version::Parse(U_("1.17.02.28")), Version(18285096) ); VERIFY_EQUAL( Version::Parse(U_("1.fe.02.28")), Version(0x01fe0228) ); VERIFY_EQUAL( Version::Parse(U_("01.fe.02.28")), Version(0x01fe0228) ); VERIFY_EQUAL( Version::Parse(U_("1.22")), Version(0x01220000) ); VERIFY_EQUAL( MPT_V("1.17.02.28"), Version(18285096) ); VERIFY_EQUAL( MPT_V("1.fe.02.28"), Version(0x01fe0228) ); VERIFY_EQUAL( MPT_V("01.fe.02.28"), Version(0x01fe0228) ); VERIFY_EQUAL( MPT_V("1.22"), Version(0x01220000) ); VERIFY_EQUAL( MPT_V("1.19.02.00").WithoutTestNumber(), MPT_V("1.19.02.00")); VERIFY_EQUAL( MPT_V("1.18.03.20").WithoutTestNumber(), MPT_V("1.18.03.00")); VERIFY_EQUAL( MPT_V("1.18.01.13").IsTestVersion(), true); VERIFY_EQUAL( MPT_V("1.19.01.00").IsTestVersion(), false); VERIFY_EQUAL( MPT_V("1.17.02.54").IsTestVersion(), false); VERIFY_EQUAL( MPT_V("1.18.00.00").IsTestVersion(), false); VERIFY_EQUAL( MPT_V("1.18.02.00").IsTestVersion(), false); VERIFY_EQUAL( MPT_V("1.18.02.01").IsTestVersion(), true); // Ensure that versions ending in .00.00 (which are ambiguous to truncated version numbers in certain file formats (e.g. S3M and IT) do not get qualified as test builds. VERIFY_EQUAL( MPT_V("1.23.00.00").IsTestVersion(), false); static_assert( MPT_V("1.17.2.28").GetRawVersion() == 18285096 ); static_assert( MPT_V("1.17.02.48").GetRawVersion() == 18285128 ); static_assert( MPT_V("01.17.02.52").GetRawVersion() == 18285138 ); // Ensure that bit-shifting works (used in some mod loaders for example) static_assert( MPT_V("01.17.00.00").GetRawVersion() == 0x0117 << 16 ); static_assert( MPT_V("01.17.03.00").GetRawVersion() >> 8 == 0x011703 ); VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05-r13099")).IsNewerThan(VersionWithRevision::Parse(U_("1.30.00.05-r13100"))), false); VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05-r13099")).IsNewerThan(VersionWithRevision::Parse(U_("1.30.00.05-r13099"))), false); VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05-r13099")).IsNewerThan(VersionWithRevision::Parse(U_("1.30.00.05-r13098"))), true); VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05-r13099")).IsNewerThan(VersionWithRevision::Parse(U_("1.30.00.05"))), false); VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05-r13099")).IsOlderThan(VersionWithRevision::Parse(U_("1.30.00.05-r13100"))), true); VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05-r13099")).IsOlderThan(VersionWithRevision::Parse(U_("1.30.00.05-r13099"))), false); VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05-r13099")).IsOlderThan(VersionWithRevision::Parse(U_("1.30.00.05-r13098"))), false); VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05-r13099")).IsOlderThan(VersionWithRevision::Parse(U_("1.30.00.05"))), false); VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05")).IsNewerThan(VersionWithRevision::Parse(U_("1.30.00.05-r13100"))), false); VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05")).IsNewerThan(VersionWithRevision::Parse(U_("1.30.00.05-r13099"))), false); VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05")).IsNewerThan(VersionWithRevision::Parse(U_("1.30.00.05-r13098"))), false); VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05")).IsNewerThan(VersionWithRevision::Parse(U_("1.30.00.05"))), false); VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05")).IsOlderThan(VersionWithRevision::Parse(U_("1.30.00.05-r13100"))), false); VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05")).IsOlderThan(VersionWithRevision::Parse(U_("1.30.00.05-r13099"))), false); VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05")).IsOlderThan(VersionWithRevision::Parse(U_("1.30.00.05-r13098"))), false); VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05")).IsOlderThan(VersionWithRevision::Parse(U_("1.30.00.05"))), false); VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05-r13099")).IsEqualTo(VersionWithRevision::Parse(U_("1.30.00.05-r13100"))), false); VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05-r13099")).IsEqualTo(VersionWithRevision::Parse(U_("1.30.00.05-r13099"))), true); VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05-r13099")).IsEqualTo(VersionWithRevision::Parse(U_("1.30.00.05-r13098"))), false); VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05-r13099")).IsEqualTo(VersionWithRevision::Parse(U_("1.30.00.05"))), false); VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05-r13099")).IsEquivalentTo(VersionWithRevision::Parse(U_("1.30.00.05-r13100"))), false); VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05-r13099")).IsEquivalentTo(VersionWithRevision::Parse(U_("1.30.00.05-r13099"))), true); VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05-r13099")).IsEquivalentTo(VersionWithRevision::Parse(U_("1.30.00.05-r13098"))), false); VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05-r13099")).IsEquivalentTo(VersionWithRevision::Parse(U_("1.30.00.05"))), true); VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05")).IsEqualTo(VersionWithRevision::Parse(U_("1.30.00.05-r13100"))), false); VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05")).IsEqualTo(VersionWithRevision::Parse(U_("1.30.00.05-r13099"))), false); VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05")).IsEqualTo(VersionWithRevision::Parse(U_("1.30.00.05-r13098"))), false); VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05")).IsEqualTo(VersionWithRevision::Parse(U_("1.30.00.05"))), true); VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05")).IsEquivalentTo(VersionWithRevision::Parse(U_("1.30.00.05-r13100"))), true); VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05")).IsEquivalentTo(VersionWithRevision::Parse(U_("1.30.00.05-r13099"))), true); VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05")).IsEquivalentTo(VersionWithRevision::Parse(U_("1.30.00.05-r13098"))), true); VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05")).IsEquivalentTo(VersionWithRevision::Parse(U_("1.30.00.05"))), true); } #ifdef MODPLUG_TRACKER //Verify that the version obtained from the executable file is the same as //defined in Version. { WCHAR szFullPath[MAX_PATH]; DWORD dwVerHnd; DWORD dwVerInfoSize; // Get version information from the application ::GetModuleFileNameW(NULL, szFullPath, mpt::saturate_cast(std::size(szFullPath))); dwVerInfoSize = ::GetFileVersionInfoSizeW(szFullPath, &dwVerHnd); if (!dwVerInfoSize) throw std::runtime_error("!dwVerInfoSize is true"); std::vector pVersionInfo(dwVerInfoSize); WCHAR *szVer = nullptr; UINT uVerLength; if (!(::GetFileVersionInfoW(szFullPath, (DWORD)dwVerHnd, (DWORD)dwVerInfoSize, pVersionInfo.data()))) { throw std::runtime_error("GetFileVersionInfo() returned false"); } if (!(::VerQueryValueW(pVersionInfo.data(), L"\\StringFileInfo\\040904b0\\FileVersion", (LPVOID*)&szVer, &uVerLength))) { throw std::runtime_error("VerQueryValue() returned false"); } mpt::ustring version = mpt::ToUnicode(szVer); VERIFY_EQUAL( version, mpt::ufmt::val(Version::Current()) ); VERIFY_EQUAL( Version::Parse(version), Version::Current() ); } #endif #ifdef LIBOPENMPT_BUILD #if MPT_TEST_HAS_FILESYSTEM #if !MPT_OS_DJGPP mpt::PathString version_mk = GetPathPrefix() + P_("libopenmpt/libopenmpt_version.mk"); mpt::ifstream f(version_mk, std::ios::in); VERIFY_EQUAL(f ? true : false, true); std::map fields; std::string line; while(std::getline(f, line)) { line = mpt::trim(line); if(line.empty()) { continue; } std::vector line_fields = mpt::String::Split(line, std::string("=")); VERIFY_EQUAL_NONCONT(line_fields.size(), 2u); line_fields[0] = mpt::trim(line_fields[0]); line_fields[1] = mpt::trim(line_fields[1]); VERIFY_EQUAL_NONCONT(line_fields[0].length() > 0, true); fields[line_fields[0]] = line_fields[1]; } VERIFY_EQUAL(fields["LIBOPENMPT_VERSION_MAJOR"], mpt::afmt::val(OPENMPT_API_VERSION_MAJOR)); VERIFY_EQUAL(fields["LIBOPENMPT_VERSION_MINOR"], mpt::afmt::val(OPENMPT_API_VERSION_MINOR)); VERIFY_EQUAL(fields["LIBOPENMPT_VERSION_PATCH"], mpt::afmt::val(OPENMPT_API_VERSION_PATCH)); VERIFY_EQUAL(fields["LIBOPENMPT_VERSION_PREREL"], mpt::afmt::val(OPENMPT_API_VERSION_PREREL)); if(std::string(OPENMPT_API_VERSION_PREREL).length() > 0) { VERIFY_EQUAL(std::string(OPENMPT_API_VERSION_PREREL).substr(0, 1), "-"); } VERIFY_EQUAL(OPENMPT_API_VERSION_IS_PREREL, (std::string(OPENMPT_API_VERSION_PREREL).length() > 0) ? 1 : 0); VERIFY_EQUAL(fields["LIBOPENMPT_LTVER_CURRENT"].length() > 0, true); VERIFY_EQUAL(fields["LIBOPENMPT_LTVER_REVISION"].length() > 0, true); VERIFY_EQUAL(fields["LIBOPENMPT_LTVER_AGE"].length() > 0, true); #endif // !MPT_OS_DJGPP #endif // MPT_TEST_HAS_FILESYSTEM #endif // LIBOPENMPT_BUILD } // Test if data types are interpreted correctly static MPT_NOINLINE void TestTypes() { static_assert(sizeof(std::uintptr_t) == sizeof(void*)); #if defined(__SIZEOF_POINTER__) static_assert(__SIZEOF_POINTER__ == mpt::pointer_size); static_assert(__SIZEOF_POINTER__ == sizeof(void*)); #endif VERIFY_EQUAL(int8_min, std::numeric_limits::min()); VERIFY_EQUAL(int8_max, std::numeric_limits::max()); VERIFY_EQUAL(uint8_max, std::numeric_limits::max()); VERIFY_EQUAL(int16_min, std::numeric_limits::min()); VERIFY_EQUAL(int16_max, std::numeric_limits::max()); VERIFY_EQUAL(uint16_max, std::numeric_limits::max()); VERIFY_EQUAL(int32_min, std::numeric_limits::min()); VERIFY_EQUAL(int32_max, std::numeric_limits::max()); VERIFY_EQUAL(uint32_max, std::numeric_limits::max()); VERIFY_EQUAL(int64_min, std::numeric_limits::min()); VERIFY_EQUAL(int64_max, std::numeric_limits::max()); VERIFY_EQUAL(uint64_max, std::numeric_limits::max()); static_assert(int8_max == std::numeric_limits::max()); static_assert(uint8_max == std::numeric_limits::max()); static_assert(int16_max == std::numeric_limits::max()); static_assert(uint16_max == std::numeric_limits::max()); static_assert(int32_max == std::numeric_limits::max()); static_assert(uint32_max == std::numeric_limits::max()); static_assert(int64_max == std::numeric_limits::max()); static_assert(uint64_max == std::numeric_limits::max()); } #ifdef MODPLUG_TRACKER // In tracker debug builds, the sprintf-like function is retained in order to be able to validate our own formatting against sprintf. // There are 4 reasons why this is not available for library code: // 1. printf-like functionality is not type-safe. // 2. There are portability problems with char/wchar_t and the semantics of %s/%ls/%S . // 3. There are portability problems with specifying format for 64bit integers. // 4. Formatting of floating point values depends on the currently set C locale. // A library is not allowed to mock with that and thus cannot influence the behavior in this case. template static std::string StringFormat(std::string format, T x) { #if MPT_COMPILER_MSVC // Count the needed array size. const size_t nCount = _scprintf(format.c_str(), x); // null character not included. std::vector buf(nCount + 1); // + 1 is for null terminator. sprintf_s(&(buf[0]), buf.size(), format.c_str(), x); return &(buf[0]); #else int size = snprintf(NULL, 0, format.c_str(), x); // get required size, requires c99 compliant snprintf which msvc does not have std::vector temp(size + 1); snprintf(&(temp[0]), size + 1, format.c_str(), x); return &(temp[0]); #endif } #endif template static void TestFloatFormat(Tfloat x, std::string format, mpt::FormatFlags f, std::size_t width = 0, int precision = -1) { #ifdef MODPLUG_TRACKER std::string str_sprintf = StringFormat(format, x); #endif std::string str_iostreams = mpt::afmt::fmt(x, mpt::FormatSpec().SetFlags(f).SetWidth(width).SetPrecision(precision)); #ifdef MODPLUG_TRACKER //MPT_LOG_GLOBAL(LogDebug, "test", mpt::ToUnicode(mpt::Charset::ASCII, str_sprintf)); #endif //MPT_LOG_GLOBAL(LogDebug, "test", mpt::ToUnicode(mpt::Charset::ASCII, str_iostreams)); #ifdef MODPLUG_TRACKER #if MPT_MSVC_AT_LEAST(2019,4) || MPT_GCC_AT_LEAST(11,1,0) // to_chars generates shortest form instead of 0-padded precision if(precision != -1) #endif { VERIFY_EQUAL(str_iostreams, str_sprintf); // this will fail with a set c locale (and there is nothing that can be done about that in libopenmpt) } #else MPT_UNREFERENCED_PARAMETER(format); MPT_UNUSED_VARIABLE(str_iostreams); #endif } template static void TestFloatFormats(Tfloat x) { TestFloatFormat(x, "%.8g", mpt::afmt::NotaNrm | mpt::afmt::FillOff, 0, 8); TestFloatFormat(x, MPT_AFORMAT("%.{}g")(std::numeric_limits::max_digits10), mpt::afmt::NotaNrm | mpt::afmt::FillOff); TestFloatFormat(x, MPT_AFORMAT("%.{}f")(std::numeric_limits::digits10), mpt::afmt::NotaFix | mpt::afmt::FillOff); TestFloatFormat(x, MPT_AFORMAT("%.{}e")(std::numeric_limits::max_digits10 - 1), mpt::afmt::NotaSci | mpt::afmt::FillOff); TestFloatFormat(x, "%.0f", mpt::afmt::NotaFix | mpt::afmt::FillOff, 0, 0); TestFloatFormat(x, "%.1f", mpt::afmt::NotaFix | mpt::afmt::FillOff, 0, 1); TestFloatFormat(x, "%.2f", mpt::afmt::NotaFix | mpt::afmt::FillOff, 0, 2); TestFloatFormat(x, "%.3f", mpt::afmt::NotaFix | mpt::afmt::FillOff, 0, 3); TestFloatFormat(x, "%0.1f", mpt::afmt::NotaFix | mpt::afmt::FillNul, 0, 1); TestFloatFormat(x, "%02.0f", mpt::afmt::NotaFix | mpt::afmt::FillNul, 2, 0); } static MPT_NOINLINE void TestStringFormatting() { VERIFY_EQUAL(mpt::afmt::val(1.5f), "1.5"); VERIFY_EQUAL(mpt::afmt::val(true), "1"); VERIFY_EQUAL(mpt::afmt::val(false), "0"); //VERIFY_EQUAL(mpt::afmt::val('A'), "A"); // deprecated //VERIFY_EQUAL(mpt::afmt::val(L'A'), "A"); // deprecated VERIFY_EQUAL(mpt::afmt::val(0), "0"); VERIFY_EQUAL(mpt::afmt::val(-23), "-23"); VERIFY_EQUAL(mpt::afmt::val(42), "42"); VERIFY_EQUAL(mpt::afmt::hex0<3>((int32)-1), "-001"); VERIFY_EQUAL(mpt::afmt::hex((int32)-1), "-1"); VERIFY_EQUAL(mpt::afmt::hex(-0xabcde), "-abcde"); VERIFY_EQUAL(mpt::afmt::hex(int32_min), "-80000000"); VERIFY_EQUAL(mpt::afmt::hex(int32_min + 1), "-7fffffff"); VERIFY_EQUAL(mpt::afmt::hex(0x123e), "123e"); VERIFY_EQUAL(mpt::afmt::hex0<6>(0x123e), "00123e"); VERIFY_EQUAL(mpt::afmt::hex0<2>(0x123e), "123e"); VERIFY_EQUAL(mpt::afmt::dec0<0>(1), "1"); VERIFY_EQUAL(mpt::afmt::dec0<1>(1), "1"); VERIFY_EQUAL(mpt::afmt::dec0<2>(1), "01"); VERIFY_EQUAL(mpt::afmt::dec0<3>(1), "001"); VERIFY_EQUAL(mpt::afmt::dec0<0>(11), "11"); VERIFY_EQUAL(mpt::afmt::dec0<1>(11), "11"); VERIFY_EQUAL(mpt::afmt::dec0<2>(11), "11"); VERIFY_EQUAL(mpt::afmt::dec0<3>(11), "011"); VERIFY_EQUAL(mpt::afmt::dec0<0>(-1), "-1"); VERIFY_EQUAL(mpt::afmt::dec0<1>(-1), "-1"); VERIFY_EQUAL(mpt::afmt::dec0<2>(-1), "-01"); VERIFY_EQUAL(mpt::afmt::dec0<3>(-1), "-001"); VERIFY_EQUAL(mpt::ufmt::HEX0<7>(0xa2345678), U_("A2345678")); VERIFY_EQUAL(mpt::ufmt::HEX0<8>(0xa2345678), U_("A2345678")); VERIFY_EQUAL(mpt::ufmt::HEX0<9>(0xa2345678), U_("0A2345678")); VERIFY_EQUAL(mpt::ufmt::HEX0<10>(0xa2345678), U_("00A2345678")); #if MPT_WSTRING_FORMAT VERIFY_EQUAL(mpt::wfmt::hex(0x123e), L"123e"); VERIFY_EQUAL(mpt::wfmt::hex0<6>(0x123e), L"00123e"); VERIFY_EQUAL(mpt::wfmt::hex0<2>(0x123e), L"123e"); #endif VERIFY_EQUAL(mpt::afmt::val(-87.0f), "-87"); if(mpt::afmt::val(-0.5e-6) != "-5e-007" && mpt::afmt::val(-0.5e-6) != "-5e-07" && mpt::afmt::val(-0.5e-6) != "-5e-7" && mpt::afmt::val(-0.5e-6) != "-4.9999999999999998e-7" && mpt::afmt::val(-0.5e-6) != "-4.9999999999999998e-07" && mpt::afmt::val(-0.5e-6) != "-4.9999999999999998e-007" ) { VERIFY_EQUAL(true, false); } if(mpt::afmt::val(-1.0 / 65536.0) != "-1.52587890625e-005" && mpt::afmt::val(-1.0 / 65536.0) != "-1.52587890625e-05" && mpt::afmt::val(-1.0 / 65536.0) != "-1.52587890625e-5" ) { VERIFY_EQUAL(true, false); } if(mpt::afmt::val(-1.0f / 65536.0f) != "-1.52587891e-005" && mpt::afmt::val(-1.0f / 65536.0f) != "-1.52587891e-05" && mpt::afmt::val(-1.0f / 65536.0f) != "-1.52587891e-5" && mpt::afmt::val(-1.0f / 65536.0f) != "-1.5258789e-005" && mpt::afmt::val(-1.0f / 65536.0f) != "-1.5258789e-05" && mpt::afmt::val(-1.0f / 65536.0f) != "-1.5258789e-5" ) { VERIFY_EQUAL(true, false); } if(mpt::afmt::val(58.65403492763) != "58.654034927630001" && mpt::afmt::val(58.65403492763) != "58.65403492763" ) { VERIFY_EQUAL(true, false); } VERIFY_EQUAL(mpt::afmt::flt(58.65403492763, 6), "58.654"); VERIFY_EQUAL(mpt::afmt::fix(23.42, 1), "23.4"); VERIFY_EQUAL(mpt::afmt::fix(234.2, 1), "234.2"); VERIFY_EQUAL(mpt::afmt::fix(2342.0, 1), "2342.0"); VERIFY_EQUAL(mpt::afmt::dec(2, ';', 2345678), std::string("2;34;56;78")); VERIFY_EQUAL(mpt::afmt::dec(2, ';', 12345678), std::string("12;34;56;78")); VERIFY_EQUAL(mpt::afmt::hex(3, ':', 0xa2345678), std::string("a2:345:678")); VERIFY_EQUAL(mpt::ufmt::dec(2, ';', 12345678), U_("12;34;56;78")); VERIFY_EQUAL(mpt::ufmt::hex(3, ':', 0xa2345678), U_("a2:345:678")); VERIFY_EQUAL(mpt::ufmt::HEX0<7>(3, ':', 0xa2345678), U_("A2:345:678")); VERIFY_EQUAL(mpt::ufmt::HEX0<8>(3, ':', 0xa2345678), U_("A2:345:678")); VERIFY_EQUAL(mpt::ufmt::HEX0<9>(3, ':', 0xa2345678), U_("0A2:345:678")); VERIFY_EQUAL(mpt::ufmt::HEX0<10>(3, ':', 0xa2345678), U_("0:0A2:345:678")); VERIFY_EQUAL(mpt::ufmt::HEX0<11>(3, ':', 0xa2345678), U_("00:0A2:345:678")); VERIFY_EQUAL(mpt::ufmt::HEX0<12>(3, ':', 0xa2345678), U_("000:0A2:345:678")); VERIFY_EQUAL(mpt::ufmt::HEX0<7>(3, ':', -0x12345678), U_("-12:345:678")); VERIFY_EQUAL(mpt::ufmt::HEX0<8>(3, ':', -0x12345678), U_("-12:345:678")); VERIFY_EQUAL(mpt::ufmt::HEX0<9>(3, ':', -0x12345678), U_("-012:345:678")); VERIFY_EQUAL(mpt::ufmt::HEX0<10>(3, ':', -0x12345678), U_("-0:012:345:678")); VERIFY_EQUAL(mpt::ufmt::HEX0<11>(3, ':', -0x12345678), U_("-00:012:345:678")); VERIFY_EQUAL(mpt::ufmt::HEX0<12>(3, ':', -0x12345678), U_("-000:012:345:678")); VERIFY_EQUAL(mpt::ufmt::HEX0<5>(3, ':', 0x345678), U_("345:678")); VERIFY_EQUAL(mpt::ufmt::HEX0<6>(3, ':', 0x345678), U_("345:678")); VERIFY_EQUAL(mpt::ufmt::HEX0<7>(3, ':', 0x345678), U_("0:345:678")); VERIFY_EQUAL(mpt::ufmt::HEX0<5>(3, ':', -0x345678), U_("-345:678")); VERIFY_EQUAL(mpt::ufmt::HEX0<6>(3, ':', -0x345678), U_("-345:678")); VERIFY_EQUAL(mpt::ufmt::HEX0<7>(3, ':', -0x345678), U_("-0:345:678")); VERIFY_EQUAL(mpt::afmt::left(3, "a"), "a "); VERIFY_EQUAL(mpt::afmt::right(3, "a"), " a"); VERIFY_EQUAL(mpt::afmt::center(3, "a"), " a "); VERIFY_EQUAL(mpt::afmt::center(4, "a"), " a "); #if defined(MPT_WITH_MFC) VERIFY_EQUAL(mpt::cfmt::left(3, CString(_T("a"))), CString(_T("a "))); VERIFY_EQUAL(mpt::cfmt::right(3, CString(_T("a"))), CString(_T(" a"))); VERIFY_EQUAL(mpt::cfmt::center(3, CString(_T("a"))), CString(_T(" a "))); VERIFY_EQUAL(mpt::cfmt::center(4, CString(_T("a"))), CString(_T(" a "))); #endif // MPT_WITH_MFC VERIFY_EQUAL(ConvertStrTo("586"), 586u); VERIFY_EQUAL(ConvertStrTo("2147483647"), (uint32)int32_max); VERIFY_EQUAL(ConvertStrTo("4294967295"), uint32_max); VERIFY_EQUAL(ConvertStrTo("-9223372036854775808"), int64_min); VERIFY_EQUAL(ConvertStrTo("-159"), -159); VERIFY_EQUAL(ConvertStrTo("9223372036854775807"), int64_max); VERIFY_EQUAL(ConvertStrTo("85059"), 85059u); VERIFY_EQUAL(ConvertStrTo("9223372036854775807"), (uint64)int64_max); VERIFY_EQUAL(ConvertStrTo("18446744073709551615"), uint64_max); VERIFY_EQUAL(ConvertStrTo("-87.0"), -87.0f); #if !MPT_OS_DJGPP VERIFY_EQUAL(ConvertStrTo("-0.5e-6"), -0.5e-6); #endif #if !MPT_OS_DJGPP VERIFY_EQUAL(ConvertStrTo("58.65403492763"), 58.65403492763); #else VERIFY_EQUAL_EPS(ConvertStrTo("58.65403492763"), 58.65403492763, 0.0001); #endif VERIFY_EQUAL(ConvertStrTo(mpt::afmt::val(-87.0)), -87.0f); #if !MPT_OS_DJGPP VERIFY_EQUAL(ConvertStrTo(mpt::afmt::val(-0.5e-6)), -0.5e-6); #endif VERIFY_EQUAL(mpt::String::Parse::Hex("fe"), 254); #if MPT_WSTRING_FORMAT VERIFY_EQUAL(mpt::String::Parse::Hex(L"fe"), 254); #endif VERIFY_EQUAL(mpt::String::Parse::Hex(U_("ffff")), 65535); TestFloatFormats(0.0f); TestFloatFormats(-0.0f); TestFloatFormats(1.0f); TestFloatFormats(-1.0f); TestFloatFormats(0.1f); TestFloatFormats(-0.1f); TestFloatFormats(1000000000.0f); TestFloatFormats(-1000000000.0f); TestFloatFormats(0.0000000001f); TestFloatFormats(-0.0000000001f); TestFloatFormats(6.12345f); TestFloatFormats(0.0); TestFloatFormats(-0.0); TestFloatFormats(1.0); TestFloatFormats(-1.0); TestFloatFormats(0.1); TestFloatFormats(-0.1); TestFloatFormats(1000000000.0); TestFloatFormats(-1000000000.0); TestFloatFormats(0.0000000001); TestFloatFormats(-0.0000000001); TestFloatFormats(6.12345); TestFloatFormats(42.1234567890); TestFloatFormats(0.1234567890); TestFloatFormats(1234567890000000.0); TestFloatFormats(0.0000001234567890); TestFloatFormats(mpt::numbers::pi); TestFloatFormats(3.14159265358979323846); TestFloatFormats(3.14159265358979323846f); VERIFY_EQUAL(mpt::afmt::flt(6.12345, 3), "6.12"); VERIFY_EQUAL(mpt::afmt::fix(6.12345, 3), "6.123"); VERIFY_EQUAL(mpt::afmt::flt(6.12345, 4), "6.123"); VERIFY_EQUAL(mpt::afmt::fix(6.12345, 4), "6.1235"); #if MPT_WSTRING_FORMAT VERIFY_EQUAL(mpt::wfmt::flt(6.12345, 3), L"6.12"); VERIFY_EQUAL(mpt::wfmt::fix(6.12345, 3), L"6.123"); VERIFY_EQUAL(mpt::wfmt::flt(6.12345, 4), L"6.123"); #endif static_assert(mpt::parse_format_string_argument_count("") == 0); static_assert(mpt::parse_format_string_argument_count("{{") == 0); static_assert(mpt::parse_format_string_argument_count("}}") == 0); static_assert(mpt::parse_format_string_argument_count("{}") == 1); static_assert(mpt::parse_format_string_argument_count("{}{}") == 2); static_assert(mpt::parse_format_string_argument_count("{0}{1}") == 2); // basic VERIFY_EQUAL(MPT_AFORMAT("{}{}{}")(1,2,3), "123"); VERIFY_EQUAL(MPT_AFORMAT("{2}{1}{0}")(1,2,3), "321"); VERIFY_EQUAL(MPT_AFORMAT("{2}{1}{0}{4}{3}{6}{5}{7}{10}{9}{8}")(0,1,2,3,4,5,6,7,8,9,"a"), "21043657a98"); //VERIFY_EQUAL(MPT_AFORMAT("{2}{1}{0}{2}{1}{0}{10}{9}{8}")(0,1,2,3,4,5,6,7,8,9,"a"), "210210a98"); #if MPT_WSTRING_FORMAT VERIFY_EQUAL(MPT_WFORMAT("{}{}{}")(1,2,3), L"123"); #endif // escaping behviour VERIFY_EQUAL(MPT_AFORMAT("%")(), "%"); VERIFY_EQUAL(MPT_AFORMAT("%")(), "%"); VERIFY_EQUAL(MPT_AFORMAT("%%")(), "%%"); VERIFY_EQUAL(MPT_AFORMAT("{}")("a"), "a"); VERIFY_EQUAL(MPT_AFORMAT("{}%")("a"), "a%"); VERIFY_EQUAL(MPT_AFORMAT("{}%")("a"), "a%"); VERIFY_EQUAL(MPT_AFORMAT("{}%%")("a"), "a%%"); VERIFY_EQUAL(MPT_AFORMAT("%1")(), "%1"); VERIFY_EQUAL(MPT_AFORMAT("%{}")("a"), "%a"); VERIFY_EQUAL(MPT_AFORMAT("%b")(), "%b"); VERIFY_EQUAL(MPT_AFORMAT("{{}}")(), "{}"); VERIFY_EQUAL(MPT_AFORMAT("{{{}}}")("a"), "{a}"); #if defined(MPT_WITH_MFC) VERIFY_EQUAL(mpt::ufmt::val(CString(_T("foobar"))), U_("foobar")); VERIFY_EQUAL(mpt::ufmt::val(CString(_T("foobar"))), U_("foobar")); VERIFY_EQUAL(MPT_CFORMAT("{}{}{}")(1,2,3), _T("123")); VERIFY_EQUAL(MPT_CFORMAT("{}{}{}")(1,mpt::cfmt::dec0<3>(2),3), _T("10023")); #endif // MPT_WITH_MFC FlagSet foo; foo.set(MOD_TYPE_MOD, true); VERIFY_EQUAL(MPT_UFORMAT("{}")(foo), U_("00000000000000000000000000000001")); } namespace { struct Gregorian { int Y,M,D,h,m,s; static Gregorian FromTM(tm t) { Gregorian g; g.Y = t.tm_year + 1900; g.M = t.tm_mon + 1; g.D = t.tm_mday; g.h = t.tm_hour; g.m = t.tm_min; g.s = t.tm_sec; return g; } static tm ToTM(Gregorian g) { tm t; MemsetZero(t); t.tm_year = g.Y - 1900; t.tm_mon = g.M - 1; t.tm_mday = g.D; t.tm_hour = g.h; t.tm_min = g.m; t.tm_sec = g.s; return t; } }; inline bool operator ==(Gregorian a, Gregorian b) { return a.Y == b.Y && a.M == b.M && a.D == b.D && a.h == b.h && a.m == b.m && a.s == b.s; } } static int64 TestDate1(int s, int m, int h, int D, int M, int Y) { return mpt::Date::Unix::FromUTC(Gregorian::ToTM(Gregorian{Y,M,D,h,m,s})); } static Gregorian TestDate2(int s, int m, int h, int D, int M, int Y) { return Gregorian{Y,M,D,h,m,s}; } static MPT_NOINLINE void TestMisc1() { VERIFY_EQUAL(ModCommand::IsPcNote(NOTE_MAX), false); VERIFY_EQUAL(ModCommand::IsPcNote(NOTE_PC), true); VERIFY_EQUAL(ModCommand::IsPcNote(NOTE_PCS), true); VERIFY_EQUAL(CModSpecifications::ExtensionToType(".mod"), MOD_TYPE_MOD); VERIFY_EQUAL(CModSpecifications::ExtensionToType("mod"), MOD_TYPE_MOD); VERIFY_EQUAL(CModSpecifications::ExtensionToType(".s3m"), MOD_TYPE_S3M); VERIFY_EQUAL(CModSpecifications::ExtensionToType("s3m"), MOD_TYPE_S3M); VERIFY_EQUAL(CModSpecifications::ExtensionToType(".xm"), MOD_TYPE_XM); VERIFY_EQUAL(CModSpecifications::ExtensionToType("xm"), MOD_TYPE_XM); VERIFY_EQUAL(CModSpecifications::ExtensionToType(".it"), MOD_TYPE_IT); VERIFY_EQUAL(CModSpecifications::ExtensionToType("it"), MOD_TYPE_IT); VERIFY_EQUAL(CModSpecifications::ExtensionToType(".itp"), MOD_TYPE_NONE); VERIFY_EQUAL(CModSpecifications::ExtensionToType("itp"), MOD_TYPE_NONE); VERIFY_EQUAL(CModSpecifications::ExtensionToType("mptm"), MOD_TYPE_MPT); VERIFY_EQUAL(CModSpecifications::ExtensionToType("invalidExtension"), MOD_TYPE_NONE); VERIFY_EQUAL(CModSpecifications::ExtensionToType("ita"), MOD_TYPE_NONE); VERIFY_EQUAL(CModSpecifications::ExtensionToType("s2m"), MOD_TYPE_NONE); VERIFY_EQUAL(CModSpecifications::ExtensionToType(""), MOD_TYPE_NONE); // invalid VERIFY_EQUAL(SampleFormat::FromInt(0), SampleFormat::Invalid); VERIFY_EQUAL(SampleFormat::FromInt(0b0'0000'11'0), SampleFormat::Invalid); // correct VERIFY_EQUAL(SampleFormat::FromInt(0b0'0001'00'1), SampleFormat::Unsigned8); VERIFY_EQUAL(SampleFormat::FromInt(0b0'0001'00'0), SampleFormat::Int8); VERIFY_EQUAL(SampleFormat::FromInt(0b0'0010'00'0), SampleFormat::Int16); VERIFY_EQUAL(SampleFormat::FromInt(0b0'0011'00'0), SampleFormat::Int24); VERIFY_EQUAL(SampleFormat::FromInt(0b0'0100'00'0), SampleFormat::Int32); VERIFY_EQUAL(SampleFormat::FromInt(0b1'0100'00'0), SampleFormat::Float32); VERIFY_EQUAL(SampleFormat::FromInt(0b1'1000'00'0), SampleFormat::Float64); // no size VERIFY_EQUAL(SampleFormat::FromInt(0b0'0000'00'0), SampleFormat::Invalid); VERIFY_EQUAL(SampleFormat::FromInt(0b0'0000'00'1), SampleFormat::Unsigned8); VERIFY_EQUAL(SampleFormat::FromInt(0b1'0000'00'0), SampleFormat::Float32); VERIFY_EQUAL(SampleFormat::FromInt(0b1'0000'00'1), SampleFormat::Invalid); // invalid unsigned VERIFY_EQUAL(SampleFormat::FromInt(0b0'0010'00'1), SampleFormat::Int16); VERIFY_EQUAL(SampleFormat::FromInt(0b0'0011'00'1), SampleFormat::Int24); VERIFY_EQUAL(SampleFormat::FromInt(0b0'0100'00'1), SampleFormat::Int32); // invalid float VERIFY_EQUAL(SampleFormat::FromInt(0b1'0001'00'0), SampleFormat::Int8); VERIFY_EQUAL(SampleFormat::FromInt(0b1'0010'00'0), SampleFormat::Int16); VERIFY_EQUAL(SampleFormat::FromInt(0b1'0011'00'0), SampleFormat::Int24); // bogus size VERIFY_EQUAL(SampleFormat::FromInt(0b0'0001'00'0), SampleFormat::Int8); VERIFY_EQUAL(SampleFormat::FromInt(0b0'0010'00'0), SampleFormat::Int16); VERIFY_EQUAL(SampleFormat::FromInt(0b0'0011'00'0), SampleFormat::Int24); VERIFY_EQUAL(SampleFormat::FromInt(0b0'0100'00'0), SampleFormat::Int32); VERIFY_EQUAL(SampleFormat::FromInt(0b0'0101'00'0), SampleFormat::Int32); VERIFY_EQUAL(SampleFormat::FromInt(0b0'0110'00'0), SampleFormat::Int16); VERIFY_EQUAL(SampleFormat::FromInt(0b0'0111'00'0), SampleFormat::Int24); VERIFY_EQUAL(SampleFormat::FromInt(0b0'1000'00'0), SampleFormat::Float64); VERIFY_EQUAL(SampleFormat::FromInt(0b0'1001'00'0), SampleFormat::Float64); VERIFY_EQUAL(SampleFormat::FromInt(0b0'1010'00'0), SampleFormat::Int16); VERIFY_EQUAL(SampleFormat::FromInt(0b0'1011'00'0), SampleFormat::Int24); VERIFY_EQUAL(SampleFormat::FromInt(0b0'1100'00'0), SampleFormat::Int32); VERIFY_EQUAL(SampleFormat::FromInt(0b0'1101'00'0), SampleFormat::Int32); VERIFY_EQUAL(SampleFormat::FromInt(0b0'1110'00'0), SampleFormat::Int16); VERIFY_EQUAL(SampleFormat::FromInt(0b0'1111'00'0), SampleFormat::Int24); VERIFY_EQUAL(SampleFormat::FromInt(0b0'0001'00'1), SampleFormat::Unsigned8); VERIFY_EQUAL(SampleFormat::FromInt(0b0'0010'00'1), SampleFormat::Int16); VERIFY_EQUAL(SampleFormat::FromInt(0b0'0011'00'1), SampleFormat::Int24); VERIFY_EQUAL(SampleFormat::FromInt(0b0'0100'00'1), SampleFormat::Int32); VERIFY_EQUAL(SampleFormat::FromInt(0b0'0101'00'1), SampleFormat::Int32); VERIFY_EQUAL(SampleFormat::FromInt(0b0'0110'00'1), SampleFormat::Int16); VERIFY_EQUAL(SampleFormat::FromInt(0b0'0111'00'1), SampleFormat::Int24); VERIFY_EQUAL(SampleFormat::FromInt(0b0'1000'00'1), SampleFormat::Float64); VERIFY_EQUAL(SampleFormat::FromInt(0b0'1001'00'1), SampleFormat::Float64); VERIFY_EQUAL(SampleFormat::FromInt(0b0'1010'00'1), SampleFormat::Int16); VERIFY_EQUAL(SampleFormat::FromInt(0b0'1011'00'1), SampleFormat::Int24); VERIFY_EQUAL(SampleFormat::FromInt(0b0'1100'00'1), SampleFormat::Int32); VERIFY_EQUAL(SampleFormat::FromInt(0b0'1101'00'1), SampleFormat::Int32); VERIFY_EQUAL(SampleFormat::FromInt(0b0'1110'00'1), SampleFormat::Int16); VERIFY_EQUAL(SampleFormat::FromInt(0b0'1111'00'1), SampleFormat::Int24); VERIFY_EQUAL(SampleFormat::FromInt(0b1'0001'00'0), SampleFormat::Int8); VERIFY_EQUAL(SampleFormat::FromInt(0b1'0010'00'0), SampleFormat::Int16); VERIFY_EQUAL(SampleFormat::FromInt(0b1'0011'00'0), SampleFormat::Int24); VERIFY_EQUAL(SampleFormat::FromInt(0b1'0100'00'0), SampleFormat::Float32); VERIFY_EQUAL(SampleFormat::FromInt(0b1'0101'00'0), SampleFormat::Float32); VERIFY_EQUAL(SampleFormat::FromInt(0b1'0110'00'0), SampleFormat::Int16); VERIFY_EQUAL(SampleFormat::FromInt(0b1'0111'00'0), SampleFormat::Int24); VERIFY_EQUAL(SampleFormat::FromInt(0b1'1000'00'0), SampleFormat::Float64); VERIFY_EQUAL(SampleFormat::FromInt(0b1'1001'00'0), SampleFormat::Float64); VERIFY_EQUAL(SampleFormat::FromInt(0b1'1010'00'0), SampleFormat::Int16); VERIFY_EQUAL(SampleFormat::FromInt(0b1'1011'00'0), SampleFormat::Int24); VERIFY_EQUAL(SampleFormat::FromInt(0b1'1100'00'0), SampleFormat::Float32); VERIFY_EQUAL(SampleFormat::FromInt(0b1'1101'00'0), SampleFormat::Float32); VERIFY_EQUAL(SampleFormat::FromInt(0b1'1110'00'0), SampleFormat::Int16); VERIFY_EQUAL(SampleFormat::FromInt(0b1'1111'00'0), SampleFormat::Int24); VERIFY_EQUAL(SampleFormat::FromInt(0b1'0001'00'1), SampleFormat::Unsigned8); VERIFY_EQUAL(SampleFormat::FromInt(0b1'0010'00'1), SampleFormat::Int16); VERIFY_EQUAL(SampleFormat::FromInt(0b1'0011'00'1), SampleFormat::Int24); VERIFY_EQUAL(SampleFormat::FromInt(0b1'0100'00'1), SampleFormat::Float32); VERIFY_EQUAL(SampleFormat::FromInt(0b1'0101'00'1), SampleFormat::Float32); VERIFY_EQUAL(SampleFormat::FromInt(0b1'0110'00'1), SampleFormat::Int16); VERIFY_EQUAL(SampleFormat::FromInt(0b1'0111'00'1), SampleFormat::Int24); VERIFY_EQUAL(SampleFormat::FromInt(0b1'1000'00'1), SampleFormat::Float64); VERIFY_EQUAL(SampleFormat::FromInt(0b1'1001'00'1), SampleFormat::Float64); VERIFY_EQUAL(SampleFormat::FromInt(0b1'1010'00'1), SampleFormat::Int16); VERIFY_EQUAL(SampleFormat::FromInt(0b1'1011'00'1), SampleFormat::Int24); VERIFY_EQUAL(SampleFormat::FromInt(0b1'1100'00'1), SampleFormat::Float32); VERIFY_EQUAL(SampleFormat::FromInt(0b1'1101'00'1), SampleFormat::Float32); VERIFY_EQUAL(SampleFormat::FromInt(0b1'1110'00'1), SampleFormat::Int16); VERIFY_EQUAL(SampleFormat::FromInt(0b1'1111'00'1), SampleFormat::Int24); } static MPT_NOINLINE void TestMisc2() { // Check for completeness of supported effect list in mod specifications for(const auto &spec : ModSpecs::Collection) { VERIFY_EQUAL(strlen(spec->commands), (size_t)MAX_EFFECTS); VERIFY_EQUAL(strlen(spec->volcommands), (size_t)MAX_VOLCMDS); } #ifdef MODPLUG_TRACKER #ifdef MPT_ENABLE_FILEIO { std::vector data; data.push_back(mpt::as_byte(0)); data.push_back(mpt::as_byte(255)); data.push_back(mpt::as_byte(1)); data.push_back(mpt::as_byte(2)); mpt::PathString fn = GetTempFilenameBase() + P_("lazy"); RemoveFile(fn); mpt::LazyFileRef f(fn); f = data; std::vector data2; data2 = f; VERIFY_EQUAL(data.size(), data2.size()); for(std::size_t i = 0; i < data.size() && i < data2.size(); ++i) { VERIFY_EQUAL(data[i], data2[i]); } RemoveFile(fn); } #endif #endif // MODPLUG_TRACKER #ifdef MPT_WITH_ZLIB VERIFY_EQUAL(crc32(0, mpt::byte_cast(std::string("123456789").c_str()), 9), 0xCBF43926u); #endif #ifdef MPT_WITH_MINIZ VERIFY_EQUAL(mz_crc32(0, mpt::byte_cast(std::string("123456789").c_str()), 9), 0xCBF43926u); #endif // Check floating-point accuracy in TransposeToFrequency static constexpr int32 transposeToFrequency[] = { 5, 5, 5, 5, 31, 32, 33, 34, 196, 202, 207, 214, 1243, 1280, 1317, 1356, 7894, 8125, 8363, 8608, 50121, 51590, 53102, 54658, 318251, 327576, 337175, 347055, 2020767, 2079980, 2140928, 2203663, }; int freqIndex = 0; for(int32 transpose = -128; transpose < 128; transpose += 32) { for(int32 finetune = -128; finetune < 128; finetune += 64, freqIndex++) { const auto freq = ModSample::TransposeToFrequency(transpose, finetune); VERIFY_EQUAL_EPS(transposeToFrequency[freqIndex], static_cast(freq), 1); if(transpose >= -96) { // Verify transpose+finetune <-> frequency roundtrip // (not for transpose = -128 because it would require fractional precision that we don't have here) ModSample smp; smp.nC5Speed = freq; smp.FrequencyToTranspose(); smp.TransposeToFrequency(); VERIFY_EQUAL(freq, smp.nC5Speed); } } } { ModSample smp; smp.nC5Speed = 9999; smp.Transpose(1.5); VERIFY_EQUAL_EPS(28281, static_cast(smp.nC5Speed), 1); smp.Transpose(-1.3); VERIFY_EQUAL_EPS(11486, static_cast(smp.nC5Speed), 1); } // Check SamplePosition fixed-point type VERIFY_EQUAL(SamplePosition(1).GetRaw(), 1); VERIFY_EQUAL(SamplePosition(2).Set(1), SamplePosition(1, 0)); VERIFY_EQUAL(SamplePosition(2).SetInt(1), SamplePosition(1, 2)); VERIFY_EQUAL(SamplePosition(1).IsPositive(), true); VERIFY_EQUAL(SamplePosition(0).IsZero(), true); VERIFY_EQUAL(SamplePosition(1, 0).IsUnity(), true); VERIFY_EQUAL(SamplePosition(1, 1).IsUnity(), false); VERIFY_EQUAL(SamplePosition(-1).IsNegative(), true); VERIFY_EQUAL(SamplePosition(int64_max).GetRaw(), int64_max); VERIFY_EQUAL(SamplePosition(2, SamplePosition::fractMax).GetInt(), 2); VERIFY_EQUAL(SamplePosition(2, SamplePosition::fractMax).GetFract(), SamplePosition::fractMax); VERIFY_EQUAL(SamplePosition(1, SamplePosition::fractMax).GetInvertedFract(), SamplePosition(0, 1)); VERIFY_EQUAL(SamplePosition(1, 0).GetInvertedFract(), SamplePosition(1, 0)); VERIFY_EQUAL(SamplePosition(2, 0).Negate(), SamplePosition(-2, 0)); VERIFY_EQUAL(SamplePosition::Ratio(10, 5), SamplePosition(2, 0)); VERIFY_EQUAL(SamplePosition(1, 1) + SamplePosition(2, 2), SamplePosition(3, 3)); VERIFY_EQUAL(SamplePosition(1, 0) * 3, SamplePosition(3, 0)); VERIFY_EQUAL((SamplePosition(6, 0) / SamplePosition(2, 0)), 3); VERIFY_EQUAL(srlztn::ID::FromInt(static_cast(0x87654321u)).AsString(), srlztn::ID("\x21\x43\x65\x87").AsString()); #if defined(MODPLUG_TRACKER) VERIFY_EQUAL(mpt::OS::Wine::Version(U_("1.1.44" )).AsString() , U_("1.1.44")); VERIFY_EQUAL(mpt::OS::Wine::Version(U_("1.6.2" )).AsString() , U_("1.6.2" )); VERIFY_EQUAL(mpt::OS::Wine::Version(U_("1.8" )).AsString() , U_("1.8.0" )); VERIFY_EQUAL(mpt::OS::Wine::Version(U_("2.0-rc" )).AsString() , U_("2.0.0" )); VERIFY_EQUAL(mpt::OS::Wine::Version(U_("2.0-rc4")).AsString() , U_("2.0.0" )); VERIFY_EQUAL(mpt::OS::Wine::Version(U_("2.0" )).AsString() , U_("2.0.0" )); VERIFY_EQUAL(mpt::OS::Wine::Version(U_("2.4" )).AsString() , U_("2.4.0" )); #endif // MODPLUG_TRACKER // date VERIFY_EQUAL( 0, TestDate1( 0, 0, 0, 1, 1, 1970 )); VERIFY_EQUAL( 3600, TestDate1( 0, 0, 1, 1, 1, 1970 )); VERIFY_EQUAL( 86400, TestDate1( 0, 0, 0, 2, 1, 1970 )); VERIFY_EQUAL( 31536000, TestDate1( 0, 0, 0, 1, 1, 1971 )); VERIFY_EQUAL( 100000000, TestDate1( 40, 46, 9, 3, 3, 1973 )); VERIFY_EQUAL( 951782400, TestDate1( 0, 0, 0, 29, 2, 2000 )); VERIFY_EQUAL( 1000000000, TestDate1( 40, 46, 1, 9, 9, 2001 )); VERIFY_EQUAL( 1044057600, TestDate1( 0, 0, 0, 1, 2, 2003 )); VERIFY_EQUAL( 1044144000, TestDate1( 0, 0, 0, 2, 2, 2003 )); VERIFY_EQUAL( 1046476800, TestDate1( 0, 0, 0, 1, 3, 2003 )); VERIFY_EQUAL( 1064966400, TestDate1( 0, 0, 0, 1, 10, 2003 )); VERIFY_EQUAL( 1077926399, TestDate1( 59, 59, 23, 27, 2, 2004 )); VERIFY_EQUAL( 1077926400, TestDate1( 0, 0, 0, 28, 2, 2004 )); VERIFY_EQUAL( 1077926410, TestDate1( 10, 0, 0, 28, 2, 2004 )); VERIFY_EQUAL( 1078012799, TestDate1( 59, 59, 23, 28, 2, 2004 )); VERIFY_EQUAL( 1078012800, TestDate1( 0, 0, 0, 29, 2, 2004 )); VERIFY_EQUAL( 1078012820, TestDate1( 20, 0, 0, 29, 2, 2004 )); VERIFY_EQUAL( 1078099199, TestDate1( 59, 59, 23, 29, 2, 2004 )); VERIFY_EQUAL( 1078099200, TestDate1( 0, 0, 0, 1, 3, 2004 )); VERIFY_EQUAL( 1078099230, TestDate1( 30, 0, 0, 1, 3, 2004 )); VERIFY_EQUAL( 1078185599, TestDate1( 59, 59, 23, 1, 3, 2004 )); VERIFY_EQUAL( 1096588800, TestDate1( 0, 0, 0, 1, 10, 2004 )); VERIFY_EQUAL( 1413064016, TestDate1( 56, 46, 21, 11, 10, 2014 )); VERIFY_EQUAL( 1413064100, TestDate1( 20, 48, 21, 11, 10, 2014 )); VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 0).AsUTC()), TestDate2( 0, 0, 0, 1, 1, 1970 )); VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 3600).AsUTC()), TestDate2( 0, 0, 1, 1, 1, 1970 )); VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 86400).AsUTC()), TestDate2( 0, 0, 0, 2, 1, 1970 )); VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 31536000).AsUTC()), TestDate2( 0, 0, 0, 1, 1, 1971 )); VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 100000000).AsUTC()), TestDate2( 40, 46, 9, 3, 3, 1973 )); VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 951782400).AsUTC()), TestDate2( 0, 0, 0, 29, 2, 2000 )); VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1000000000).AsUTC()), TestDate2( 40, 46, 1, 9, 9, 2001 )); VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1044057600).AsUTC()), TestDate2( 0, 0, 0, 1, 2, 2003 )); VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1044144000).AsUTC()), TestDate2( 0, 0, 0, 2, 2, 2003 )); VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1046476800).AsUTC()), TestDate2( 0, 0, 0, 1, 3, 2003 )); VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1064966400).AsUTC()), TestDate2( 0, 0, 0, 1, 10, 2003 )); VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1077926399).AsUTC()), TestDate2( 59, 59, 23, 27, 2, 2004 )); VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1077926400).AsUTC()), TestDate2( 0, 0, 0, 28, 2, 2004 )); VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1077926410).AsUTC()), TestDate2( 10, 0, 0, 28, 2, 2004 )); VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1078012799).AsUTC()), TestDate2( 59, 59, 23, 28, 2, 2004 )); VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1078012800).AsUTC()), TestDate2( 0, 0, 0, 29, 2, 2004 )); VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1078012820).AsUTC()), TestDate2( 20, 0, 0, 29, 2, 2004 )); VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1078099199).AsUTC()), TestDate2( 59, 59, 23, 29, 2, 2004 )); VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1078099200).AsUTC()), TestDate2( 0, 0, 0, 1, 3, 2004 )); VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1078099230).AsUTC()), TestDate2( 30, 0, 0, 1, 3, 2004 )); VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1078185599).AsUTC()), TestDate2( 59, 59, 23, 1, 3, 2004 )); VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1096588800).AsUTC()), TestDate2( 0, 0, 0, 1, 10, 2004 )); VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1413064016).AsUTC()), TestDate2( 56, 46, 21, 11, 10, 2014 )); VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1413064100).AsUTC()), TestDate2( 20, 48, 21, 11, 10, 2014 )); #ifdef MODPLUG_TRACKER // URI & HTTP { URI uri = ParseURI(U_("scheme://username:password@host:port/path?query#fragment")); VERIFY_EQUAL(uri.scheme, U_("scheme")); VERIFY_EQUAL(uri.username, U_("username")); VERIFY_EQUAL(uri.password, U_("password")); VERIFY_EQUAL(uri.host, U_("host")); VERIFY_EQUAL(uri.port, U_("port")); VERIFY_EQUAL(uri.path, U_("/path")); VERIFY_EQUAL(uri.query, U_("query")); VERIFY_EQUAL(uri.fragment, U_("fragment")); } { URI uri = ParseURI(U_("scheme://host/path")); VERIFY_EQUAL(uri.scheme, U_("scheme")); VERIFY_EQUAL(uri.username, U_("")); VERIFY_EQUAL(uri.password, U_("")); VERIFY_EQUAL(uri.host, U_("host")); VERIFY_EQUAL(uri.port, U_("")); VERIFY_EQUAL(uri.path, U_("/path")); VERIFY_EQUAL(uri.query, U_("")); VERIFY_EQUAL(uri.fragment, U_("")); } { URI uri = ParseURI(U_("scheme://host")); VERIFY_EQUAL(uri.scheme, U_("scheme")); VERIFY_EQUAL(uri.username, U_("")); VERIFY_EQUAL(uri.password, U_("")); VERIFY_EQUAL(uri.host, U_("host")); VERIFY_EQUAL(uri.port, U_("")); VERIFY_EQUAL(uri.path, U_("")); VERIFY_EQUAL(uri.query, U_("")); VERIFY_EQUAL(uri.fragment, U_("")); } { URI uri = ParseURI(U_("scheme://host?query")); VERIFY_EQUAL(uri.scheme, U_("scheme")); VERIFY_EQUAL(uri.username, U_("")); VERIFY_EQUAL(uri.password, U_("")); VERIFY_EQUAL(uri.host, U_("host")); VERIFY_EQUAL(uri.port, U_("")); VERIFY_EQUAL(uri.path, U_("")); VERIFY_EQUAL(uri.query, U_("query")); VERIFY_EQUAL(uri.fragment, U_("")); } { URI uri = ParseURI(U_("scheme://host#fragment")); VERIFY_EQUAL(uri.scheme, U_("scheme")); VERIFY_EQUAL(uri.username, U_("")); VERIFY_EQUAL(uri.password, U_("")); VERIFY_EQUAL(uri.host, U_("host")); VERIFY_EQUAL(uri.port, U_("")); VERIFY_EQUAL(uri.path, U_("")); VERIFY_EQUAL(uri.query, U_("")); VERIFY_EQUAL(uri.fragment, U_("fragment")); } { URI uri = ParseURI(U_("scheme://host?#")); VERIFY_EQUAL(uri.scheme, U_("scheme")); VERIFY_EQUAL(uri.username, U_("")); VERIFY_EQUAL(uri.password, U_("")); VERIFY_EQUAL(uri.host, U_("host")); VERIFY_EQUAL(uri.port, U_("")); VERIFY_EQUAL(uri.path, U_("")); VERIFY_EQUAL(uri.query, U_("")); VERIFY_EQUAL(uri.fragment, U_("")); } { URI uri = ParseURI(U_("scheme://host#?")); VERIFY_EQUAL(uri.scheme, U_("scheme")); VERIFY_EQUAL(uri.username, U_("")); VERIFY_EQUAL(uri.password, U_("")); VERIFY_EQUAL(uri.host, U_("host")); VERIFY_EQUAL(uri.port, U_("")); VERIFY_EQUAL(uri.path, U_("")); VERIFY_EQUAL(uri.query, U_("")); VERIFY_EQUAL(uri.fragment, U_("?")); } { URI uri = ParseURI(U_("scheme://username:password@[2001:db8::1]:port/path?query#fragment")); VERIFY_EQUAL(uri.scheme, U_("scheme")); VERIFY_EQUAL(uri.username, U_("username")); VERIFY_EQUAL(uri.password, U_("password")); VERIFY_EQUAL(uri.host, U_("[2001:db8::1]")); VERIFY_EQUAL(uri.port, U_("port")); VERIFY_EQUAL(uri.path, U_("/path")); VERIFY_EQUAL(uri.query, U_("query")); VERIFY_EQUAL(uri.fragment, U_("fragment")); } { URI uri = ParseURI(U_("https://john.doe@www.example.com:123/forum/questions/?tag=networking&order=newest#top")); VERIFY_EQUAL(uri.scheme, U_("https")); VERIFY_EQUAL(uri.username, U_("john.doe")); VERIFY_EQUAL(uri.password, U_("")); VERIFY_EQUAL(uri.host, U_("www.example.com")); VERIFY_EQUAL(uri.port, U_("123")); VERIFY_EQUAL(uri.path, U_("/forum/questions/")); VERIFY_EQUAL(uri.query, U_("tag=networking&order=newest")); VERIFY_EQUAL(uri.fragment, U_("top")); } { URI uri = ParseURI(U_("ldap://[2001:db8::7]/c=GB?objectClass?one")); VERIFY_EQUAL(uri.scheme, U_("ldap")); VERIFY_EQUAL(uri.username, U_("")); VERIFY_EQUAL(uri.password, U_("")); VERIFY_EQUAL(uri.host, U_("[2001:db8::7]")); VERIFY_EQUAL(uri.port, U_("")); VERIFY_EQUAL(uri.path, U_("/c=GB")); VERIFY_EQUAL(uri.query, U_("objectClass?one")); VERIFY_EQUAL(uri.fragment, U_("")); } { URI uri = ParseURI(U_("mailto:John.Doe@example.com")); VERIFY_EQUAL(uri.scheme, U_("mailto")); VERIFY_EQUAL(uri.username, U_("")); VERIFY_EQUAL(uri.password, U_("")); VERIFY_EQUAL(uri.host, U_("")); VERIFY_EQUAL(uri.port, U_("")); VERIFY_EQUAL(uri.path, U_("John.Doe@example.com")); VERIFY_EQUAL(uri.query, U_("")); VERIFY_EQUAL(uri.fragment, U_("")); } { URI uri = ParseURI(U_("news:comp.infosystems.www.servers.unix")); VERIFY_EQUAL(uri.scheme, U_("news")); VERIFY_EQUAL(uri.username, U_("")); VERIFY_EQUAL(uri.password, U_("")); VERIFY_EQUAL(uri.host, U_("")); VERIFY_EQUAL(uri.port, U_("")); VERIFY_EQUAL(uri.path, U_("comp.infosystems.www.servers.unix")); VERIFY_EQUAL(uri.query, U_("")); VERIFY_EQUAL(uri.fragment, U_("")); } { URI uri = ParseURI(U_("tel:+1-816-555-1212")); VERIFY_EQUAL(uri.scheme, U_("tel")); VERIFY_EQUAL(uri.username, U_("")); VERIFY_EQUAL(uri.password, U_("")); VERIFY_EQUAL(uri.host, U_("")); VERIFY_EQUAL(uri.port, U_("")); VERIFY_EQUAL(uri.path, U_("+1-816-555-1212")); VERIFY_EQUAL(uri.query, U_("")); VERIFY_EQUAL(uri.fragment, U_("")); } { URI uri = ParseURI(U_("telnet://192.0.2.16:80/")); VERIFY_EQUAL(uri.scheme, U_("telnet")); VERIFY_EQUAL(uri.username, U_("")); VERIFY_EQUAL(uri.password, U_("")); VERIFY_EQUAL(uri.host, U_("192.0.2.16")); VERIFY_EQUAL(uri.port, U_("80")); VERIFY_EQUAL(uri.path, U_("/")); VERIFY_EQUAL(uri.query, U_("")); VERIFY_EQUAL(uri.fragment, U_("")); } { URI uri = ParseURI(U_("urn:oasis:names:specification:docbook:dtd:xml:4.1.2")); VERIFY_EQUAL(uri.scheme, U_("urn")); VERIFY_EQUAL(uri.username, U_("")); VERIFY_EQUAL(uri.password, U_("")); VERIFY_EQUAL(uri.host, U_("")); VERIFY_EQUAL(uri.port, U_("")); VERIFY_EQUAL(uri.path, U_("oasis:names:specification:docbook:dtd:xml:4.1.2")); VERIFY_EQUAL(uri.query, U_("")); VERIFY_EQUAL(uri.fragment, U_("")); } { HTTP::Request req; req.SetURI(ParseURI(U_("https://host/path?a1=a&a2=b"))); VERIFY_EQUAL(req.protocol, HTTP::Protocol::HTTPS); VERIFY_EQUAL(req.host, U_("host")); VERIFY_EQUAL(req.path, U_("/path")); VERIFY_EQUAL(req.query.size(), 2u); if(req.query.size() == 2) { VERIFY_EQUAL(req.query[0], std::make_pair(U_("a1"), U_("a"))); VERIFY_EQUAL(req.query[1], std::make_pair(U_("a2"), U_("b"))); } } { HTTP::Request req; req.SetURI(ParseURI(U_("https://host/"))); VERIFY_EQUAL(req.protocol, HTTP::Protocol::HTTPS); VERIFY_EQUAL(req.host, U_("host")); VERIFY_EQUAL(req.path, U_("/")); } #endif // MODPLUG_TRACKER // https://github.com/kripken/emscripten/issues/4251 #if MPT_OS_EMSCRIPTEN volatile int transpose = 32; volatile int finetune = 0; float exp = (transpose * 128.0f + finetune) * (1.0f / (12.0f * 128.0f)); float f = ::powf(2.0f, exp); double d = ::pow (2.0 , (double)exp); VERIFY_EQUAL_EPS(d, 6.349605, 0.00001); VERIFY_EQUAL_EPS(f, 6.349605, 0.00001); #endif } static MPT_NOINLINE void TestRandom() { #ifdef FLAKY_TESTS mpt::default_prng& prng = *s_PRNG; { std::vector hist(256); for(std::size_t i = 0; i < 256*256; ++i) { uint8 value = mpt::random(prng); hist[value] += 1; } for(std::size_t i = 0; i < 256; ++i) { VERIFY_EQUAL_QUIET_NONCONT(mpt::is_in_range(hist[i], 16u, 65520u), true); } } { std::vector hist(256); for(std::size_t i = 0; i < 256*256; ++i) { int8 value = mpt::random(prng); hist[static_cast(value) + 0x80] += 1; } for(std::size_t i = 0; i < 256; ++i) { VERIFY_EQUAL_QUIET_NONCONT(mpt::is_in_range(hist[i], 16u, 65520u), true); } } { std::vector hist(256); for(std::size_t i = 0; i < 256*256; ++i) { uint8 value = mpt::random(prng, 1); hist[value] += 1; } for(std::size_t i = 0; i < 256; ++i) { if(i < 2) { VERIFY_EQUAL_QUIET_NONCONT(mpt::is_in_range(hist[i], 16u, 65520u), true); } else { VERIFY_EQUAL_QUIET_NONCONT(hist[i], 0u); } } } #endif } static MPT_NOINLINE void TestCharsets() { // Path splitting #if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS VERIFY_EQUAL(P_("").GetDrive(), P_("")); VERIFY_EQUAL(P_("").GetDir(), P_("")); VERIFY_EQUAL(P_("").GetPath(), P_("")); VERIFY_EQUAL(P_("").GetFileName(), P_("")); VERIFY_EQUAL(P_("").GetFileExt(), P_("")); VERIFY_EQUAL(P_("").GetFullFileName(), P_("")); VERIFY_EQUAL(P_("C:\\").GetDrive(), P_("C:")); VERIFY_EQUAL(P_("C:\\").GetDir(), P_("\\")); VERIFY_EQUAL(P_("C:\\").GetPath(), P_("C:\\")); VERIFY_EQUAL(P_("C:\\").GetFileName(), P_("")); VERIFY_EQUAL(P_("C:\\").GetFileExt(), P_("")); VERIFY_EQUAL(P_("C:\\").GetFullFileName(), P_("")); VERIFY_EQUAL(P_("\\directory\\").GetDrive(), P_("")); VERIFY_EQUAL(P_("\\directory\\").GetDir(), P_("\\directory\\")); VERIFY_EQUAL(P_("\\directory\\").GetPath(), P_("\\directory\\")); VERIFY_EQUAL(P_("\\directory\\").GetFileName(), P_("")); VERIFY_EQUAL(P_("\\directory\\").GetFileExt(), P_("")); VERIFY_EQUAL(P_("\\directory\\").GetFullFileName(), P_("")); VERIFY_EQUAL(P_("\\directory\\file.txt").GetDrive(), P_("")); VERIFY_EQUAL(P_("\\directory\\file.txt").GetDir(), P_("\\directory\\")); VERIFY_EQUAL(P_("\\directory\\file.txt").GetPath(), P_("\\directory\\")); VERIFY_EQUAL(P_("\\directory\\file.txt").GetFileName(), P_("file")); VERIFY_EQUAL(P_("\\directory\\file.txt").GetFileExt(), P_(".txt")); VERIFY_EQUAL(P_("\\directory\\file.txt").GetFullFileName(), P_("file.txt")); VERIFY_EQUAL(P_(".").GetDrive(), P_("")); VERIFY_EQUAL(P_(".").GetDir(), P_("")); VERIFY_EQUAL(P_(".").GetPath(), P_("")); VERIFY_EQUAL(P_(".").GetFileName(), P_(".")); VERIFY_EQUAL(P_(".").GetFileExt(), P_("")); VERIFY_EQUAL(P_(".").GetFullFileName(), P_(".")); VERIFY_EQUAL(P_("..").GetDrive(), P_("")); VERIFY_EQUAL(P_("..").GetDir(), P_("")); VERIFY_EQUAL(P_("..").GetPath(), P_("")); VERIFY_EQUAL(P_("..").GetFileName(), P_("..")); VERIFY_EQUAL(P_("..").GetFileExt(), P_("")); VERIFY_EQUAL(P_("..").GetFullFileName(), P_("..")); VERIFY_EQUAL(P_("dir\\.").GetDrive(), P_("")); VERIFY_EQUAL(P_("dir\\.").GetDir(), P_("dir\\")); VERIFY_EQUAL(P_("dir\\.").GetPath(), P_("dir\\")); VERIFY_EQUAL(P_("dir\\.").GetFileName(), P_(".")); VERIFY_EQUAL(P_("dir\\.").GetFileExt(), P_("")); VERIFY_EQUAL(P_("dir\\.").GetFullFileName(), P_(".")); VERIFY_EQUAL(P_("dir\\..").GetDrive(), P_("")); VERIFY_EQUAL(P_("dir\\..").GetDir(), P_("dir\\")); VERIFY_EQUAL(P_("dir\\..").GetPath(), P_("dir\\")); VERIFY_EQUAL(P_("dir\\..").GetFileName(), P_("..")); VERIFY_EQUAL(P_("dir\\..").GetFileExt(), P_("")); VERIFY_EQUAL(P_("dir\\..").GetFullFileName(), P_("..")); VERIFY_EQUAL(P_(".txt").GetDrive(), P_("")); VERIFY_EQUAL(P_(".txt").GetDir(), P_("")); VERIFY_EQUAL(P_(".txt").GetPath(), P_("")); VERIFY_EQUAL(P_(".txt").GetFileName(), P_(".txt")); VERIFY_EQUAL(P_(".txt").GetFileExt(), P_("")); VERIFY_EQUAL(P_(".txt").GetFullFileName(), P_(".txt")); VERIFY_EQUAL(P_("C:tmp.txt").GetDrive(), P_("C:")); VERIFY_EQUAL(P_("C:tmp.txt").GetDir(), P_("")); VERIFY_EQUAL(P_("C:tmp.txt").GetPath(), P_("C:")); VERIFY_EQUAL(P_("C:tmp.txt").GetFileName(), P_("tmp")); VERIFY_EQUAL(P_("C:tmp.txt").GetFileExt(), P_(".txt")); VERIFY_EQUAL(P_("C:tmp.txt").GetFullFileName(), P_("tmp.txt")); VERIFY_EQUAL(P_("C:tempdir\\tmp.txt").GetDrive(), P_("C:")); VERIFY_EQUAL(P_("C:tempdir\\tmp.txt").GetDir(), P_("tempdir\\")); VERIFY_EQUAL(P_("C:tempdir\\tmp.txt").GetPath(), P_("C:tempdir\\")); VERIFY_EQUAL(P_("C:tempdir\\tmp.txt").GetFileName(), P_("tmp")); VERIFY_EQUAL(P_("C:tempdir\\tmp.txt").GetFileExt(), P_(".txt")); VERIFY_EQUAL(P_("C:tempdir\\tmp.txt").GetFullFileName(), P_("tmp.txt")); VERIFY_EQUAL(P_("C:\\tempdir\\tmp.txt").GetDrive(), P_("C:")); VERIFY_EQUAL(P_("C:\\tempdir\\tmp.txt").GetDir(), P_("\\tempdir\\")); VERIFY_EQUAL(P_("C:\\tempdir\\tmp.txt").GetPath(), P_("C:\\tempdir\\")); VERIFY_EQUAL(P_("C:\\tempdir\\tmp.txt").GetFileName(), P_("tmp")); VERIFY_EQUAL(P_("C:\\tempdir\\tmp.txt").GetFileExt(), P_(".txt")); VERIFY_EQUAL(P_("C:\\tempdir\\tmp.txt").GetFullFileName(), P_("tmp.txt")); VERIFY_EQUAL(P_("C:\\tempdir\\tmp.foo.txt").GetFileName(), P_("tmp.foo")); VERIFY_EQUAL(P_("C:\\tempdir\\tmp.foo.txt").GetFileExt(), P_(".txt")); VERIFY_EQUAL(P_("\\\\server").GetDrive(), P_("\\\\server")); VERIFY_EQUAL(P_("\\\\server").GetDir(), P_("")); VERIFY_EQUAL(P_("\\\\server").GetPath(), P_("\\\\server")); VERIFY_EQUAL(P_("\\\\server").GetFileName(), P_("")); VERIFY_EQUAL(P_("\\\\server").GetFileExt(), P_("")); VERIFY_EQUAL(P_("\\\\server").GetFullFileName(), P_("")); VERIFY_EQUAL(P_("\\\\server\\").GetDrive(), P_("\\\\server\\")); VERIFY_EQUAL(P_("\\\\server\\").GetDir(), P_("")); VERIFY_EQUAL(P_("\\\\server\\").GetPath(), P_("\\\\server\\")); VERIFY_EQUAL(P_("\\\\server\\").GetFileName(), P_("")); VERIFY_EQUAL(P_("\\\\server\\").GetFileExt(), P_("")); VERIFY_EQUAL(P_("\\\\server\\").GetFullFileName(), P_("")); VERIFY_EQUAL(P_("\\\\server\\share").GetDrive(), P_("\\\\server\\share")); VERIFY_EQUAL(P_("\\\\server\\share").GetDir(), P_("")); VERIFY_EQUAL(P_("\\\\server\\share").GetPath(), P_("\\\\server\\share")); VERIFY_EQUAL(P_("\\\\server\\share").GetFileName(), P_("")); VERIFY_EQUAL(P_("\\\\server\\share").GetFileExt(), P_("")); VERIFY_EQUAL(P_("\\\\server\\share").GetFullFileName(), P_("")); VERIFY_EQUAL(P_("\\\\server\\share\\").GetDrive(), P_("\\\\server\\share")); VERIFY_EQUAL(P_("\\\\server\\share\\").GetDir(), P_("\\")); VERIFY_EQUAL(P_("\\\\server\\share\\").GetPath(), P_("\\\\server\\share\\")); VERIFY_EQUAL(P_("\\\\server\\share\\").GetFileName(), P_("")); VERIFY_EQUAL(P_("\\\\server\\share\\").GetFileExt(), P_("")); VERIFY_EQUAL(P_("\\\\server\\share\\").GetFullFileName(), P_("")); VERIFY_EQUAL(P_("\\\\server\\share\\dir1\\dir2\\name.foo.ext").GetDrive(), P_("\\\\server\\share")); VERIFY_EQUAL(P_("\\\\server\\share\\dir1\\dir2\\name.foo.ext").GetDir(), P_("\\dir1\\dir2\\")); VERIFY_EQUAL(P_("\\\\server\\share\\dir1\\dir2\\name.foo.ext").GetPath(), P_("\\\\server\\share\\dir1\\dir2\\")); VERIFY_EQUAL(P_("\\\\server\\share\\dir1\\dir2\\name.foo.ext").GetFileName(), P_("name.foo")); VERIFY_EQUAL(P_("\\\\server\\share\\dir1\\dir2\\name.foo.ext").GetFileExt(), P_(".ext")); VERIFY_EQUAL(P_("\\\\server\\share\\dir1\\dir2\\name.foo.ext").GetFullFileName(), P_("name.foo.ext")); VERIFY_EQUAL(P_("\\\\?\\C:\\tempdir\\dir.2\\tmp.foo.txt").GetDrive(), P_("C:")); VERIFY_EQUAL(P_("\\\\?\\C:\\tempdir\\dir.2\\tmp.foo.txt").GetDir(), P_("\\tempdir\\dir.2\\")); VERIFY_EQUAL(P_("\\\\?\\C:\\tempdir\\dir.2\\tmp.foo.txt").GetPath(), P_("C:\\tempdir\\dir.2\\")); VERIFY_EQUAL(P_("\\\\?\\C:\\tempdir\\dir.2\\tmp.foo.txt").GetFileName(), P_("tmp.foo")); VERIFY_EQUAL(P_("\\\\?\\C:\\tempdir\\dir.2\\tmp.foo.txt").GetFileExt(), P_(".txt")); VERIFY_EQUAL(P_("\\\\?\\C:\\tempdir\\dir.2\\tmp.foo.txt").GetFullFileName(), P_("tmp.foo.txt")); VERIFY_EQUAL(P_("\\\\?\\UNC\\server\\share\\dir1\\dir2\\name.foo.ext").GetDrive(), P_("\\\\server\\share")); VERIFY_EQUAL(P_("\\\\?\\UNC\\server\\share\\dir1\\dir2\\name.foo.ext").GetDir(), P_("\\dir1\\dir2\\")); VERIFY_EQUAL(P_("\\\\?\\UNC\\server\\share\\dir1\\dir2\\name.foo.ext").GetPath(), P_("\\\\server\\share\\dir1\\dir2\\")); VERIFY_EQUAL(P_("\\\\?\\UNC\\server\\share\\dir1\\dir2\\name.foo.ext").GetFileName(), P_("name.foo")); VERIFY_EQUAL(P_("\\\\?\\UNC\\server\\share\\dir1\\dir2\\name.foo.ext").GetFileExt(), P_(".ext")); VERIFY_EQUAL(P_("\\\\?\\UNC\\server\\share\\dir1\\dir2\\name.foo.ext").GetFullFileName(), P_("name.foo.ext")); #endif // Path conversions #ifdef MODPLUG_TRACKER const mpt::PathString exePath = P_("C:\\OpenMPT\\"); VERIFY_EQUAL(P_("C:\\OpenMPT\\").AbsolutePathToRelative(exePath), P_(".\\")); VERIFY_EQUAL(P_("c:\\OpenMPT\\foo").AbsolutePathToRelative(exePath), P_(".\\foo")); VERIFY_EQUAL(P_("C:\\foo").AbsolutePathToRelative(exePath), P_("\\foo")); VERIFY_EQUAL(P_(".\\").RelativePathToAbsolute(exePath), P_("C:\\OpenMPT\\")); VERIFY_EQUAL(P_(".\\foo").RelativePathToAbsolute(exePath), P_("C:\\OpenMPT\\foo")); VERIFY_EQUAL(P_("\\foo").RelativePathToAbsolute(exePath), P_("C:\\foo")); VERIFY_EQUAL(P_("\\\\server\\path\\file").AbsolutePathToRelative(exePath), P_("\\\\server\\path\\file")); VERIFY_EQUAL(P_("\\\\server\\path\\file").RelativePathToAbsolute(exePath), P_("\\\\server\\path\\file")); VERIFY_EQUAL(P_("").Simplify(), P_("")); VERIFY_EQUAL(P_(" ").Simplify(), P_(" ")); VERIFY_EQUAL(P_("foo\\bar").Simplify(), P_("foo\\bar")); VERIFY_EQUAL(P_(".\\foo\\bar").Simplify(), P_(".\\foo\\bar")); VERIFY_EQUAL(P_(".\\\\foo\\bar").Simplify(), P_(".\\foo\\bar")); VERIFY_EQUAL(P_("./\\foo\\bar").Simplify(), P_(".\\foo\\bar")); VERIFY_EQUAL(P_("\\foo\\bar").Simplify(), P_("\\foo\\bar")); VERIFY_EQUAL(P_("A:\\name_1\\.\\name_2\\..\\name_3\\").Simplify(), P_("A:\\name_1\\name_3")); VERIFY_EQUAL(P_("A:\\name_1\\..\\name_2\\./name_3").Simplify(), P_("A:\\name_2\\name_3")); VERIFY_EQUAL(P_("A:\\name_1\\.\\name_2\\.\\name_3\\..\\name_4\\..").Simplify(), P_("A:\\name_1\\name_2")); VERIFY_EQUAL(P_("A:foo\\\\bar").Simplify(), P_("A:\\foo\\bar")); VERIFY_EQUAL(P_("C:\\..").Simplify(), P_("C:\\")); VERIFY_EQUAL(P_("C:\\.").Simplify(), P_("C:\\")); VERIFY_EQUAL(P_("\\\\foo\\..\\.bar").Simplify(), P_("\\\\.bar")); VERIFY_EQUAL(P_("\\\\foo\\..\\..\\bar").Simplify(), P_("\\\\bar")); #endif #ifdef MODPLUG_TRACKER #if MPT_COMPILER_MSVC // Tracker code currently relies on BROKEN MSVC behaviour with respect to %s // handling in printf functions when the target is a wide string. // Ensure that Microsoft KEEPS this BROKEN behavour (it conflicts with C99 and // C++11 demanded semantics. // Additionally, the tracker code makes use of %hs and %ls which are the only // way to unconditionally expect narrow or wide string parameters // respectively; %ls is standard conforming, %hs is a Microsoft extension. // We check all possible combinations and expect Microsoft-style behaviour // here. char src_char[256]; wchar_t src_wchar_t[256]; TCHAR src_TCHAR[256]; char dst_char[256]; wchar_t dst_wchar_t[256]; TCHAR dst_TCHAR[256]; MemsetZero(src_char); MemsetZero(src_wchar_t); MemsetZero(src_TCHAR); strcpy(src_char, "ab"); wcscpy(src_wchar_t, L"ab"); _tcscpy(src_TCHAR, _T("ab")); #define MPT_TEST_PRINTF(dst_type, function, format, src_type) \ do { \ MemsetZero(dst_ ## dst_type); \ function(dst_ ## dst_type, format, src_ ## src_type); \ VERIFY_EQUAL(std::char_traits< dst_type >::compare(dst_ ## dst_type, src_ ## dst_type, std::char_traits< dst_type >::length( src_ ## dst_type ) + 1), 0); \ } while(0) \ /**/ #define MPT_TEST_PRINTF_N(dst_type, function, format, src_type) \ do { \ MemsetZero(dst_ ## dst_type); \ function(dst_ ## dst_type, 255, format, src_ ## src_type); \ VERIFY_EQUAL(std::char_traits< dst_type >::compare(dst_ ## dst_type, src_ ## dst_type, std::char_traits< dst_type >::length( src_ ## dst_type ) + 1), 0); \ } while(0) \ /**/ // CRT narrow MPT_TEST_PRINTF(char, sprintf, "%s", char); MPT_TEST_PRINTF(char, sprintf, "%S", wchar_t); MPT_TEST_PRINTF(char, sprintf, "%hs", char); MPT_TEST_PRINTF(char, sprintf, "%hS", char); MPT_TEST_PRINTF(char, sprintf, "%ls", wchar_t); MPT_TEST_PRINTF(char, sprintf, "%lS", wchar_t); MPT_TEST_PRINTF(char, sprintf, "%ws", wchar_t); MPT_TEST_PRINTF(char, sprintf, "%wS", wchar_t); // CRT wide MPT_TEST_PRINTF_N(wchar_t, swprintf, L"%s", wchar_t); MPT_TEST_PRINTF_N(wchar_t, swprintf, L"%S", char); MPT_TEST_PRINTF_N(wchar_t, swprintf, L"%hs", char); MPT_TEST_PRINTF_N(wchar_t, swprintf, L"%hS", char); MPT_TEST_PRINTF_N(wchar_t, swprintf, L"%ls", wchar_t); MPT_TEST_PRINTF_N(wchar_t, swprintf, L"%lS", wchar_t); MPT_TEST_PRINTF_N(wchar_t, swprintf, L"%ws", wchar_t); MPT_TEST_PRINTF_N(wchar_t, swprintf, L"%wS", wchar_t); // WinAPI TCHAR MPT_TEST_PRINTF(TCHAR, wsprintf, _T("%s"), TCHAR); MPT_TEST_PRINTF(TCHAR, wsprintf, _T("%hs"), char); MPT_TEST_PRINTF(TCHAR, wsprintf, _T("%hS"), char); MPT_TEST_PRINTF(TCHAR, wsprintf, _T("%ls"), wchar_t); MPT_TEST_PRINTF(TCHAR, wsprintf, _T("%lS"), wchar_t); // WinAPI CHAR MPT_TEST_PRINTF(char, wsprintfA, "%s", char); MPT_TEST_PRINTF(char, wsprintfA, "%S", wchar_t); MPT_TEST_PRINTF(char, wsprintfA, "%hs", char); MPT_TEST_PRINTF(char, wsprintfA, "%hS", char); MPT_TEST_PRINTF(char, wsprintfA, "%ls", wchar_t); MPT_TEST_PRINTF(char, wsprintfA, "%lS", wchar_t); // WinAPI WCHAR MPT_TEST_PRINTF(wchar_t, wsprintfW, L"%s", wchar_t); MPT_TEST_PRINTF(wchar_t, wsprintfW, L"%S", char); MPT_TEST_PRINTF(wchar_t, wsprintfW, L"%hs", char); MPT_TEST_PRINTF(wchar_t, wsprintfW, L"%hS", char); MPT_TEST_PRINTF(wchar_t, wsprintfW, L"%ls", wchar_t); MPT_TEST_PRINTF(wchar_t, wsprintfW, L"%lS", wchar_t); #undef MPT_TEST_PRINTF #undef MPT_TEST_PRINTF_n #endif #endif VERIFY_EQUAL(mpt::CompareNoCaseAscii("f", "f", 6) == 0, true); VERIFY_EQUAL(mpt::CompareNoCaseAscii("f", "F", 6) == 0, true); VERIFY_EQUAL(mpt::CompareNoCaseAscii("F", "f", 6) == 0, true); VERIFY_EQUAL(mpt::CompareNoCaseAscii("f", "g", 6) < 0, true); VERIFY_EQUAL(mpt::CompareNoCaseAscii("h", "g", 6) > 0, true); VERIFY_EQUAL(mpt::CompareNoCaseAscii("fgh", "FgH", 6) == 0, true); VERIFY_EQUAL(mpt::CompareNoCaseAscii("012345678", "012345678", 9) == 0, true); VERIFY_EQUAL(mpt::CompareNoCaseAscii("", "012345678", 9) < 0, true); VERIFY_EQUAL(mpt::CompareNoCaseAscii("FgH", "", 6) > 0, true); VERIFY_EQUAL(mpt::CompareNoCaseAscii("FgH", "F", 6) > 0, true); VERIFY_EQUAL(mpt::CompareNoCaseAscii("FgH", "Fg", 6) > 0, true); VERIFY_EQUAL(mpt::CompareNoCaseAscii("FgH", "fg", 6) > 0, true); VERIFY_EQUAL(mpt::CompareNoCaseAscii("0123456789", "FgH", 0) == 0, true); VERIFY_EQUAL(mpt::CompareNoCaseAscii("FgH", "fgh", 1) == 0, true); VERIFY_EQUAL(mpt::CompareNoCaseAscii("FgH", "fgh", 2) == 0, true); VERIFY_EQUAL(mpt::CompareNoCaseAscii("FgH", "fgh", 3) == 0, true); VERIFY_EQUAL(mpt::CompareNoCaseAscii("FgH", "fghi", 3) == 0, true); VERIFY_EQUAL(mpt::CompareNoCaseAscii("FgH", "fghi", 4) < 0, true); VERIFY_EQUAL(mpt::CompareNoCaseAscii("FIH", "fghi", 1) == 0, true); VERIFY_EQUAL(mpt::CompareNoCaseAscii("FIH", "fghi", 2) > 0, true); } #ifdef MODPLUG_TRACKER struct CustomSettingsTestType { float x; float y; CustomSettingsTestType(float x_ = 0.0f, float y_ = 0.0f) : x(x_), y(y_) { } }; } // namespace Test template <> inline Test::CustomSettingsTestType FromSettingValue(const SettingValue &val) { MPT_ASSERT(val.GetTypeTag() == "myType"); mpt::ustring xy = val.as(); if(xy.empty()) { return Test::CustomSettingsTestType(0.0f, 0.0f); } std::size_t pos = xy.find(U_("|")); mpt::ustring x = xy.substr(0, pos); mpt::ustring y = xy.substr(pos + 1); return Test::CustomSettingsTestType(ConvertStrTo(x), ConvertStrTo(y)); } template <> inline SettingValue ToSettingValue(const Test::CustomSettingsTestType &val) { return SettingValue(mpt::ufmt::val(val.x) + U_("|") + mpt::ufmt::val(val.y), "myType"); } namespace Test { #endif // MODPLUG_TRACKER static MPT_NOINLINE void TestSettings() { #ifdef MODPLUG_TRACKER VERIFY_EQUAL(SettingPath(U_("a"),U_("b")) < SettingPath(U_("a"),U_("c")), true); VERIFY_EQUAL(!(SettingPath(U_("c"),U_("b")) < SettingPath(U_("a"),U_("c"))), true); { DefaultSettingsContainer conf; int32 foobar = conf.Read(U_("Test"), U_("bar"), 23); conf.Write(U_("Test"), U_("bar"), 64); conf.Write(U_("Test"), U_("bar"), 42); conf.Read(U_("Test"), U_("baz"), 4711); foobar = conf.Read(U_("Test"), U_("bar"), 28); } { DefaultSettingsContainer conf; int32 foobar = conf.Read(U_("Test"), U_("bar"), 28); VERIFY_EQUAL(foobar, 42); conf.Write(U_("Test"), U_("bar"), 43); } { DefaultSettingsContainer conf; int32 foobar = conf.Read(U_("Test"), U_("bar"), 123); VERIFY_EQUAL(foobar, 43); conf.Write(U_("Test"), U_("bar"), 88); } { DefaultSettingsContainer conf; Setting foo(conf, U_("Test"), U_("bar"), 99); VERIFY_EQUAL(foo, 88); foo = 7; } { DefaultSettingsContainer conf; Setting foo(conf, U_("Test"), U_("bar"), 99); VERIFY_EQUAL(foo, 7); } { DefaultSettingsContainer conf; conf.Read(U_("Test"), U_("struct"), std::string("")); conf.Write(U_("Test"), U_("struct"), std::string("")); } { DefaultSettingsContainer conf; CustomSettingsTestType dummy = conf.Read(U_("Test"), U_("struct"), CustomSettingsTestType(1.0f, 1.0f)); dummy = CustomSettingsTestType(0.125f, 32.0f); conf.Write(U_("Test"), U_("struct"), dummy); } { DefaultSettingsContainer conf; Setting dummyVar(conf, U_("Test"), U_("struct"), CustomSettingsTestType(1.0f, 1.0f)); CustomSettingsTestType dummy = dummyVar; VERIFY_EQUAL(dummy.x, 0.125f); VERIFY_EQUAL(dummy.y, 32.0f); } #endif // MODPLUG_TRACKER } // Test MIDI Event generating / reading static MPT_NOINLINE void TestMIDIEvents() { uint32 midiEvent; midiEvent = MIDIEvents::CC(MIDIEvents::MIDICC_Balance_Coarse, 13, 40); VERIFY_EQUAL_NONCONT(MIDIEvents::GetTypeFromEvent(midiEvent), MIDIEvents::evControllerChange); VERIFY_EQUAL_NONCONT(MIDIEvents::GetChannelFromEvent(midiEvent), 13); VERIFY_EQUAL_NONCONT(MIDIEvents::GetDataByte1FromEvent(midiEvent), MIDIEvents::MIDICC_Balance_Coarse); VERIFY_EQUAL_NONCONT(MIDIEvents::GetDataByte2FromEvent(midiEvent), 40); midiEvent = MIDIEvents::NoteOn(10, 50, 120); VERIFY_EQUAL_NONCONT(MIDIEvents::GetTypeFromEvent(midiEvent), MIDIEvents::evNoteOn); VERIFY_EQUAL_NONCONT(MIDIEvents::GetChannelFromEvent(midiEvent), 10); VERIFY_EQUAL_NONCONT(MIDIEvents::GetDataByte1FromEvent(midiEvent), 50); VERIFY_EQUAL_NONCONT(MIDIEvents::GetDataByte2FromEvent(midiEvent), 120); midiEvent = MIDIEvents::NoteOff(15, 127, 42); VERIFY_EQUAL_NONCONT(MIDIEvents::GetTypeFromEvent(midiEvent), MIDIEvents::evNoteOff); VERIFY_EQUAL_NONCONT(MIDIEvents::GetChannelFromEvent(midiEvent), 15); VERIFY_EQUAL_NONCONT(MIDIEvents::GetDataByte1FromEvent(midiEvent), 127); VERIFY_EQUAL_NONCONT(MIDIEvents::GetDataByte2FromEvent(midiEvent), 42); midiEvent = MIDIEvents::ProgramChange(1, 127); VERIFY_EQUAL_NONCONT(MIDIEvents::GetTypeFromEvent(midiEvent), MIDIEvents::evProgramChange); VERIFY_EQUAL_NONCONT(MIDIEvents::GetChannelFromEvent(midiEvent), 1); VERIFY_EQUAL_NONCONT(MIDIEvents::GetDataByte1FromEvent(midiEvent), 127); VERIFY_EQUAL_NONCONT(MIDIEvents::GetDataByte2FromEvent(midiEvent), 0); midiEvent = MIDIEvents::PitchBend(2, MIDIEvents::pitchBendCentre); VERIFY_EQUAL_NONCONT(MIDIEvents::GetTypeFromEvent(midiEvent), MIDIEvents::evPitchBend); VERIFY_EQUAL_NONCONT(MIDIEvents::GetChannelFromEvent(midiEvent), 2); VERIFY_EQUAL_NONCONT(MIDIEvents::GetDataByte1FromEvent(midiEvent), 0x00); VERIFY_EQUAL_NONCONT(MIDIEvents::GetDataByte2FromEvent(midiEvent), 0x40); midiEvent = MIDIEvents::System(MIDIEvents::sysStart); VERIFY_EQUAL_NONCONT(MIDIEvents::GetTypeFromEvent(midiEvent), MIDIEvents::evSystem); VERIFY_EQUAL_NONCONT(MIDIEvents::GetChannelFromEvent(midiEvent), MIDIEvents::sysStart); VERIFY_EQUAL_NONCONT(MIDIEvents::GetDataByte1FromEvent(midiEvent), 0); VERIFY_EQUAL_NONCONT(MIDIEvents::GetDataByte2FromEvent(midiEvent), 0); } // Check if our test file was loaded correctly. static void TestLoadXMFile(const CSoundFile &sndFile) { #ifdef MODPLUG_TRACKER const CModDoc *pModDoc = sndFile.GetpModDoc(); VERIFY_EQUAL_NONCONT(pModDoc->IsChannelUnused(0), true); VERIFY_EQUAL_NONCONT(pModDoc->IsChannelUnused(1), false); #endif // MODPLUG_TRACKER // Global Variables VERIFY_EQUAL_NONCONT(sndFile.GetTitle(), "Test Module"); VERIFY_EQUAL_NONCONT(sndFile.m_songMessage.substr(0, 32), "OpenMPT Module Loader Test Suite"); VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultTempo, TEMPO(139, 0)); VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultSpeed, 5); VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultGlobalVolume, 128); VERIFY_EQUAL_NONCONT(sndFile.m_nVSTiVolume, 42); VERIFY_EQUAL_NONCONT(sndFile.m_nSamplePreAmp, 23); VERIFY_EQUAL_NONCONT((sndFile.m_SongFlags & SONG_FILE_FLAGS), SONG_LINEARSLIDES | SONG_EXFILTERRANGE); VERIFY_EQUAL_NONCONT(sndFile.m_playBehaviour[MSF_COMPATIBLE_PLAY], true); VERIFY_EQUAL_NONCONT(sndFile.m_playBehaviour[kMIDICCBugEmulation], false); VERIFY_EQUAL_NONCONT(sndFile.m_playBehaviour[kMPTOldSwingBehaviour], false); VERIFY_EQUAL_NONCONT(sndFile.m_playBehaviour[kOldMIDIPitchBends], false); VERIFY_EQUAL_NONCONT(sndFile.GetMixLevels(), MixLevels::Compatible); VERIFY_EQUAL_NONCONT(sndFile.m_nTempoMode, TempoMode::Modern); VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultRowsPerBeat, 6); VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultRowsPerMeasure, 12); VERIFY_EQUAL_NONCONT(sndFile.m_dwCreatedWithVersion, MPT_V("1.19.02.05")); VERIFY_EQUAL_NONCONT(sndFile.Order().GetRestartPos(), 1); // Macros VERIFY_EQUAL_NONCONT(sndFile.m_MidiCfg.GetParameteredMacroType(0), kSFxReso); VERIFY_EQUAL_NONCONT(sndFile.m_MidiCfg.GetParameteredMacroType(1), kSFxDryWet); VERIFY_EQUAL_NONCONT(sndFile.m_MidiCfg.GetFixedMacroType(), kZxxResoFltMode); // Channels VERIFY_EQUAL_NONCONT(sndFile.GetNumChannels(), 2); VERIFY_EQUAL_NONCONT((sndFile.ChnSettings[0].szName == "First Channel"), true); #ifndef NO_PLUGINS VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[0].nMixPlugin, 0); #endif // NO_PLUGINS VERIFY_EQUAL_NONCONT((sndFile.ChnSettings[1].szName == "Second Channel"), true); #ifndef NO_PLUGINS VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[1].nMixPlugin, 1); #endif // NO_PLUGINS // Samples VERIFY_EQUAL_NONCONT(sndFile.GetNumSamples(), 3); VERIFY_EQUAL_NONCONT((sndFile.m_szNames[1] == "Pulse Sample"), true); VERIFY_EQUAL_NONCONT((sndFile.m_szNames[2] == "Empty Sample"), true); VERIFY_EQUAL_NONCONT((sndFile.m_szNames[3] == "Unassigned Sample"), true); #ifdef MODPLUG_TRACKER VERIFY_EQUAL_NONCONT(pModDoc->FindSampleParent(1), 1); VERIFY_EQUAL_NONCONT(pModDoc->FindSampleParent(2), 1); VERIFY_EQUAL_NONCONT(pModDoc->FindSampleParent(3), INSTRUMENTINDEX_INVALID); #endif // MODPLUG_TRACKER const ModSample &sample = sndFile.GetSample(1); VERIFY_EQUAL_NONCONT(sample.GetBytesPerSample(), 1); VERIFY_EQUAL_NONCONT(sample.GetNumChannels(), 1); VERIFY_EQUAL_NONCONT(sample.GetElementarySampleSize(), 1); VERIFY_EQUAL_NONCONT(sample.GetSampleSizeInBytes(), 16); VERIFY_EQUAL_NONCONT(sample.nFineTune, 35); VERIFY_EQUAL_NONCONT(sample.RelativeTone, 1); VERIFY_EQUAL_NONCONT(sample.nVolume, 32 * 4); VERIFY_EQUAL_NONCONT(sample.nGlobalVol, 64); VERIFY_EQUAL_NONCONT(sample.nPan, 160); VERIFY_EQUAL_NONCONT(sample.uFlags, CHN_PANNING | CHN_LOOP | CHN_PINGPONGLOOP); VERIFY_EQUAL_NONCONT(sample.nLoopStart, 1); VERIFY_EQUAL_NONCONT(sample.nLoopEnd, 8); VERIFY_EQUAL_NONCONT(sample.nVibType, VIB_SQUARE); VERIFY_EQUAL_NONCONT(sample.nVibSweep, 3); VERIFY_EQUAL_NONCONT(sample.nVibRate, 4); VERIFY_EQUAL_NONCONT(sample.nVibDepth, 5); // Sample Data for(size_t i = 0; i < 6; i++) { VERIFY_EQUAL_NONCONT(sample.sample8()[i], 18); } for(size_t i = 6; i < 16; i++) { VERIFY_EQUAL_NONCONT(sample.sample8()[i], 0); } // Instruments VERIFY_EQUAL_NONCONT(sndFile.GetNumInstruments(), 1); const ModInstrument *pIns = sndFile.Instruments[1]; VERIFY_EQUAL_NONCONT(pIns->nFadeOut, 1024); VERIFY_EQUAL_NONCONT(pIns->nPan, 128); VERIFY_EQUAL_NONCONT(pIns->dwFlags, InstrumentFlags(0)); VERIFY_EQUAL_NONCONT(pIns->nPPS, 0); VERIFY_EQUAL_NONCONT(pIns->nPPC, NOTE_MIDDLEC - 1); VERIFY_EQUAL_NONCONT(pIns->nVolRampUp, 1200); VERIFY_EQUAL_NONCONT(pIns->resampling, SRCMODE_SINC8LP); VERIFY_EQUAL_NONCONT(pIns->IsCutoffEnabled(), false); VERIFY_EQUAL_NONCONT(pIns->GetCutoff(), 0); VERIFY_EQUAL_NONCONT(pIns->IsResonanceEnabled(), false); VERIFY_EQUAL_NONCONT(pIns->GetResonance(), 0); VERIFY_EQUAL_NONCONT(pIns->filterMode, FilterMode::Unchanged); VERIFY_EQUAL_NONCONT(pIns->nVolSwing, 0); VERIFY_EQUAL_NONCONT(pIns->nPanSwing, 0); VERIFY_EQUAL_NONCONT(pIns->nCutSwing, 0); VERIFY_EQUAL_NONCONT(pIns->nResSwing, 0); VERIFY_EQUAL_NONCONT(pIns->nNNA, NewNoteAction::NoteCut); VERIFY_EQUAL_NONCONT(pIns->nDCT, DuplicateCheckType::None); VERIFY_EQUAL_NONCONT(pIns->nMixPlug, 1); VERIFY_EQUAL_NONCONT(pIns->nMidiChannel, 16); VERIFY_EQUAL_NONCONT(pIns->nMidiProgram, 64); VERIFY_EQUAL_NONCONT(pIns->wMidiBank, 2); VERIFY_EQUAL_NONCONT(pIns->midiPWD, 8); VERIFY_EQUAL_NONCONT(pIns->pTuning, sndFile.GetDefaultTuning()); VERIFY_EQUAL_NONCONT(pIns->pitchToTempoLock, TEMPO(0, 0)); VERIFY_EQUAL_NONCONT(pIns->pluginVelocityHandling, PLUGIN_VELOCITYHANDLING_VOLUME); VERIFY_EQUAL_NONCONT(pIns->pluginVolumeHandling, PLUGIN_VOLUMEHANDLING_MIDI); for(size_t i = sndFile.GetModSpecifications().noteMin; i < sndFile.GetModSpecifications().noteMax; i++) { VERIFY_EQUAL_NONCONT(pIns->Keyboard[i], (i == NOTE_MIDDLEC - 1) ? 2 : 1); } VERIFY_EQUAL_NONCONT(pIns->VolEnv.dwFlags, ENV_ENABLED | ENV_SUSTAIN); VERIFY_EQUAL_NONCONT(pIns->VolEnv.size(), 3); VERIFY_EQUAL_NONCONT(pIns->VolEnv.nReleaseNode, ENV_RELEASE_NODE_UNSET); VERIFY_EQUAL_NONCONT(pIns->VolEnv[2].tick, 96); VERIFY_EQUAL_NONCONT(pIns->VolEnv[2].value, 0); VERIFY_EQUAL_NONCONT(pIns->VolEnv.nSustainStart, 1); VERIFY_EQUAL_NONCONT(pIns->VolEnv.nSustainEnd, 1); VERIFY_EQUAL_NONCONT(pIns->PanEnv.dwFlags, ENV_LOOP); VERIFY_EQUAL_NONCONT(pIns->PanEnv.size(), 12); VERIFY_EQUAL_NONCONT(pIns->PanEnv.nLoopStart, 9); VERIFY_EQUAL_NONCONT(pIns->PanEnv.nLoopEnd, 11); VERIFY_EQUAL_NONCONT(pIns->PanEnv.nReleaseNode, ENV_RELEASE_NODE_UNSET); VERIFY_EQUAL_NONCONT(pIns->PanEnv[9].tick, 46); VERIFY_EQUAL_NONCONT(pIns->PanEnv[9].value, 23); VERIFY_EQUAL_NONCONT(pIns->PitchEnv.dwFlags, EnvelopeFlags(0)); VERIFY_EQUAL_NONCONT(pIns->PitchEnv.size(), 0); // Sequences VERIFY_EQUAL_NONCONT(sndFile.Order.GetNumSequences(), 1); VERIFY_EQUAL_NONCONT(sndFile.Order()[0], 0); VERIFY_EQUAL_NONCONT(sndFile.Order()[1], 1); // Patterns VERIFY_EQUAL_NONCONT(sndFile.Patterns.GetNumPatterns(), 2); VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetName(), "First Pattern"); VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetNumRows(), 64); VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetNumChannels(), 2); VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetOverrideSignature(), false); VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetRowsPerBeat(), 0); VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetRowsPerMeasure(), 0); VERIFY_EQUAL_NONCONT(sndFile.Patterns.IsPatternEmpty(0), true); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetName(), "Second Pattern"); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetNumRows(), 32); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetNumChannels(), 2); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetOverrideSignature(), false); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetRowsPerBeat(), 0); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetRowsPerMeasure(), 0); VERIFY_EQUAL_NONCONT(sndFile.Patterns.IsPatternEmpty(1), false); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(0, 0)->IsPcNote(), false); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(0, 0)->note, NOTE_NONE); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(0, 0)->instr, 0); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(0, 0)->volcmd, VOLCMD_VIBRATOSPEED); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(0, 0)->vol, 15); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(31, 0)->IsEmpty(), true); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(31, 1)->IsEmpty(), false); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(31, 1)->IsPcNote(), false); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(31, 1)->note, NOTE_MIDDLEC + 12); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(31, 1)->instr, 45); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(31, 1)->volcmd, VOLCMD_VOLSLIDEDOWN); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(31, 1)->vol, 5); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(31, 1)->command, CMD_PANNING8); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(31, 1)->param, 0xFF); // Test 4-Bit Panning conversion for(int i = 0; i < 16; i++) { VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(10 + i, 0)->vol, i * 4); } // Plugins #ifndef NO_PLUGINS const SNDMIXPLUGIN &plug = sndFile.m_MixPlugins[0]; VERIFY_EQUAL_NONCONT(plug.GetName(), U_("First Plugin")); VERIFY_EQUAL_NONCONT(plug.fDryRatio, 0.26f); VERIFY_EQUAL_NONCONT(plug.IsMasterEffect(), true); VERIFY_EQUAL_NONCONT(plug.GetGain(), 11); #endif // NO_PLUGINS } // Check if our test file was loaded correctly. static void TestLoadMPTMFile(const CSoundFile &sndFile) { // Global Variables VERIFY_EQUAL_NONCONT(sndFile.GetTitle(), "Test Module_____________X"); VERIFY_EQUAL_NONCONT(sndFile.m_songMessage.substr(0, 32), "OpenMPT Module Loader Test Suite"); VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultTempo, TEMPO(139, 999)); VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultSpeed, 5); VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultGlobalVolume, 128); VERIFY_EQUAL_NONCONT(sndFile.m_nVSTiVolume, 42); VERIFY_EQUAL_NONCONT(sndFile.m_nSamplePreAmp, 23); VERIFY_EQUAL_NONCONT((sndFile.m_SongFlags & SONG_FILE_FLAGS), SONG_LINEARSLIDES | SONG_EXFILTERRANGE | SONG_ITCOMPATGXX | SONG_ITOLDEFFECTS); VERIFY_EQUAL_NONCONT(sndFile.m_playBehaviour[MSF_COMPATIBLE_PLAY], true); VERIFY_EQUAL_NONCONT(sndFile.m_playBehaviour[kMIDICCBugEmulation], false); VERIFY_EQUAL_NONCONT(sndFile.m_playBehaviour[kMPTOldSwingBehaviour], false); VERIFY_EQUAL_NONCONT(sndFile.m_playBehaviour[kOldMIDIPitchBends], false); VERIFY_EQUAL_NONCONT(sndFile.GetMixLevels(), MixLevels::Compatible); VERIFY_EQUAL_NONCONT(sndFile.m_nTempoMode, TempoMode::Modern); VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultRowsPerBeat, 6); VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultRowsPerMeasure, 12); VERIFY_EQUAL_NONCONT(sndFile.m_dwCreatedWithVersion, MPT_V("1.19.02.05")); VERIFY_EQUAL_NONCONT(sndFile.m_nResampling, SRCMODE_SINC8LP); VERIFY_EQUAL_NONCONT(sndFile.m_songArtist, U_("Tester")); VERIFY_EQUAL_NONCONT(sndFile.m_tempoSwing.size(), 6); VERIFY_EQUAL_NONCONT(sndFile.m_tempoSwing[0], 29360125); VERIFY_EQUAL_NONCONT(sndFile.m_tempoSwing[1], 4194305); VERIFY_EQUAL_NONCONT(sndFile.m_tempoSwing[2], 29360128); VERIFY_EQUAL_NONCONT(sndFile.m_tempoSwing[3], 4194305); VERIFY_EQUAL_NONCONT(sndFile.m_tempoSwing[4], 29360128); VERIFY_EQUAL_NONCONT(sndFile.m_tempoSwing[5], 4194305); // Edit history VERIFY_EQUAL_NONCONT(sndFile.GetFileHistory().size() > 15, true); const FileHistory &fh = sndFile.GetFileHistory().front(); VERIFY_EQUAL_NONCONT(fh.loadDate.tm_year, 111); VERIFY_EQUAL_NONCONT(fh.loadDate.tm_mon, 5); VERIFY_EQUAL_NONCONT(fh.loadDate.tm_mday, 14); VERIFY_EQUAL_NONCONT(fh.loadDate.tm_hour, 21); VERIFY_EQUAL_NONCONT(fh.loadDate.tm_min, 8); VERIFY_EQUAL_NONCONT(fh.loadDate.tm_sec, 32); VERIFY_EQUAL_NONCONT((uint32)((double)fh.openTime / HISTORY_TIMER_PRECISION), 31); // Macros VERIFY_EQUAL_NONCONT(sndFile.m_MidiCfg.GetParameteredMacroType(0), kSFxReso); VERIFY_EQUAL_NONCONT(sndFile.m_MidiCfg.GetParameteredMacroType(1), kSFxDryWet); VERIFY_EQUAL_NONCONT(sndFile.m_MidiCfg.GetParameteredMacroType(2), kSFxPolyAT); VERIFY_EQUAL_NONCONT(sndFile.m_MidiCfg.GetFixedMacroType(), kZxxResoFltMode); // Channels VERIFY_EQUAL_NONCONT(sndFile.GetNumChannels(), 70); VERIFY_EQUAL_NONCONT((sndFile.ChnSettings[0].szName == "First Channel"), true); VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[0].nPan, 32); VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[0].nVolume, 32); VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[0].dwFlags, CHN_MUTE); #ifndef NO_PLUGINS VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[0].nMixPlugin, 0); #endif // NO_PLUGINS VERIFY_EQUAL_NONCONT((sndFile.ChnSettings[1].szName == "Second Channel"), true); VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[1].nPan, 128); VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[1].nVolume, 16); VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[1].dwFlags, CHN_SURROUND); #ifndef NO_PLUGINS VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[1].nMixPlugin, 1); #endif // NO_PLUGINS VERIFY_EQUAL_NONCONT((sndFile.ChnSettings[69].szName == "Last Channel______X"), true); VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[69].nPan, 256); VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[69].nVolume, 7); VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[69].dwFlags, ChannelFlags(0)); #ifndef NO_PLUGINS VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[69].nMixPlugin, 1); #endif // NO_PLUGINS // Samples VERIFY_EQUAL_NONCONT(sndFile.GetNumSamples(), 4); { const ModSample &sample = sndFile.GetSample(1); VERIFY_EQUAL_NONCONT(sample.GetBytesPerSample(), 1); VERIFY_EQUAL_NONCONT(sample.GetNumChannels(), 1); VERIFY_EQUAL_NONCONT(sample.GetElementarySampleSize(), 1); VERIFY_EQUAL_NONCONT(sample.GetSampleSizeInBytes(), 16); VERIFY_EQUAL_NONCONT(sample.GetSampleRate(MOD_TYPE_MPT), 9001); VERIFY_EQUAL_NONCONT(sample.nVolume, 32 * 4); VERIFY_EQUAL_NONCONT(sample.nGlobalVol, 16); VERIFY_EQUAL_NONCONT(sample.nPan, 160); VERIFY_EQUAL_NONCONT(sample.uFlags, CHN_PANNING | CHN_LOOP | CHN_SUSTAINLOOP | CHN_PINGPONGSUSTAIN); VERIFY_EQUAL_NONCONT(sample.nLoopStart, 1); VERIFY_EQUAL_NONCONT(sample.nLoopEnd, 8); VERIFY_EQUAL_NONCONT(sample.nSustainStart, 1); VERIFY_EQUAL_NONCONT(sample.nSustainEnd, 7); VERIFY_EQUAL_NONCONT(sample.nVibType, VIB_SQUARE); VERIFY_EQUAL_NONCONT(sample.nVibSweep, 3); VERIFY_EQUAL_NONCONT(sample.nVibRate, 4); VERIFY_EQUAL_NONCONT(sample.nVibDepth, 5); // Sample Data for(size_t i = 0; i < 6; i++) { VERIFY_EQUAL_NONCONT(sample.sample8()[i], 18); } for(size_t i = 6; i < 16; i++) { VERIFY_EQUAL_NONCONT(sample.sample8()[i], 0); } VERIFY_EQUAL_NONCONT(sample.cues[0], 2); VERIFY_EQUAL_NONCONT(sample.cues[8], 9); } { const ModSample &sample = sndFile.GetSample(2); VERIFY_EQUAL_NONCONT((sndFile.m_szNames[2] == "Stereo / 16-Bit"), true); VERIFY_EQUAL_NONCONT(sample.GetBytesPerSample(), 4); VERIFY_EQUAL_NONCONT(sample.GetNumChannels(), 2); VERIFY_EQUAL_NONCONT(sample.GetElementarySampleSize(), 2); VERIFY_EQUAL_NONCONT(sample.GetSampleSizeInBytes(), 16 * 4); VERIFY_EQUAL_NONCONT(sample.GetSampleRate(MOD_TYPE_MPT), 16000); VERIFY_EQUAL_NONCONT(sample.uFlags, CHN_16BIT | CHN_STEREO | CHN_LOOP); // Sample Data (Stereo Interleaved) for(size_t i = 0; i < 7; i++) { VERIFY_EQUAL_NONCONT(sample.sample16()[4 + i], int16(-32768)); } VERIFY_EQUAL_NONCONT(sample.cues[0], 3); VERIFY_EQUAL_NONCONT(sample.cues[8], 14); } // External sample { const ModSample &sample = sndFile.GetSample(4); VERIFY_EQUAL_NONCONT((sndFile.m_szNames[4] == "Overridden Name"), true); VERIFY_EQUAL_NONCONT((sample.filename == "External"), true); #ifdef MPT_EXTERNAL_SAMPLES VERIFY_EQUAL_NONCONT(sample.GetBytesPerSample(), 1); VERIFY_EQUAL_NONCONT(sample.GetNumChannels(), 1); VERIFY_EQUAL_NONCONT(sample.GetElementarySampleSize(), 1); VERIFY_EQUAL_NONCONT(sample.GetSampleSizeInBytes(), 64); VERIFY_EQUAL_NONCONT(sample.nLoopStart, 42); VERIFY_EQUAL_NONCONT(sample.nLoopEnd, 55); VERIFY_EQUAL_NONCONT(sample.nSustainStart, 42); VERIFY_EQUAL_NONCONT(sample.nSustainEnd, 55); VERIFY_EQUAL_NONCONT(sample.uFlags, CHN_LOOP | CHN_SUSTAINLOOP | CHN_PINGPONGSUSTAIN | SMP_KEEPONDISK); #endif // MPT_EXTERNAL_SAMPLES VERIFY_EQUAL_NONCONT(sample.GetSampleRate(MOD_TYPE_MPT), 10101); VERIFY_EQUAL_NONCONT(sample.nVolume, 26 * 4); VERIFY_EQUAL_NONCONT(sample.nGlobalVol, 26); VERIFY_EQUAL_NONCONT(sample.nPan, 26 * 4); VERIFY_EQUAL_NONCONT(sample.nVibType, VIB_SINE); VERIFY_EQUAL_NONCONT(sample.nVibSweep, 37); VERIFY_EQUAL_NONCONT(sample.nVibRate, 42); VERIFY_EQUAL_NONCONT(sample.nVibDepth, 23); // Sample Data #ifdef MPT_EXTERNAL_SAMPLES for(size_t i = 0; i < 16; i++) { VERIFY_EQUAL_NONCONT(sample.sample8()[i], int8(45)); } #endif // MPT_EXTERNAL_SAMPLES VERIFY_EQUAL_NONCONT(sample.cues[0], 10); VERIFY_EQUAL_NONCONT(sample.cues[8], 50); } // Instruments VERIFY_EQUAL_NONCONT(sndFile.GetNumInstruments(), 2); for(INSTRUMENTINDEX ins = 1; ins <= 2; ins++) { const ModInstrument *pIns = sndFile.Instruments[ins]; VERIFY_EQUAL_NONCONT(pIns->nGlobalVol, 32); VERIFY_EQUAL_NONCONT(pIns->nFadeOut, 1024); VERIFY_EQUAL_NONCONT(pIns->nPan, 64); VERIFY_EQUAL_NONCONT(pIns->dwFlags, INS_SETPANNING); VERIFY_EQUAL_NONCONT(pIns->nPPS, 8); VERIFY_EQUAL_NONCONT(pIns->nPPC, (NOTE_MIDDLEC - NOTE_MIN) + 6); // F#5 VERIFY_EQUAL_NONCONT(pIns->nVolRampUp, 1200); VERIFY_EQUAL_NONCONT(pIns->resampling, SRCMODE_SINC8LP); VERIFY_EQUAL_NONCONT(pIns->IsCutoffEnabled(), true); VERIFY_EQUAL_NONCONT(pIns->GetCutoff(), 0x32); VERIFY_EQUAL_NONCONT(pIns->IsResonanceEnabled(), true); VERIFY_EQUAL_NONCONT(pIns->GetResonance(), 0x64); VERIFY_EQUAL_NONCONT(pIns->filterMode, FilterMode::HighPass); VERIFY_EQUAL_NONCONT(pIns->nVolSwing, 0x30); VERIFY_EQUAL_NONCONT(pIns->nPanSwing, 0x18); VERIFY_EQUAL_NONCONT(pIns->nCutSwing, 0x0C); VERIFY_EQUAL_NONCONT(pIns->nResSwing, 0x3C); VERIFY_EQUAL_NONCONT(pIns->nNNA, NewNoteAction::Continue); VERIFY_EQUAL_NONCONT(pIns->nDCT, DuplicateCheckType::Note); VERIFY_EQUAL_NONCONT(pIns->nDNA, DuplicateNoteAction::NoteFade); VERIFY_EQUAL_NONCONT(pIns->nMixPlug, 1); VERIFY_EQUAL_NONCONT(pIns->nMidiChannel, 16); VERIFY_EQUAL_NONCONT(pIns->nMidiProgram, 64); VERIFY_EQUAL_NONCONT(pIns->wMidiBank, 2); VERIFY_EQUAL_NONCONT(pIns->midiPWD, ins); VERIFY_EQUAL_NONCONT(pIns->pTuning, sndFile.GetDefaultTuning()); VERIFY_EQUAL_NONCONT(pIns->pitchToTempoLock, TEMPO(130, 2000)); VERIFY_EQUAL_NONCONT(pIns->pluginVelocityHandling, PLUGIN_VELOCITYHANDLING_VOLUME); VERIFY_EQUAL_NONCONT(pIns->pluginVolumeHandling, PLUGIN_VOLUMEHANDLING_MIDI); for(size_t i = 0; i < NOTE_MAX; i++) { VERIFY_EQUAL_NONCONT(pIns->Keyboard[i], (i == NOTE_MIDDLEC - 1) ? 99 : 1); VERIFY_EQUAL_NONCONT(pIns->NoteMap[i], (i == NOTE_MIDDLEC - 1) ? (i + 13) : (i + 1)); } VERIFY_EQUAL_NONCONT(pIns->VolEnv.dwFlags, ENV_ENABLED | ENV_CARRY); VERIFY_EQUAL_NONCONT(pIns->VolEnv.size(), 3); VERIFY_EQUAL_NONCONT(pIns->VolEnv.nReleaseNode, 1); VERIFY_EQUAL_NONCONT(pIns->VolEnv[2].tick, 96); VERIFY_EQUAL_NONCONT(pIns->VolEnv[2].value, 0); VERIFY_EQUAL_NONCONT(pIns->PanEnv.dwFlags, ENV_LOOP); VERIFY_EQUAL_NONCONT(pIns->PanEnv.size(), 76); VERIFY_EQUAL_NONCONT(pIns->PanEnv.nLoopStart, 22); VERIFY_EQUAL_NONCONT(pIns->PanEnv.nLoopEnd, 29); VERIFY_EQUAL_NONCONT(pIns->PanEnv.nReleaseNode, ENV_RELEASE_NODE_UNSET); VERIFY_EQUAL_NONCONT(pIns->PanEnv[75].tick, 427); VERIFY_EQUAL_NONCONT(pIns->PanEnv[75].value, 27); VERIFY_EQUAL_NONCONT(pIns->PitchEnv.dwFlags, ENV_ENABLED | ENV_CARRY | ENV_SUSTAIN | ENV_FILTER); VERIFY_EQUAL_NONCONT(pIns->PitchEnv.size(), 3); VERIFY_EQUAL_NONCONT(pIns->PitchEnv.nSustainStart, 1); VERIFY_EQUAL_NONCONT(pIns->PitchEnv.nSustainEnd, 2); VERIFY_EQUAL_NONCONT(pIns->PitchEnv[1].tick, 96); VERIFY_EQUAL_NONCONT(pIns->PitchEnv[1].value, 64); } // Sequences VERIFY_EQUAL_NONCONT(sndFile.Order.GetNumSequences(), 2); VERIFY_EQUAL_NONCONT(sndFile.Order(0).GetLengthTailTrimmed(), 3); VERIFY_EQUAL_NONCONT(sndFile.Order(0).GetName(), U_("First Sequence")); VERIFY_EQUAL_NONCONT(sndFile.Order(0)[0], sndFile.Order.GetIgnoreIndex()); VERIFY_EQUAL_NONCONT(sndFile.Order(0)[1], 0); VERIFY_EQUAL_NONCONT(sndFile.Order(0)[2], sndFile.Order.GetIgnoreIndex()); VERIFY_EQUAL_NONCONT(sndFile.Order(0).GetRestartPos(), 1); VERIFY_EQUAL_NONCONT(sndFile.Order(1).GetLengthTailTrimmed(), 3); VERIFY_EQUAL_NONCONT(sndFile.Order(1).GetName(), U_("Second Sequence")); VERIFY_EQUAL_NONCONT(sndFile.Order(1)[0], 1); VERIFY_EQUAL_NONCONT(sndFile.Order(1)[1], 2); VERIFY_EQUAL_NONCONT(sndFile.Order(1)[2], 3); VERIFY_EQUAL_NONCONT(sndFile.Order(1).GetRestartPos(), 2); // Patterns VERIFY_EQUAL_NONCONT(sndFile.Patterns.GetNumPatterns(), 2); VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetName(), "First Pattern"); VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetNumRows(), 70); VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetNumChannels(), 70); VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetOverrideSignature(), true); VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetRowsPerBeat(), 5); VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetRowsPerMeasure(), 10); VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].HasTempoSwing(), true); VERIFY_EQUAL_NONCONT(sndFile.Patterns.IsPatternEmpty(0), true); { TempoSwing swing = sndFile.Patterns[0].GetTempoSwing(); VERIFY_EQUAL_NONCONT(swing.size(), 5); VERIFY_EQUAL_NONCONT(swing[0], 16770149); VERIFY_EQUAL_NONCONT(swing[1], 16803696); VERIFY_EQUAL_NONCONT(swing[2], 16770157); VERIFY_EQUAL_NONCONT(swing[3], 29347774); VERIFY_EQUAL_NONCONT(swing[4], 4194304); } VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetName(), "Second Pattern"); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetNumRows(), 32); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetNumChannels(), 70); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetOverrideSignature(), false); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetRowsPerBeat(), 0); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetRowsPerMeasure(), 0); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].HasTempoSwing(), false); VERIFY_EQUAL_NONCONT(sndFile.Patterns.IsPatternEmpty(1), false); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(0, 0)->IsPcNote(), true); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(0, 0)->note, NOTE_PC); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(0, 0)->instr, 99); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(0, 0)->GetValueVolCol(), 1); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(0, 0)->GetValueEffectCol(), 200); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(31, 0)->IsEmpty(), true); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(31, 1)->IsEmpty(), false); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(31, 1)->IsPcNote(), false); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(31, 1)->note, NOTE_MIDDLEC + 12); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(31, 1)->instr, 45); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(31, 1)->volcmd, VOLCMD_VOLSLIDEDOWN); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(31, 1)->vol, 5); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(31, 1)->command, CMD_PANNING8); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(31, 1)->param, 0xFF); // Plugins #ifndef NO_PLUGINS const SNDMIXPLUGIN &plug = sndFile.m_MixPlugins[0]; VERIFY_EQUAL_NONCONT(plug.GetName(), U_("First Plugin")); VERIFY_EQUAL_NONCONT(plug.fDryRatio, 0.26f); VERIFY_EQUAL_NONCONT(plug.IsMasterEffect(), true); VERIFY_EQUAL_NONCONT(plug.GetGain(), 11); VERIFY_EQUAL_NONCONT(plug.pMixPlugin != nullptr, true); if(plug.pMixPlugin) { VERIFY_EQUAL_NONCONT(plug.pMixPlugin->GetParameter(1), 0.5f); VERIFY_EQUAL_NONCONT(plug.pMixPlugin->IsInstrument(), false); } #endif // NO_PLUGINS #ifdef MODPLUG_TRACKER // MIDI Mapping VERIFY_EQUAL_NONCONT(sndFile.GetMIDIMapper().GetCount(), 1); const CMIDIMappingDirective &mapping = sndFile.GetMIDIMapper().GetDirective(0); VERIFY_EQUAL_NONCONT(mapping.GetAllowPatternEdit(), true); VERIFY_EQUAL_NONCONT(mapping.GetCaptureMIDI(), false); VERIFY_EQUAL_NONCONT(mapping.IsActive(), true); VERIFY_EQUAL_NONCONT(mapping.GetAnyChannel(), false); VERIFY_EQUAL_NONCONT(mapping.GetChannel(), 5); VERIFY_EQUAL_NONCONT(mapping.GetPlugIndex(), 1); VERIFY_EQUAL_NONCONT(mapping.GetParamIndex(), 0); VERIFY_EQUAL_NONCONT(mapping.GetEvent(), MIDIEvents::evControllerChange); VERIFY_EQUAL_NONCONT(mapping.GetController(), MIDIEvents::MIDICC_ModulationWheel_Coarse); // Channel colors const auto &chns = sndFile.ChnSettings; VERIFY_EQUAL_NONCONT(chns[0].color, RGB(255, 0, 0)); VERIFY_EQUAL_NONCONT(chns[1].color, RGB(0, 255, 0)); VERIFY_EQUAL_NONCONT(chns[2].color, RGB(0, 0, 255)); VERIFY_EQUAL_NONCONT(chns[3].color, ModChannelSettings::INVALID_COLOR); VERIFY_EQUAL_NONCONT(chns[67].color, RGB(255, 0, 255)); VERIFY_EQUAL_NONCONT(chns[68].color, RGB(255, 255, 0)); VERIFY_EQUAL_NONCONT(chns[69].color, ModChannelSettings::INVALID_COLOR); #endif VERIFY_EQUAL_NONCONT(sndFile.FrequencyToCutOff(sndFile.CutOffToFrequency(0)), 0); VERIFY_EQUAL_NONCONT(sndFile.FrequencyToCutOff(sndFile.CutOffToFrequency(80)), 80); VERIFY_EQUAL_NONCONT(sndFile.FrequencyToCutOff(sndFile.CutOffToFrequency(127)), 127); } // Check if our test file was loaded correctly. static void TestLoadS3MFile(const CSoundFile &sndFile, bool resaved) { // Global Variables VERIFY_EQUAL_NONCONT(sndFile.GetTitle(), "S3M_Test__________________X"); VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultTempo, TEMPO(33, 0)); VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultSpeed, 254); VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultGlobalVolume, 32 * 4); VERIFY_EQUAL_NONCONT(sndFile.m_nVSTiVolume, 36); VERIFY_EQUAL_NONCONT(sndFile.m_nSamplePreAmp, 16); VERIFY_EQUAL_NONCONT((sndFile.m_SongFlags & SONG_FILE_FLAGS), SONG_FASTVOLSLIDES); VERIFY_EQUAL_NONCONT(sndFile.GetMixLevels(), MixLevels::Compatible); VERIFY_EQUAL_NONCONT(sndFile.m_nTempoMode, TempoMode::Classic); VERIFY_EQUAL_NONCONT(sndFile.m_dwLastSavedWithVersion, resaved ? Version::Current() : MPT_V("1.27.00.00")); // Channels VERIFY_EQUAL_NONCONT(sndFile.GetNumChannels(), 4); VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[0].nPan, 0); VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[0].dwFlags, ChannelFlags(0)); VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[1].nPan, 256); VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[1].dwFlags, CHN_MUTE); VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[2].nPan, 85); VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[2].dwFlags, ChannelFlags(0)); VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[3].nPan, 171); VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[3].dwFlags, CHN_MUTE); // Samples VERIFY_EQUAL_NONCONT(sndFile.GetNumSamples(), 4); { const ModSample &sample = sndFile.GetSample(1); VERIFY_EQUAL_NONCONT(sndFile.m_szNames[1], "Sample_1__________________X"); VERIFY_EQUAL_NONCONT(sample.filename, "Filename_1_X"); VERIFY_EQUAL_NONCONT(sample.GetBytesPerSample(), 1); VERIFY_EQUAL_NONCONT(sample.GetNumChannels(), 1); VERIFY_EQUAL_NONCONT(sample.GetElementarySampleSize(), 1); VERIFY_EQUAL_NONCONT(sample.GetSampleSizeInBytes(), 60); VERIFY_EQUAL_NONCONT(sample.GetSampleRate(MOD_TYPE_S3M), 9001); VERIFY_EQUAL_NONCONT(sample.nVolume, 32 * 4); VERIFY_EQUAL_NONCONT(sample.nGlobalVol, 64); VERIFY_EQUAL_NONCONT(sample.uFlags, CHN_LOOP); VERIFY_EQUAL_NONCONT(sample.nLoopStart, 16); VERIFY_EQUAL_NONCONT(sample.nLoopEnd, 60); // Sample Data for(size_t i = 0; i < 30; i++) { VERIFY_EQUAL_NONCONT(sample.sample8()[i], 127); } for(size_t i = 31; i < 60; i++) { VERIFY_EQUAL_NONCONT(sample.sample8()[i], -128); } } { const ModSample &sample = sndFile.GetSample(2); VERIFY_EQUAL_NONCONT(sndFile.m_szNames[2], "Empty"); VERIFY_EQUAL_NONCONT(sample.GetSampleRate(MOD_TYPE_S3M), 16384); VERIFY_EQUAL_NONCONT(sample.nVolume, 2 * 4); } { const ModSample &sample = sndFile.GetSample(3); VERIFY_EQUAL_NONCONT(sndFile.m_szNames[3], "Stereo / 16-Bit"); VERIFY_EQUAL_NONCONT(sample.filename, "Filename_3_X"); VERIFY_EQUAL_NONCONT(sample.GetBytesPerSample(), 4); VERIFY_EQUAL_NONCONT(sample.GetNumChannels(), 2); VERIFY_EQUAL_NONCONT(sample.GetElementarySampleSize(), 2); VERIFY_EQUAL_NONCONT(sample.GetSampleSizeInBytes(), 64); VERIFY_EQUAL_NONCONT(sample.GetSampleRate(MOD_TYPE_S3M), 16000); VERIFY_EQUAL_NONCONT(sample.nVolume, 0); VERIFY_EQUAL_NONCONT(sample.uFlags, CHN_LOOP | CHN_16BIT | CHN_STEREO); VERIFY_EQUAL_NONCONT(sample.nLoopStart, 0); VERIFY_EQUAL_NONCONT(sample.nLoopEnd, 16); // Sample Data (Stereo Interleaved) for(size_t i = 0; i < 7; i++) { VERIFY_EQUAL_NONCONT(sample.sample16()[4 + i], int16(-32768)); } } { const ModSample &sample = sndFile.GetSample(4); VERIFY_EQUAL_NONCONT(sndFile.m_szNames[4], "adlib"); VERIFY_EQUAL_NONCONT((sample.filename == ""), true); VERIFY_EQUAL_NONCONT(sample.GetSampleRate(MOD_TYPE_S3M), 8363); VERIFY_EQUAL_NONCONT(sample.nVolume, 58 * 4); VERIFY_EQUAL_NONCONT(sample.uFlags, CHN_ADLIB); VERIFY_EQUAL_NONCONT(sample.adlib, (OPLPatch{ { 0x00, 0x00, 0xC0, 0x00, 0xF0, 0xD2, 0x05, 0xB3, 0x01, 0x00, 0x00, 0x00 } })); } // Orders VERIFY_EQUAL_NONCONT(sndFile.Order().GetRestartPos(), 0); VERIFY_EQUAL_NONCONT(sndFile.Order().GetLengthTailTrimmed(), 5); VERIFY_EQUAL_NONCONT(sndFile.Order()[0], 0); VERIFY_EQUAL_NONCONT(sndFile.Order()[1], sndFile.Order.GetIgnoreIndex()); VERIFY_EQUAL_NONCONT(sndFile.Order()[2], sndFile.Order.GetInvalidPatIndex()); VERIFY_EQUAL_NONCONT(sndFile.Order()[3], 1); VERIFY_EQUAL_NONCONT(sndFile.Order()[4], 0); // Patterns VERIFY_EQUAL_NONCONT(sndFile.Patterns.GetNumPatterns(), 2); VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetNumRows(), 64); VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetNumChannels(), 4); VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetOverrideSignature(), false); VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetpModCommand(0, 0)->note, NOTE_MIN + 12); VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetpModCommand(1, 0)->note, NOTE_MIN + 107); VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetpModCommand(0, 1)->volcmd, VOLCMD_VOLUME); VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetpModCommand(0, 1)->vol, 0); VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetpModCommand(1, 1)->volcmd, VOLCMD_VOLUME); VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetpModCommand(1, 1)->vol, 64); VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetpModCommand(2, 1)->volcmd, VOLCMD_PANNING); VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetpModCommand(2, 1)->vol, 0); VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetpModCommand(3, 1)->volcmd, VOLCMD_PANNING); VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetpModCommand(3, 1)->vol, 64); VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetpModCommand(0, 3)->command, CMD_SPEED); VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetpModCommand(0, 3)->param, 0x11); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetNumRows(), 64); VERIFY_EQUAL_NONCONT(sndFile.Patterns.IsPatternEmpty(1), false); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(63, 3)->param, 0x04); } // Check if our test file was loaded correctly. static void TestLoadMODFile(CSoundFile &sndFile) { // Global Variables VERIFY_EQUAL_NONCONT(sndFile.GetTitle(), "MOD_Test___________X"); VERIFY_EQUAL_NONCONT((sndFile.m_SongFlags & SONG_FILE_FLAGS), SONG_PT_MODE | SONG_AMIGALIMITS | SONG_ISAMIGA); VERIFY_EQUAL_NONCONT(sndFile.GetMixLevels(), MixLevels::Compatible); VERIFY_EQUAL_NONCONT(sndFile.m_nTempoMode, TempoMode::Classic); VERIFY_EQUAL_NONCONT(sndFile.GetNumChannels(), 4); VERIFY_EQUAL_NONCONT(sndFile.m_playBehaviour[kMODOneShotLoops], true); VERIFY_EQUAL_NONCONT(sndFile.m_playBehaviour[kMODSampleSwap], true); VERIFY_EQUAL_NONCONT(sndFile.m_playBehaviour[kMODIgnorePanning], true); VERIFY_EQUAL_NONCONT(sndFile.m_playBehaviour[kMODVBlankTiming], false); // Test GetLength code, in particular with subsongs VERIFY_EQUAL_NONCONT(sndFile.GetLength(eNoAdjust, GetLengthTarget(0, 4)).back().targetReached, false); const auto allSubSongs = sndFile.GetLength(eNoAdjust, GetLengthTarget(true)); VERIFY_EQUAL_NONCONT(allSubSongs.size(), 2); VERIFY_EQUAL_EPS(allSubSongs[0].duration, 2.04, 0.1); VERIFY_EQUAL_EPS(allSubSongs[1].duration, 118.84, 0.1); VERIFY_EQUAL_NONCONT(allSubSongs[0].lastOrder, 0); VERIFY_EQUAL_NONCONT(allSubSongs[0].lastRow, 1); VERIFY_EQUAL_NONCONT(allSubSongs[1].lastOrder, 2); VERIFY_EQUAL_NONCONT(allSubSongs[1].lastRow, 61); VERIFY_EQUAL_NONCONT(allSubSongs[1].startOrder, 2); VERIFY_EQUAL_NONCONT(allSubSongs[1].startRow, 0); // Samples VERIFY_EQUAL_NONCONT(sndFile.GetNumSamples(), 31); { const ModSample &sample = sndFile.GetSample(1); VERIFY_EQUAL_NONCONT(sndFile.m_szNames[1], "Sample_1_____________X"); VERIFY_EQUAL_NONCONT(sample.GetBytesPerSample(), 1); VERIFY_EQUAL_NONCONT(sample.GetNumChannels(), 1); VERIFY_EQUAL_NONCONT(sample.GetElementarySampleSize(), 1); VERIFY_EQUAL_NONCONT(sample.GetSampleSizeInBytes(), 1244); VERIFY_EQUAL_NONCONT(sample.nFineTune, 0x70); VERIFY_EQUAL_NONCONT(sample.RelativeTone, 0); VERIFY_EQUAL_NONCONT(sample.nVolume, 256); VERIFY_EQUAL_NONCONT(sample.nGlobalVol, 64); VERIFY_EQUAL_NONCONT(sample.uFlags, CHN_LOOP); VERIFY_EQUAL_NONCONT(sample.nLoopStart, 0); VERIFY_EQUAL_NONCONT(sample.nLoopEnd, 128); // Sample Data VERIFY_EQUAL_NONCONT(sample.sample8()[0], 0); VERIFY_EQUAL_NONCONT(sample.sample8()[1], 0); VERIFY_EQUAL_NONCONT(sample.sample8()[2], -29); } { const ModSample &sample = sndFile.GetSample(3); VERIFY_EQUAL_NONCONT(sndFile.m_szNames[3], "OpenMPT Module Loader"); VERIFY_EQUAL_NONCONT(sample.GetSampleSizeInBytes(), 0); VERIFY_EQUAL_NONCONT(sample.nFineTune, -0x80); VERIFY_EQUAL_NONCONT(sample.RelativeTone, 0); VERIFY_EQUAL_NONCONT(sample.nVolume, 4); VERIFY_EQUAL_NONCONT(sample.nGlobalVol, 64); } // Orders VERIFY_EQUAL_NONCONT(sndFile.Order().GetRestartPos(), 0); VERIFY_EQUAL_NONCONT(sndFile.Order().GetLengthTailTrimmed(), 4); VERIFY_EQUAL_NONCONT(sndFile.Order()[0], 0); VERIFY_EQUAL_NONCONT(sndFile.Order()[1], 1); VERIFY_EQUAL_NONCONT(sndFile.Order()[2], 2); VERIFY_EQUAL_NONCONT(sndFile.Order()[3], 0); // Patterns VERIFY_EQUAL_NONCONT(sndFile.Patterns.GetNumPatterns(), 3); VERIFY_EQUAL_NONCONT(sndFile.Patterns[2].GetNumRows(), 64); VERIFY_EQUAL_NONCONT(sndFile.Patterns[2].GetpModCommand(1, 0)->note, NOTE_MIDDLEC + 12); VERIFY_EQUAL_NONCONT(sndFile.Patterns[2].GetpModCommand(16, 3)->instr, 1); VERIFY_EQUAL_NONCONT(sndFile.Patterns[2].GetpModCommand(19, 3)->command, CMD_PANNING8); VERIFY_EQUAL_NONCONT(sndFile.Patterns[2].GetpModCommand(19, 3)->param, 0x28); VERIFY_EQUAL_NONCONT(sndFile.Patterns[2].GetpModCommand(20, 0)->command, CMD_TEMPO); VERIFY_EQUAL_NONCONT(sndFile.Patterns[2].GetpModCommand(20, 1)->command, CMD_SPEED); } #ifdef MODPLUG_TRACKER static bool ShouldRunTests() { mpt::PathString theFile = theApp.GetInstallPath(); if(theFile.IsDirectory() && (theFile + P_("test")).IsDirectory()) { if((theFile + P_("test\\test.mptm")).IsFile()) { return true; } } return false; } static mpt::PathString GetTestFilenameBase() { mpt::PathString theFile = theApp.GetInstallPath(); theFile += P_("test/test."); return theFile; } static mpt::PathString GetTempFilenameBase() { return GetTestFilenameBase(); } typedef CModDoc *TSoundFileContainer; static CSoundFile &GetSoundFile(TSoundFileContainer &sndFile) { return sndFile->GetSoundFile(); } static TSoundFileContainer CreateSoundFileContainer(const mpt::PathString &filename) { CModDoc *pModDoc = static_cast(theApp.OpenDocumentFile(filename.ToCString(), FALSE)); return pModDoc; } static void DestroySoundFileContainer(TSoundFileContainer &sndFile) { sndFile->OnCloseDocument(); } static void SaveTestFile(const TSoundFileContainer &sndFile, const mpt::PathString &filename) { sndFile->DoSave(filename); // Saving the file puts it in the MRU list... theApp.RemoveMruItem(0); } const auto SaveIT = SaveTestFile; const auto SaveXM = SaveTestFile; const auto SaveS3M = SaveTestFile; const auto SaveMOD = SaveTestFile; #else // !MODPLUG_TRACKER static bool ShouldRunTests() { #if MPT_TEST_HAS_FILESYSTEM return true; #else return false; #endif } static mpt::PathString GetTestFilenameBase() { return Test::GetPathPrefix() + P_("./test/test."); } static mpt::PathString GetTempFilenameBase() { return P_("./test."); } typedef std::shared_ptr TSoundFileContainer; static CSoundFile &GetSoundFile(TSoundFileContainer &sndFile) { return *sndFile.get(); } static TSoundFileContainer CreateSoundFileContainer(const mpt::PathString &filename) { mpt::ifstream stream(filename, std::ios::binary); FileReader file = mpt::IO::make_FileCursor(stream); std::shared_ptr pSndFile = std::make_shared(); pSndFile->Create(file, CSoundFile::loadCompleteModule); return pSndFile; } static void DestroySoundFileContainer(TSoundFileContainer & /* sndFile */ ) { return; } #ifndef MODPLUG_NO_FILESAVE static void SaveIT(const TSoundFileContainer &sndFile, const mpt::PathString &filename) { mpt::ofstream f(filename, std::ios::binary); sndFile->SaveIT(f, filename, false); } static void SaveXM(const TSoundFileContainer &sndFile, const mpt::PathString &filename) { mpt::ofstream f(filename, std::ios::binary); sndFile->SaveXM(f, false); } static void SaveS3M(const TSoundFileContainer &sndFile, const mpt::PathString &filename) { mpt::ofstream f(filename, std::ios::binary); sndFile->SaveS3M(f); } static void SaveMOD(const TSoundFileContainer &sndFile, const mpt::PathString &filename) { mpt::ofstream f(filename, std::ios::binary); sndFile->SaveMod(f); } #endif // !MODPLUG_NO_FILESAVE #endif // MODPLUG_TRACKER // Test file loading and saving static MPT_NOINLINE void TestLoadSaveFile() { if(!ShouldRunTests()) { return; } #ifdef MODPLUG_TRACKER bool saveMutedChannels = TrackerSettings::Instance().MiscSaveChannelMuteStatus; TrackerSettings::Instance().MiscSaveChannelMuteStatus = true; #endif mpt::PathString filenameBaseSrc = GetTestFilenameBase(); mpt::PathString filenameBase = GetTempFilenameBase(); // Test MPTM file loading { TSoundFileContainer sndFileContainer = CreateSoundFileContainer(filenameBaseSrc + P_("mptm")); TestLoadMPTMFile(GetSoundFile(sndFileContainer)); #ifndef MODPLUG_NO_FILESAVE // Test file saving GetSoundFile(sndFileContainer).m_dwLastSavedWithVersion = Version::Current(); SaveIT(sndFileContainer, filenameBase + P_("saved.mptm")); #endif DestroySoundFileContainer(sndFileContainer); } // Reload the saved file and test if everything is still working correctly. #ifndef MODPLUG_NO_FILESAVE { TSoundFileContainer sndFileContainer = CreateSoundFileContainer(filenameBase + P_("saved.mptm")); TestLoadMPTMFile(GetSoundFile(sndFileContainer)); DestroySoundFileContainer(sndFileContainer); RemoveFile(filenameBase + P_("saved.mptm")); } #endif // Test XM file loading { TSoundFileContainer sndFileContainer = CreateSoundFileContainer(filenameBaseSrc + P_("xm")); TestLoadXMFile(GetSoundFile(sndFileContainer)); // In OpenMPT 1.20 (up to revision 1459), there was a bug in the XM saver // that would create broken XMs if the sample map contained samples that // were only referenced below C-1 or above B-8 (such samples should not // be written). Let's insert a sample there and check if re-loading the // file still works. GetSoundFile(sndFileContainer).m_nSamples++; GetSoundFile(sndFileContainer).Instruments[1]->Keyboard[110] = GetSoundFile(sndFileContainer).GetNumSamples(); #ifndef MODPLUG_NO_FILESAVE // Test file saving GetSoundFile(sndFileContainer).m_dwLastSavedWithVersion = Version::Current(); SaveXM(sndFileContainer, filenameBase + P_("saved.xm")); #endif DestroySoundFileContainer(sndFileContainer); } // Reload the saved file and test if everything is still working correctly. #ifndef MODPLUG_NO_FILESAVE { TSoundFileContainer sndFileContainer = CreateSoundFileContainer(filenameBase + P_("saved.xm")); TestLoadXMFile(GetSoundFile(sndFileContainer)); DestroySoundFileContainer(sndFileContainer); RemoveFile(filenameBase + P_("saved.xm")); } #endif // Test S3M file loading { TSoundFileContainer sndFileContainer = CreateSoundFileContainer(filenameBaseSrc + P_("s3m")); auto &sndFile = GetSoundFile(sndFileContainer); TestLoadS3MFile(sndFile, false); // Test GetLength code, in particular with subsongs sndFile.ChnSettings[1].dwFlags.reset(CHN_MUTE); VERIFY_EQUAL_EPS(sndFile.GetLength(eAdjustSamplePositions, GetLengthTarget(3, 1)).back().duration, 19.237, 0.01); VERIFY_EQUAL_NONCONT(sndFile.GetLength(eAdjustSamplePositions, GetLengthTarget(2, 0).StartPos(0, 1, 0)).back().targetReached, false); auto allSubSongs = sndFile.GetLength(eNoAdjust, GetLengthTarget(true)); VERIFY_EQUAL_NONCONT(allSubSongs.size(), 3); double totalDuration = 0.0; for(const auto &subSong : allSubSongs) { totalDuration += subSong.duration; } VERIFY_EQUAL_EPS(totalDuration, 3674.38, 1.0); #ifndef MODPLUG_NO_FILESAVE // Test file saving sndFile.ChnSettings[1].dwFlags.set(CHN_MUTE); sndFile.m_dwLastSavedWithVersion = Version::Current(); SaveS3M(sndFileContainer, filenameBase + P_("saved.s3m")); #endif DestroySoundFileContainer(sndFileContainer); } // Reload the saved file and test if everything is still working correctly. #ifndef MODPLUG_NO_FILESAVE { TSoundFileContainer sndFileContainer = CreateSoundFileContainer(filenameBase + P_("saved.s3m")); TestLoadS3MFile(GetSoundFile(sndFileContainer), true); DestroySoundFileContainer(sndFileContainer); RemoveFile(filenameBase + P_("saved.s3m")); } #endif // Test MOD file loading { TSoundFileContainer sndFileContainer = CreateSoundFileContainer(filenameBaseSrc + P_("mod")); auto &sndFile = GetSoundFile(sndFileContainer); TestLoadMODFile(sndFile); #ifndef MODPLUG_NO_FILESAVE // Test file saving SaveMOD(sndFileContainer, filenameBase + P_("saved.mod")); #endif DestroySoundFileContainer(sndFileContainer); } // Reload the saved file and test if everything is still working correctly. #ifndef MODPLUG_NO_FILESAVE { TSoundFileContainer sndFileContainer = CreateSoundFileContainer(filenameBase + P_("saved.mod")); TestLoadMODFile(GetSoundFile(sndFileContainer)); DestroySoundFileContainer(sndFileContainer); RemoveFile(filenameBase + P_("saved.mod")); } #endif // General file I/O tests { std::ostringstream f; size_t bytesWritten; mpt::IO::WriteVarInt(f, uint16(0), &bytesWritten); VERIFY_EQUAL_NONCONT(bytesWritten, 1); mpt::IO::WriteVarInt(f, uint16(127), &bytesWritten); VERIFY_EQUAL_NONCONT(bytesWritten, 1); mpt::IO::WriteVarInt(f, uint16(128), &bytesWritten); VERIFY_EQUAL_NONCONT(bytesWritten, 2); mpt::IO::WriteVarInt(f, uint16(16383), &bytesWritten); VERIFY_EQUAL_NONCONT(bytesWritten, 2); mpt::IO::WriteVarInt(f, uint16(16384), &bytesWritten); VERIFY_EQUAL_NONCONT(bytesWritten, 3); mpt::IO::WriteVarInt(f, uint16(65535), &bytesWritten); VERIFY_EQUAL_NONCONT(bytesWritten, 3); mpt::IO::WriteVarInt(f, uint64(0xFFFFFFFFFFFFFFFFull), &bytesWritten); VERIFY_EQUAL_NONCONT(bytesWritten, 10); std::string data = f.str(); FileReader file(mpt::byte_cast(mpt::as_span(data))); uint64 v; file.ReadVarInt(v); VERIFY_EQUAL_NONCONT(v, 0); file.ReadVarInt(v); VERIFY_EQUAL_NONCONT(v, 127); file.ReadVarInt(v); VERIFY_EQUAL_NONCONT(v, 128); file.ReadVarInt(v); VERIFY_EQUAL_NONCONT(v, 16383); file.ReadVarInt(v); VERIFY_EQUAL_NONCONT(v, 16384); file.ReadVarInt(v); VERIFY_EQUAL_NONCONT(v, 65535); file.ReadVarInt(v); VERIFY_EQUAL_NONCONT(v, 0xFFFFFFFFFFFFFFFFull); } #ifdef MODPLUG_TRACKER TrackerSettings::Instance().MiscSaveChannelMuteStatus = saveMutedChannels; #endif } // Test various editing features static MPT_NOINLINE void TestEditing() { #ifdef MODPLUG_TRACKER auto modDoc = static_cast(theApp.GetModDocTemplate()->CreateNewDocument()); auto &sndFile = modDoc->GetSoundFile(); sndFile.Create(FileReader(), CSoundFile::loadCompleteModule, modDoc); sndFile.m_nChannels = 4; sndFile.ChangeModTypeTo(MOD_TYPE_MPT); // Rearrange channels sndFile.Patterns.ResizeArray(2); sndFile.Patterns.Insert(0, 32); sndFile.Patterns.Insert(1, 48); sndFile.Patterns[1].SetName("Pattern"); sndFile.Patterns[1].SetSignature(2, 4); TempoSwing swing; swing.resize(2); sndFile.Patterns[1].SetTempoSwing(swing); sndFile.Patterns[1].GetpModCommand(37, 0)->instr = 1; sndFile.Patterns[1].GetpModCommand(37, 1)->instr = 2; sndFile.Patterns[1].GetpModCommand(37, 2)->instr = 3; sndFile.Patterns[1].GetpModCommand(37, 3)->instr = 4; modDoc->ReArrangeChannels({ 3, 2, CHANNELINDEX_INVALID, 0 }); modDoc->ReArrangeChannels({ 0, 1, 1, CHANNELINDEX_INVALID, 3 }); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetName(), "Pattern"); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetRowsPerBeat(), 2); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetRowsPerMeasure(), 4); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetTempoSwing(), swing); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(37, 0)->instr, 4); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(37, 1)->instr, 3); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(37, 2)->instr, 3); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(37, 3)->instr, 0); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(37, 4)->instr, 1); // Rearrange samples sndFile.m_nSamples = 2; sndFile.GetSample(1).filename = "1"; sndFile.m_szNames[1] = "1"; sndFile.GetSample(2).filename = "2"; sndFile.m_szNames[2] = "2"; sndFile.GetSample(2).nLength = 16; sndFile.GetSample(2).AllocateSample(); modDoc->ReArrangeSamples({ 2, SAMPLEINDEX_INVALID, 1 }); VERIFY_EQUAL_NONCONT(sndFile.GetSample(1).HasSampleData(), true); VERIFY_EQUAL_NONCONT(sndFile.GetSample(1).filename, "2"); VERIFY_EQUAL_NONCONT(sndFile.m_szNames[1], "2"); VERIFY_EQUAL_NONCONT(sndFile.GetSample(2).filename, ""); VERIFY_EQUAL_NONCONT(sndFile.m_szNames[2], ""); VERIFY_EQUAL_NONCONT(sndFile.GetSample(3).filename, "1"); VERIFY_EQUAL_NONCONT(sndFile.m_szNames[3], "1"); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(37, 4)->instr, 3); // Convert / rearrange instruments modDoc->ConvertSamplesToInstruments(); modDoc->ReArrangeInstruments({ INSTRUMENTINDEX_INVALID, 2, 1, 3 }); VERIFY_EQUAL_NONCONT(sndFile.Instruments[1]->name, ""); VERIFY_EQUAL_NONCONT(sndFile.Instruments[2]->name, ""); VERIFY_EQUAL_NONCONT(sndFile.Instruments[3]->name, "2"); VERIFY_EQUAL_NONCONT(sndFile.Instruments[4]->name, "1"); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(37, 4)->instr, 4); modDoc->ConvertInstrumentsToSamples(); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(37, 4)->instr, 3); modDoc->SetModified(); VERIFY_EQUAL_NONCONT(modDoc->IsModified(), true); VERIFY_EQUAL_NONCONT(modDoc->ModifiedSinceLastAutosave(), true); VERIFY_EQUAL_NONCONT(modDoc->ModifiedSinceLastAutosave(), false); sndFile.Destroy(); modDoc->OnCloseDocument(); #endif } static void RunITCompressionTest(const std::vector &sampleData, FlagSet smpFormat, bool it215) { ModSample smp; smp.uFlags = smpFormat; smp.pData.pSample = const_cast(sampleData.data()); smp.nLength = mpt::saturate_cast(sampleData.size() / smp.GetBytesPerSample()); std::string data; { std::ostringstream f; ITCompression compression(smp, it215, &f); data = f.str(); } { FileReader file(mpt::byte_cast(mpt::as_span(data))); std::vector sampleDataNew(sampleData.size(), 0); smp.pData.pSample = sampleDataNew.data(); ITDecompression decompression(file, smp, it215); VERIFY_EQUAL_NONCONT(memcmp(sampleData.data(), sampleDataNew.data(), sampleData.size()), 0); } } static MPT_NOINLINE void TestITCompression() { // Test loading / saving of IT-compressed samples const int sampleDataSize = 65536; std::vector sampleData(sampleDataSize, 0); std::srand(0); for(int i = 0; i < sampleDataSize; i++) { sampleData[i] = mpt::random(*s_PRNG); } // Run each compression test with IT215 compression and without. for(int i = 0; i < 2; i++) { RunITCompressionTest(sampleData, ChannelFlags(0), i == 0); RunITCompressionTest(sampleData, CHN_16BIT, i == 0); RunITCompressionTest(sampleData, CHN_STEREO, i == 0); RunITCompressionTest(sampleData, CHN_16BIT | CHN_STEREO, i == 0); } } #if 0 static bool RatioEqual(CTuningBase::RATIOTYPE a, CTuningBase::RATIOTYPE b) { if(a == CTuningBase::RATIOTYPE(0) && b == CTuningBase::RATIOTYPE(0)) { return true; } if(a == CTuningBase::RATIOTYPE(0) || b == CTuningBase::RATIOTYPE(0)) { return false; } return (std::fabs(CTuningBase::RATIOTYPE(1) - (a/b)) < CTuningBase::RATIOTYPE(0.0001)); } static void CheckEqualTuningCollections(const CTuningCollection &a, const CTuningCollection &b) { VERIFY_EQUAL(a.GetName(), b.GetName()); VERIFY_EQUAL(a.GetNumTunings(), b.GetNumTunings()); for(std::size_t tuning = 0; tuning < std::min(a.GetNumTunings(), b.GetNumTunings()); ++tuning) { VERIFY_EQUAL(a.GetTuning(tuning).GetName(), b.GetTuning(tuning).GetName()); VERIFY_EQUAL(a.GetTuning(tuning).GetType(), b.GetTuning(tuning).GetType()); VERIFY_EQUAL(a.GetTuning(tuning).GetGroupSize(), b.GetTuning(tuning).GetGroupSize()); VERIFY_EQUAL(a.GetTuning(tuning).GetFineStepCount(), b.GetTuning(tuning).GetFineStepCount()); VERIFY_EQUAL(RatioEqual(a.GetTuning(tuning).GetGroupRatio(), b.GetTuning(tuning).GetGroupRatio()), true); VERIFY_EQUAL(a.GetTuning(tuning).GetValidityRange(), b.GetTuning(tuning).GetValidityRange()); for(ModCommand::NOTE note = NOTE_MIN; note <= NOTE_MAX; ++note) { VERIFY_EQUAL(a.GetTuning(tuning).GetNoteName(note - NOTE_MIDDLEC), b.GetTuning(tuning).GetNoteName(note - NOTE_MIDDLEC)); VERIFY_EQUAL(RatioEqual(a.GetTuning(tuning).GetRatio(note - NOTE_MIDDLEC), b.GetTuning(tuning).GetRatio(note - NOTE_MIDDLEC)), true); } } } #endif static double Rand01() { return mpt::random(*s_PRNG, 0.0, 1.0); } template T Rand(const T min, const T max) { return mpt::saturate_round(min + Rand01() * (max - min)); } static void GenerateCommands(CPattern& pat, const double dProbPcs, const double dProbPc) { const double dPcxProb = dProbPcs + dProbPc; for(auto &m : pat) { const double rand = Rand01(); if(rand < dPcxProb) { if(rand < dProbPcs) m.note = NOTE_PCS; else m.note = NOTE_PC; m.instr = Rand(0, MAX_MIXPLUGINS); m.SetValueVolCol(Rand(0, ModCommand::maxColumnValue)); m.SetValueEffectCol(Rand(0, ModCommand::maxColumnValue)); } else m.Clear(); } } // Test PC note serialization static MPT_NOINLINE void TestPCnoteSerialization() { FileReader file; std::unique_ptr pSndFile = std::make_unique(); CSoundFile &sndFile = *pSndFile.get(); sndFile.m_nType = MOD_TYPE_MPT; sndFile.Patterns.DestroyPatterns(); sndFile.m_nChannels = ModSpecs::mptm.channelsMax; sndFile.Patterns.Insert(0, ModSpecs::mptm.patternRowsMin); sndFile.Patterns.Insert(1, 64); GenerateCommands(sndFile.Patterns[1], 0.3, 0.3); sndFile.Patterns.Insert(2, ModSpecs::mptm.patternRowsMax); GenerateCommands(sndFile.Patterns[2], 0.5, 0.5); // Copy pattern data for comparison. CPatternContainer patterns{ sndFile.Patterns }; std::stringstream mem; WriteModPatterns(mem, sndFile.Patterns); VERIFY_EQUAL_NONCONT( mem.good(), true ); // Clear patterns. sndFile.Patterns[0].ClearCommands(); sndFile.Patterns[1].ClearCommands(); sndFile.Patterns[2].ClearCommands(); // Read data back. ReadModPatterns(mem, sndFile.Patterns); // Compare. VERIFY_EQUAL_NONCONT( sndFile.Patterns[0].GetNumRows(), ModSpecs::mptm.patternRowsMin); VERIFY_EQUAL_NONCONT( sndFile.Patterns[1].GetNumRows(), 64); VERIFY_EQUAL_NONCONT( sndFile.Patterns[2].GetNumRows(), ModSpecs::mptm.patternRowsMax); for(int i = 0; i < 3; i++) { VERIFY_EQUAL(sndFile.Patterns[i], patterns[i]); } } static inline std::size_t strnlen(const char *str, std::size_t n) { #if MPT_COMPILER_MSVC return ::strnlen(str, n); #else if(n >= std::numeric_limits::max()) { return std::strlen(str); } for(std::size_t i = 0; i < n; ++i) { if(str[i] == '\0') { return i; } } return n; #endif } // Test String I/O functionality static MPT_NOINLINE void TestStringIO() { char src0[4] = { '\0', 'X', ' ', 'X' }; // Weird empty buffer char src1[4] = { 'X', ' ', '\0', 'X' }; // Weird buffer (hello Impulse Tracker) char src2[4] = { 'X', 'Y', 'Z', ' ' }; // Full buffer, last character space char src3[4] = { 'X', 'Y', 'Z', '!' }; // Full buffer, last character non-space char src4[4] = { 'x', 'y', '\t', '\n' }; // Full buffer containing non-space whitespace char dst1[6]; // Destination buffer, larger than source buffer char dst2[3]; // Destination buffer, smaller than source buffer #define ReadTest(mode, dst, src, expectedResult) \ std::memset(dst, 0x7f, sizeof(dst)); \ mpt::String::WriteAutoBuf(dst) = mpt::String::ReadBuf(mpt::String:: mode , src); \ VERIFY_EQUAL_NONCONT(strncmp(dst, expectedResult, std::size(dst)), 0); /* Ensure that the strings are identical */ \ for(size_t i = strlen(dst); i < std::size(dst); i++) \ VERIFY_EQUAL_NONCONT(dst[i], '\0'); /* Ensure that rest of the buffer is completely nulled */ \ /**/ #define WriteTest(mode, dst, src, expectedResult) \ std::memset(dst, 0x7f, sizeof(dst)); \ mpt::String::WriteBuf(mpt::String:: mode , dst) = mpt::String::ReadAutoBuf(src); \ VERIFY_EQUAL_NONCONT(strncmp(dst, expectedResult, std::size(dst)), 0); /* Ensure that the strings are identical */ \ for(size_t i = Test::strnlen(dst, std::size(dst)); i < std::size(dst); i++) \ VERIFY_EQUAL_NONCONT(dst[i], '\0'); /* Ensure that rest of the buffer is completely nulled */ \ /**/ // Check reading of null-terminated string into larger buffer ReadTest(nullTerminated, dst1, src0, ""); ReadTest(nullTerminated, dst1, src1, "X "); ReadTest(nullTerminated, dst1, src2, "XYZ"); ReadTest(nullTerminated, dst1, src3, "XYZ"); ReadTest(nullTerminated, dst1, src4, "xy\t"); // Check reading of string that should be null-terminated, but is maybe too long to still hold the null character. ReadTest(maybeNullTerminated, dst1, src0, ""); ReadTest(maybeNullTerminated, dst1, src1, "X "); ReadTest(maybeNullTerminated, dst1, src2, "XYZ "); ReadTest(maybeNullTerminated, dst1, src3, "XYZ!"); ReadTest(maybeNullTerminated, dst1, src4, "xy\t\n"); // Check reading of space-padded strings with ignored last character ReadTest(spacePaddedNull, dst1, src0, " X"); ReadTest(spacePaddedNull, dst1, src1, "X"); ReadTest(spacePaddedNull, dst1, src2, "XYZ"); ReadTest(spacePaddedNull, dst1, src3, "XYZ"); ReadTest(spacePaddedNull, dst1, src4, "xy\t"); // Check reading of space-padded strings ReadTest(spacePadded, dst1, src0, " X X"); ReadTest(spacePadded, dst1, src1, "X X"); ReadTest(spacePadded, dst1, src2, "XYZ"); ReadTest(spacePadded, dst1, src3, "XYZ!"); ReadTest(spacePadded, dst1, src4, "xy\t\n"); /////////////////////////////// // Check reading of null-terminated string into smaller buffer ReadTest(nullTerminated, dst2, src0, ""); ReadTest(nullTerminated, dst2, src1, "X "); ReadTest(nullTerminated, dst2, src2, "XY"); ReadTest(nullTerminated, dst2, src3, "XY"); ReadTest(nullTerminated, dst2, src4, "xy"); // Check reading of string that should be null-terminated, but is maybe too long to still hold the null character. ReadTest(maybeNullTerminated, dst2, src0, ""); ReadTest(maybeNullTerminated, dst2, src1, "X "); ReadTest(maybeNullTerminated, dst2, src2, "XY"); ReadTest(maybeNullTerminated, dst2, src3, "XY"); ReadTest(maybeNullTerminated, dst2, src4, "xy"); // Check reading of space-padded strings with ignored last character ReadTest(spacePaddedNull, dst2, src0, " X"); ReadTest(spacePaddedNull, dst2, src1, "X"); ReadTest(spacePaddedNull, dst2, src2, "XY"); ReadTest(spacePaddedNull, dst2, src3, "XY"); ReadTest(spacePaddedNull, dst2, src4, "xy"); // Check reading of space-padded strings ReadTest(spacePadded, dst2, src0, " X"); ReadTest(spacePadded, dst2, src1, "X "); ReadTest(spacePadded, dst2, src2, "XY"); ReadTest(spacePadded, dst2, src3, "XY"); ReadTest(spacePadded, dst2, src4, "xy"); /////////////////////////////// // Check writing of null-terminated string into larger buffer WriteTest(nullTerminated, dst1, src0, ""); WriteTest(nullTerminated, dst1, src1, "X "); WriteTest(nullTerminated, dst1, src2, "XYZ "); WriteTest(nullTerminated, dst1, src3, "XYZ!"); // Check writing of string that should be null-terminated, but is maybe too long to still hold the null character. WriteTest(maybeNullTerminated, dst1, src0, ""); WriteTest(maybeNullTerminated, dst1, src1, "X "); WriteTest(maybeNullTerminated, dst1, src2, "XYZ "); WriteTest(maybeNullTerminated, dst1, src3, "XYZ!"); // Check writing of space-padded strings with last character set to null WriteTest(spacePaddedNull, dst1, src0, " "); WriteTest(spacePaddedNull, dst1, src1, "X "); WriteTest(spacePaddedNull, dst1, src2, "XYZ "); WriteTest(spacePaddedNull, dst1, src3, "XYZ! "); // Check writing of space-padded strings WriteTest(spacePadded, dst1, src0, " "); WriteTest(spacePadded, dst1, src1, "X "); WriteTest(spacePadded, dst1, src2, "XYZ "); WriteTest(spacePadded, dst1, src3, "XYZ! "); /////////////////////////////// // Check writing of null-terminated string into smaller buffer WriteTest(nullTerminated, dst2, src0, ""); WriteTest(nullTerminated, dst2, src1, "X "); WriteTest(nullTerminated, dst2, src2, "XY"); WriteTest(nullTerminated, dst2, src3, "XY"); // Check writing of string that should be null-terminated, but is maybe too long to still hold the null character. WriteTest(maybeNullTerminated, dst2, src0, ""); WriteTest(maybeNullTerminated, dst2, src1, "X "); WriteTest(maybeNullTerminated, dst2, src2, "XYZ"); WriteTest(maybeNullTerminated, dst2, src3, "XYZ"); // Check writing of space-padded strings with last character set to null WriteTest(spacePaddedNull, dst2, src0, " "); WriteTest(spacePaddedNull, dst2, src1, "X "); WriteTest(spacePaddedNull, dst2, src2, "XY"); WriteTest(spacePaddedNull, dst2, src3, "XY"); // Check writing of space-padded strings WriteTest(spacePadded, dst2, src0, " "); WriteTest(spacePadded, dst2, src1, "X "); WriteTest(spacePadded, dst2, src2, "XYZ"); WriteTest(spacePadded, dst2, src3, "XYZ"); #undef ReadTest #undef WriteTest { std::string dststring; std::string src0string = std::string(src0, std::size(src0)); std::string src1string = std::string(src1, std::size(src1)); std::string src2string = std::string(src2, std::size(src2)); std::string src3string = std::string(src3, std::size(src3)); #define ReadTest(mode, dst, src, expectedResult) \ dst = mpt::String::ReadBuf(mpt::String:: mode , src); \ VERIFY_EQUAL_NONCONT(dst, expectedResult); /* Ensure that the strings are identical */ \ /**/ #define WriteTest(mode, dst, src, expectedResult) \ std::memset(dst, 0x7f, sizeof(dst)); \ mpt::String::WriteBuf(mpt::String:: mode , dst) = src; \ VERIFY_EQUAL_NONCONT(strncmp(dst, expectedResult, std::size(dst)), 0); /* Ensure that the strings are identical */ \ for(size_t i = Test::strnlen(dst, std::size(dst)); i < std::size(dst); i++) \ VERIFY_EQUAL_NONCONT(dst[i], '\0'); /* Ensure that rest of the buffer is completely nulled */ \ /**/ // Check reading of null-terminated string into std::string ReadTest(nullTerminated, dststring, src0, ""); ReadTest(nullTerminated, dststring, src1, "X "); ReadTest(nullTerminated, dststring, src2, "XYZ"); ReadTest(nullTerminated, dststring, src3, "XYZ"); ReadTest(nullTerminated, dststring, src4, "xy\t"); // Check reading of string that should be null-terminated, but is maybe too long to still hold the null character. ReadTest(maybeNullTerminated, dststring, src0, ""); ReadTest(maybeNullTerminated, dststring, src1, "X "); ReadTest(maybeNullTerminated, dststring, src2, "XYZ "); ReadTest(maybeNullTerminated, dststring, src3, "XYZ!"); ReadTest(maybeNullTerminated, dststring, src4, "xy\t\n"); // Check reading of space-padded strings with ignored last character ReadTest(spacePaddedNull, dststring, src0, " X"); ReadTest(spacePaddedNull, dststring, src1, "X"); ReadTest(spacePaddedNull, dststring, src2, "XYZ"); ReadTest(spacePaddedNull, dststring, src3, "XYZ"); ReadTest(spacePaddedNull, dststring, src4, "xy\t"); // Check reading of space-padded strings ReadTest(spacePadded, dststring, src0, " X X"); ReadTest(spacePadded, dststring, src1, "X X"); ReadTest(spacePadded, dststring, src2, "XYZ"); ReadTest(spacePadded, dststring, src3, "XYZ!"); ReadTest(spacePadded, dststring, src4, "xy\t\n"); /////////////////////////////// // Check writing of null-terminated string into larger buffer WriteTest(nullTerminated, dst1, src0string, ""); WriteTest(nullTerminated, dst1, src1string, "X "); WriteTest(nullTerminated, dst1, src2string, "XYZ "); WriteTest(nullTerminated, dst1, src3string, "XYZ!"); // Check writing of string that should be null-terminated, but is maybe too long to still hold the null character. WriteTest(maybeNullTerminated, dst1, src0string, ""); WriteTest(maybeNullTerminated, dst1, src1string, "X "); WriteTest(maybeNullTerminated, dst1, src2string, "XYZ "); WriteTest(maybeNullTerminated, dst1, src3string, "XYZ!"); // Check writing of space-padded strings with last character set to null WriteTest(spacePaddedNull, dst1, src0string, " "); WriteTest(spacePaddedNull, dst1, src1string, "X "); WriteTest(spacePaddedNull, dst1, src2string, "XYZ "); WriteTest(spacePaddedNull, dst1, src3string, "XYZ! "); // Check writing of space-padded strings WriteTest(spacePadded, dst1, src0string, " "); WriteTest(spacePadded, dst1, src1string, "X "); WriteTest(spacePadded, dst1, src2string, "XYZ "); WriteTest(spacePadded, dst1, src3string, "XYZ! "); /////////////////////////////// // Check writing of null-terminated string into smaller buffer WriteTest(nullTerminated, dst2, src0string, ""); WriteTest(nullTerminated, dst2, src1string, "X "); WriteTest(nullTerminated, dst2, src2string, "XY"); WriteTest(nullTerminated, dst2, src3string, "XY"); // Check writing of string that should be null-terminated, but is maybe too long to still hold the null character. WriteTest(maybeNullTerminated, dst2, src0string, ""); WriteTest(maybeNullTerminated, dst2, src1string, "X "); WriteTest(maybeNullTerminated, dst2, src2string, "XYZ"); WriteTest(maybeNullTerminated, dst2, src3string, "XYZ"); // Check writing of space-padded strings with last character set to null WriteTest(spacePaddedNull, dst2, src0string, " "); WriteTest(spacePaddedNull, dst2, src1string, "X "); WriteTest(spacePaddedNull, dst2, src2string, "XY"); WriteTest(spacePaddedNull, dst2, src3string, "XY"); // Check writing of space-padded strings WriteTest(spacePadded, dst2, src0string, " "); WriteTest(spacePadded, dst2, src1string, "X "); WriteTest(spacePadded, dst2, src2string, "XYZ"); WriteTest(spacePadded, dst2, src3string, "XYZ"); /////////////////////////////// #undef ReadTest #undef WriteTest } { char s0[4] = {'\0', 'X', ' ', 'X' }; char s2[4] = { 'X', ' ','\0', 'X' }; char s4[4] = { 'X', 'Y', 'Z', ' ' }; char d2[2] = {'\0','\0'}; char d3[3] = {'\0','\0','\0'}; char d4[4] = {'\0','\0','\0','\0'}; char d5[5] = {'\0','\0','\0','\0','\0'}; #define CopyTest(dst, src, expectedResult) \ std::memset(dst, 0x7f, sizeof(dst)); \ mpt::String::WriteAutoBuf(dst) = mpt::String::ReadAutoBuf(src); \ VERIFY_EQUAL_NONCONT(strncmp(dst, expectedResult, std::size(dst)), 0); /* Ensure that the strings are identical */ \ for(size_t i = strlen(dst); i < std::size(dst); i++) \ VERIFY_EQUAL_NONCONT(dst[i], '\0'); /* Ensure that rest of the buffer is completely nulled */ \ /**/ CopyTest(d2, s0, ""); CopyTest(d2, s2, "X"); CopyTest(d2, s4, "X"); CopyTest(d3, s0, ""); CopyTest(d3, s2, "X "); CopyTest(d3, s4, "XY"); CopyTest(d4, s0, ""); CopyTest(d4, s2, "X "); CopyTest(d4, s4, "XYZ"); CopyTest(d5, s0, ""); CopyTest(d5, s2, "X "); CopyTest(d5, s4, "XYZ "); #undef CopyTest #define CopyTestN(dst, src, len, expectedResult) \ std::memset(dst, 0x7f, sizeof(dst)); \ mpt::String::WriteAutoBuf(dst) = mpt::String::ReadAutoBuf(src, std::min(std::size(src), static_cast(len))); \ VERIFY_EQUAL_NONCONT(strncmp(dst, expectedResult, std::size(dst)), 0); /* Ensure that the strings are identical */ \ for(size_t i = strlen(dst); i < std::size(dst); i++) \ VERIFY_EQUAL_NONCONT(dst[i], '\0'); /* Ensure that rest of the buffer is completely nulled */ \ /**/ CopyTestN(d2, s0, 1, ""); CopyTestN(d2, s2, 1, "X"); CopyTestN(d2, s4, 1, "X"); CopyTestN(d3, s0, 1, ""); CopyTestN(d3, s2, 1, "X"); CopyTestN(d3, s4, 1, "X"); CopyTestN(d4, s0, 1, ""); CopyTestN(d4, s2, 1, "X"); CopyTestN(d4, s4, 1, "X"); CopyTestN(d5, s0, 1, ""); CopyTestN(d5, s2, 1, "X"); CopyTestN(d5, s4, 1, "X"); CopyTestN(d2, s0, 2, ""); CopyTestN(d2, s2, 2, "X"); CopyTestN(d2, s4, 2, "X"); CopyTestN(d3, s0, 2, ""); CopyTestN(d3, s2, 2, "X "); CopyTestN(d3, s4, 2, "XY"); CopyTestN(d4, s0, 2, ""); CopyTestN(d4, s2, 2, "X "); CopyTestN(d4, s4, 2, "XY"); CopyTestN(d5, s0, 2, ""); CopyTestN(d5, s2, 2, "X "); CopyTestN(d5, s4, 2, "XY"); CopyTestN(d2, s0, 3, ""); CopyTestN(d2, s2, 3, "X"); CopyTestN(d2, s4, 3, "X"); CopyTestN(d3, s0, 3, ""); CopyTestN(d3, s2, 3, "X "); CopyTestN(d3, s4, 3, "XY"); CopyTestN(d4, s0, 3, ""); CopyTestN(d4, s2, 3, "X "); CopyTestN(d4, s4, 3, "XYZ"); CopyTestN(d5, s0, 3, ""); CopyTestN(d5, s2, 3, "X "); CopyTestN(d5, s4, 3, "XYZ"); CopyTestN(d2, s0, 4, ""); CopyTestN(d2, s2, 4, "X"); CopyTestN(d2, s4, 4, "X"); CopyTestN(d3, s0, 4, ""); CopyTestN(d3, s2, 4, "X "); CopyTestN(d3, s4, 4, "XY"); CopyTestN(d4, s0, 4, ""); CopyTestN(d4, s2, 4, "X "); CopyTestN(d4, s4, 4, "XYZ"); CopyTestN(d5, s0, 4, ""); CopyTestN(d5, s2, 4, "X "); CopyTestN(d5, s4, 4, "XYZ "); CopyTestN(d2, s0, 5, ""); CopyTestN(d2, s2, 5, "X"); CopyTestN(d2, s4, 5, "X"); CopyTestN(d3, s0, 5, ""); CopyTestN(d3, s2, 5, "X "); CopyTestN(d3, s4, 5, "XY"); CopyTestN(d4, s0, 5, ""); CopyTestN(d4, s2, 5, "X "); CopyTestN(d4, s4, 5, "XYZ"); CopyTestN(d5, s0, 5, ""); CopyTestN(d5, s2, 5, "X "); CopyTestN(d5, s4, 5, "XYZ "); #undef CopyTest } } static MPT_NOINLINE void TestSampleConversion() { std::vector sourceBufContainer(65536 * 4); std::vector targetBufContainer(65536 * 6); uint8 *sourceBuf = &(sourceBufContainer[0]); void *targetBuf = &(targetBufContainer[0]); // Signed 8-Bit Integer PCM // Unsigned 8-Bit Integer PCM // Delta 8-Bit Integer PCM { uint8 *source8 = sourceBuf; for(size_t i = 0; i < 256; i++) { source8[i] = static_cast(i); } int8 *signed8 = static_cast(targetBuf); uint8 *unsigned8 = static_cast(targetBuf) + 256; int8 *delta8 = static_cast(targetBuf) + 512; int8 delta = 0; CopySample(signed8, 256, 1, mpt::byte_cast(source8), 256, 1); CopySample(reinterpret_cast(unsigned8), 256, 1, mpt::byte_cast(source8), 256, 1); CopySample(delta8, 256, 1, mpt::byte_cast(source8), 256, 1); for(size_t i = 0; i < 256; i++) { delta += static_cast(i); VERIFY_EQUAL_QUIET_NONCONT(signed8[i], static_cast(i)); VERIFY_EQUAL_QUIET_NONCONT(unsigned8[i], static_cast(i + 0x80u)); VERIFY_EQUAL_QUIET_NONCONT(delta8[i], static_cast(delta)); } } // Signed 16-Bit Integer PCM // Unsigned 16-Bit Integer PCM // Delta 16-Bit Integer PCM { // Little Endian uint8 *source16 = sourceBuf; for(size_t i = 0; i < 65536; i++) { source16[i * 2 + 0] = static_cast(i & 0xFF); source16[i * 2 + 1] = static_cast(i >> 8); } int16 *signed16 = static_cast(targetBuf); uint16 *unsigned16 = static_cast(targetBuf) + 65536; int16 *delta16 = static_cast(targetBuf) + 65536 * 2; int16 delta = 0; CopySample >(signed16, 65536, 1, mpt::byte_cast(source16), 65536 * 2, 1); CopySample >(reinterpret_cast(unsigned16), 65536, 1, mpt::byte_cast(source16), 65536 * 2, 1); CopySample >(delta16, 65536, 1, mpt::byte_cast(source16), 65536 * 2, 1); for(size_t i = 0; i < 65536; i++) { delta += static_cast(i); VERIFY_EQUAL_QUIET_NONCONT(signed16[i], static_cast(i)); VERIFY_EQUAL_QUIET_NONCONT(unsigned16[i], static_cast(i + 0x8000u)); VERIFY_EQUAL_QUIET_NONCONT(delta16[i], static_cast(delta)); } // Big Endian for(size_t i = 0; i < 65536; i++) { source16[i * 2 + 0] = static_cast(i >> 8); source16[i * 2 + 1] = static_cast(i & 0xFF); } CopySample >(signed16, 65536, 1, mpt::byte_cast(source16), 65536 * 2, 1); CopySample >(reinterpret_cast(unsigned16), 65536, 1, mpt::byte_cast(source16), 65536 * 2, 1); CopySample >(delta16, 65536, 1, mpt::byte_cast(source16), 65536 * 2, 1); delta = 0; for(size_t i = 0; i < 65536; i++) { delta += static_cast(i); VERIFY_EQUAL_QUIET_NONCONT(signed16[i], static_cast(i)); VERIFY_EQUAL_QUIET_NONCONT(unsigned16[i], static_cast(i + 0x8000u)); VERIFY_EQUAL_QUIET_NONCONT(delta16[i], static_cast(delta)); } } // Signed 24-Bit Integer PCM { uint8 *source24 = sourceBuf; for(size_t i = 0; i < 65536; i++) { source24[i * 3 + 0] = 0; source24[i * 3 + 1] = static_cast(i & 0xFF); source24[i * 3 + 2] = static_cast(i >> 8); } int16 *truncated16 = static_cast(targetBuf); ModSample sample; sample.Initialize(); sample.nLength = 65536; sample.uFlags.set(CHN_16BIT); sample.pData.pSample = (static_cast(targetBuf) + 65536); CopyAndNormalizeSample, SC::DecodeInt24<0, littleEndian24> > >(sample, mpt::byte_cast(source24), 3*65536); CopySample, SC::DecodeInt24<0, littleEndian24> > >(truncated16, 65536, 1, mpt::byte_cast(source24), 65536 * 3, 1); for(size_t i = 0; i < 65536; i++) { VERIFY_EQUAL_QUIET_NONCONT(sample.sample16()[i], static_cast(i)); VERIFY_EQUAL_QUIET_NONCONT(truncated16[i], static_cast(i)); } } // Float 32-Bit { uint8 *source32 = sourceBuf; for(size_t i = 0; i < 65536; i++) { IEEE754binary32BE floatbits = IEEE754binary32BE((static_cast(i) / 65536.0f) - 0.5f); source32[i * 4 + 0] = mpt::byte_cast(floatbits.GetByte(0)); source32[i * 4 + 1] = mpt::byte_cast(floatbits.GetByte(1)); source32[i * 4 + 2] = mpt::byte_cast(floatbits.GetByte(2)); source32[i * 4 + 3] = mpt::byte_cast(floatbits.GetByte(3)); } int16 *truncated16 = static_cast(targetBuf); ModSample sample; sample.Initialize(); sample.nLength = 65536; sample.uFlags.set(CHN_16BIT); sample.pData.pSample = static_cast(targetBuf) + 65536; CopyAndNormalizeSample, SC::DecodeFloat32 > >(sample, mpt::byte_cast(source32), 4*65536); CopySample, SC::DecodeFloat32 > >(truncated16, 65536, 1, mpt::byte_cast(source32), 65536 * 4, 1); for(size_t i = 0; i < 65536; i++) { VERIFY_EQUAL_QUIET_NONCONT(sample.sample16()[i], static_cast(i - 0x8000u)); VERIFY_EQUAL_QUIET_NONCONT(std::abs(truncated16[i] - static_cast((i - 0x8000u) / 2)) <= 1, true); } } // ALaw { for(unsigned int i = 0; i < 256; ++i) { std::byte in = mpt::byte_cast(static_cast(i)); std::byte out = SC::EncodeALaw{}(SC::DecodeInt16ALaw{}(&in)); VERIFY_EQUAL_NONCONT(in, out); } VERIFY_EQUAL_NONCONT(SC::EncodeALaw{}(-32768), SC::EncodeALaw{}(-32256)); VERIFY_EQUAL_NONCONT(SC::EncodeALaw{}(-32767), SC::EncodeALaw{}(-32256)); VERIFY_EQUAL_NONCONT(SC::EncodeALaw{}(-1), SC::EncodeALaw{}(-8)); VERIFY_EQUAL_NONCONT(SC::EncodeALaw{}(0), SC::EncodeALaw{}(8)); VERIFY_EQUAL_NONCONT(SC::EncodeALaw{}(1), SC::EncodeALaw{}(8)); VERIFY_EQUAL_NONCONT(SC::EncodeALaw{}(32766), SC::EncodeALaw{}(32256)); VERIFY_EQUAL_NONCONT(SC::EncodeALaw{}(32767), SC::EncodeALaw{}(32256)); #if 0 // compare with reference impl for (int i = -32768; i <= 32767; ++i) { VERIFY_EQUAL_NONCONT(SC::EncodeALaw{}(i), mpt::byte_cast(alaw_encode(i))); } #endif } // uLaw { for(unsigned int i = 0; i < 256; ++i) { std::byte in = mpt::byte_cast(static_cast(i)); std::byte out = SC::EncodeuLaw{}(SC::DecodeInt16uLaw{}(&in)); VERIFY_EQUAL_NONCONT(in, out); } #if 0 // compare with reference impl /* bool lastMatch = true; */ for(int i = -32768; i <= 32767; ++i) { /* uint8 mine = mpt::byte_cast(SC::EncodeuLaw{}(i)); uint8 ref = ulaw_encode(i); if(lastMatch) { if(mine == ref) { VERIFY_EQUAL_NONCONT(mine, ref); lastMatch = true; } else { VERIFY_EQUAL_NONCONT(std::abs(static_cast(mine) - static_cast(ref)) <= 1, true); lastMatch = false; } } else { VERIFY_EQUAL_NONCONT(mine, ref); lastMatch = true; } */ //MPT_LOG_GLOBAL(LogNotification, "test", MPT_UFORMAT("{} {} {}")(i, ulaw_encode(i), mpt::byte_cast(SC::EncodeuLaw{}(i)))); VERIFY_EQUAL_NONCONT(SC::EncodeuLaw{}(i), mpt::byte_cast(ulaw_encode(i))); } #endif } // Range checks { int8 oneSample = 1; char *signed8 = reinterpret_cast(targetBuf); memset(signed8, 0, 4); CopySample(reinterpret_cast(targetBuf), 4, 1, reinterpret_cast(&oneSample), sizeof(oneSample), 1); VERIFY_EQUAL_NONCONT(signed8[0], 1); VERIFY_EQUAL_NONCONT(signed8[1], 0); VERIFY_EQUAL_NONCONT(signed8[2], 0); VERIFY_EQUAL_NONCONT(signed8[3], 0); } // Dither { std::vector buffer(64); DithersOpenMPT dithers(mpt::global_random_device(), 2 /* DitherModPlug */ , 2); for(std::size_t i = 0; i < 64; ++i) { std::visit( [&](auto &dither) { buffer[i] = dither.template process<16>(0, buffer[i]); }, dithers.Variant() ); } std::vector expected = { 727, -557, -552, -727, 439, 405, 703, -337, 235, -776, -458, 905, -110, 158, 374, -362, 283, 306, 710, 304, -608, 536, -501, -593, -349, 812, 916, 53, -953, 881, -236, -20, -623, -895, -302, -415, 899, -948, -766, -186, -390, -169, 253, -622, -769, -1001, 1019, 787, -239, 718, -423, 988, -91, 763, -933, -510, 484, 794, -340, 552, 866, -608, 35, 395}; for(std::size_t i = 0; i < 64; ++i) { VERIFY_EQUAL_QUIET_NONCONT(buffer[i], expected[i]); } } } } // namespace Test OPENMPT_NAMESPACE_END #else //Case: ENABLE_TESTS is not defined. OPENMPT_NAMESPACE_BEGIN namespace Test { void DoTests() { return; } } // namespace Test OPENMPT_NAMESPACE_END #endif libopenmpt-0.6.1+release.autotools/test/test.h0000644000175000017500000000064214052666041016371 00000000000000/* * test.h * ------ * Purpose: Unit tests for OpenMPT. * Notes : We need FAAAAAAAR more unit tests! * Authors: Olivier Lapicque * OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" OPENMPT_NAMESPACE_BEGIN namespace Test { void DoTests(); } // namespace Test OPENMPT_NAMESPACE_END libopenmpt-0.6.1+release.autotools/test/TestTools.h0000644000175000017500000000076614052666041017361 00000000000000/* * TestTools.h * ----------- * Purpose: Unit test framework. * Notes : * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "TestToolsTracker.h" #include "TestToolsLib.h" #include "../common/mptPathString.h" OPENMPT_NAMESPACE_BEGIN #ifdef ENABLE_TESTS namespace Test { mpt::PathString GetPathPrefix(); } // namespace Test OPENMPT_NAMESPACE_END #endif libopenmpt-0.6.1+release.autotools/test/TestToolsLib.cpp0000644000175000017500000001504314056674306020344 00000000000000/* * TestToolsLib.cpp * ---------------- * Purpose: Unit test framework for libopenmpt. * Notes : Currently somewhat unreadable :/ * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "TestToolsLib.h" #ifdef ENABLE_TESTS #ifndef MODPLUG_TRACKER #include #include #include OPENMPT_NAMESPACE_BEGIN namespace Test { void mpt_test_reporter::case_run(const mpt::source_location& loc) { #if !MPT_OS_DJGPP std::cout << "TEST..: " << MPT_AFORMAT("{}({}):")(loc.file_name() ? loc.file_name() : "", loc.line()) << ": " << std::endl; #else MPT_UNUSED(loc); #endif } void mpt_test_reporter::case_run(const mpt::source_location& loc, const char* text_e) { #if !MPT_OS_DJGPP std::cout << "TEST..: " << MPT_AFORMAT("{}({}): {}")(loc.file_name() ? loc.file_name() : "", loc.line(), text_e) << ": " << std::endl; #else MPT_UNUSED(loc); MPT_UNUSED(text_e); #endif } void mpt_test_reporter::case_run(const mpt::source_location& loc, const char* text_ex, const char* text_e) { if(text_ex) { #if !MPT_OS_DJGPP std::cout << "TEST..: " << MPT_AFORMAT("{}({}): {} throws {}")(loc.file_name() ? loc.file_name() : "", loc.line(), text_e, text_ex) << ": " << std::endl; #else MPT_UNUSED(loc); MPT_UNUSED(text_ex); MPT_UNUSED(text_e); #endif } else { #if !MPT_OS_DJGPP std::cout << "TEST..: " << MPT_AFORMAT("{}({}): {} throws")(loc.file_name() ? loc.file_name() : "", loc.line(), text_e) << ": " << std::endl; #else MPT_UNUSED(loc); MPT_UNUSED(text_ex); MPT_UNUSED(text_e); #endif } } void mpt_test_reporter::case_run(const mpt::source_location& loc, const char* text_a, const char* text_cmp, const char* text_b) { #if !MPT_OS_DJGPP std::cout << "TEST..: " << MPT_AFORMAT("{}({}): {} {} {}")(loc.file_name() ? loc.file_name() : "", loc.line(), text_a, text_cmp, text_b) << ": " << std::endl; #else MPT_UNUSED(loc); MPT_UNUSED(text_a); MPT_UNUSED(text_cmp); MPT_UNUSED(text_b); #endif } void mpt_test_reporter::case_result(const mpt::source_location& loc, const mpt::test::result& result) { MPT_UNUSED(loc); if(std::holds_alternative(result.info)) { #if !MPT_OS_DJGPP std::cout << "RESULT: PASS" << std::endl; #endif } else if(std::holds_alternative(result.info)) { fail_count++; std::cout << "RESULT: FAIL" << std::endl; std::cout.flush(); std::cerr << "FAIL: " << "FAILURE: " << std::get(result.info).text << std::endl; std::cerr.flush(); } else if(std::holds_alternative(result.info)) { fail_count++; std::cout << "RESULT: FAIL" << std::endl; std::cout.flush(); std::cerr << "FAIL: " << "UNEXPECTED EXCEPTION: " << std::get(result.info).text << std::endl; std::cerr.flush(); } else { fail_count++; std::cout << "RESULT: FAIL" << std::endl; std::cout.flush(); std::cerr << "FAIL: " << "UNKOWN" << std::endl; std::cerr.flush(); } } int fail_count = 0; static std::string remove_newlines(std::string str) { return mpt::replace(mpt::replace(str, std::string("\n"), std::string(" ")), std::string("\r"), std::string(" ")); } Testcase::Testcase(Fatality fatality, Verbosity verbosity, const char * const desc, const mpt::source_location &loc) : fatality(fatality) , verbosity(verbosity) , desc(desc) , loc(loc) { return; } std::string Testcase::AsString() const { return MPT_AFORMAT("{}({}): {}")(loc.file_name() ? loc.file_name() : "", loc.line(), remove_newlines(desc)); } void Testcase::ShowStart() const { switch(verbosity) { case VerbosityQuiet: break; case VerbosityNormal: #if !MPT_OS_DJGPP std::cout << "TEST..: " << AsString() << ": " << std::endl; #endif break; case VerbosityVerbose: #if !MPT_OS_DJGPP std::cout << "TEST..: " << AsString() << ": " << std::endl; #endif break; } } void Testcase::ShowProgress(const char * text) const { switch(verbosity) { case VerbosityQuiet: break; case VerbosityNormal: break; case VerbosityVerbose: #if !MPT_OS_DJGPP std::cout << "TEST..: " << AsString() << ": " << text << std::endl; #else MPT_UNUSED_VARIABLE(text); #endif break; } } void Testcase::ShowPass() const { switch(verbosity) { case VerbosityQuiet: break; case VerbosityNormal: #if !MPT_OS_DJGPP std::cout << "RESULT: PASS" << std::endl; #endif break; case VerbosityVerbose: #if !MPT_OS_DJGPP std::cout << "PASS..: " << AsString() << std::endl; #endif break; } } void Testcase::ShowFail(bool exception, const char * const text) const { switch(verbosity) { case VerbosityQuiet: break; case VerbosityNormal: std::cout << "RESULT: FAIL" << std::endl; break; case VerbosityVerbose: std::cout << "FAIL..: " << AsString() << std::endl; break; } std::cout.flush(); if(!exception) { if(!text || (text && std::string(text).empty())) { std::cerr << "FAIL: " << AsString() << std::endl; } else { std::cerr << "FAIL: " << AsString() << " : " << text << std::endl; } } else { if(!text || (text && std::string(text).empty())) { std::cerr << "FAIL: " << AsString() << " EXCEPTION!" << std::endl; } else { std::cerr << "FAIL: " << AsString() << " EXCEPTION: " << text << std::endl; } } std::cerr.flush(); } void Testcase::ReportPassed() { ShowPass(); } void Testcase::ReportFailed() { fail_count++; ReportException(); } void Testcase::ReportException() { try { throw; // get the exception } catch(TestFailed & e) { ShowFail(false, e.values.c_str()); if(fatality == FatalityStop) { throw; // rethrow } } catch(std::exception & e) { ShowFail(true, e.what()); throw; // rethrow } catch(...) { ShowFail(true); throw; // rethrow } } } // namespace Test #if defined(MPT_ASSERT_HANDLER_NEEDED) MPT_NOINLINE void AssertHandler(const mpt::source_location &loc, const char *expr, const char *msg) { Test::fail_count++; if(msg) { mpt::log::GlobalLogger().SendLogMessage(loc, LogError, "ASSERT", U_("ASSERTION FAILED: ") + mpt::ToUnicode(mpt::Charset::ASCII, msg) + U_(" (") + mpt::ToUnicode(mpt::Charset::ASCII, expr) + U_(")") ); } else { mpt::log::GlobalLogger().SendLogMessage(loc, LogError, "ASSERT", U_("ASSERTION FAILED: ") + mpt::ToUnicode(mpt::Charset::ASCII, expr) ); } #if defined(MPT_BUILD_FATAL_ASSERTS) std::abort(); #endif // MPT_BUILD_FATAL_ASSERTS } #endif // MPT_ASSERT_HANDLER_NEEDED OPENMPT_NAMESPACE_END #endif // !MODPLUG_TRACKER #endif // ENABLE_TESTS libopenmpt-0.6.1+release.autotools/test/TestToolsLib.h0000644000175000017500000002114414053176735020010 00000000000000/* * TestToolsLib.h * -------------- * Purpose: Unit test framework for libopenmpt. * Notes : This is more complex than the OpenMPT version because we cannot * rely on a debugger and have to deal with exceptions ourselves. * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #ifdef ENABLE_TESTS #ifndef MODPLUG_TRACKER //#define MPT_TEST_CXX11 #include "mpt/test/test.hpp" #include #include "mpt/base/bit.hpp" #include "openmpt/base/FlagSet.hpp" #include "../soundlib/Snd_defs.h" OPENMPT_NAMESPACE_BEGIN namespace Test { class mpt_test_reporter : public mpt::test::silent_reporter { public: mpt_test_reporter() = default; ~mpt_test_reporter() override = default; public: void case_run(const mpt::source_location & loc) override; void case_run(const mpt::source_location & loc, const char * text_e) override; void case_run(const mpt::source_location & loc, const char * text_ex, const char * text_e) override; void case_run(const mpt::source_location & loc, const char * text_a, const char * text_cmp, const char * text_b) override; void case_result(const mpt::source_location & loc, const mpt::test::result & result) override; }; extern int fail_count; enum Verbosity { VerbosityQuiet, VerbosityNormal, VerbosityVerbose, }; enum Fatality { FatalityContinue, FatalityStop }; struct TestFailed { std::string values; TestFailed(const std::string &values) : values(values) { } TestFailed() { } }; } // namespace Test template struct ToStringHelper { std::string operator () (const T &x) { return mpt::afmt::val(x); } }; #ifdef MPT_TEST_CXX11 template<> struct ToStringHelper { std::string operator () (const mpt::endian &x) { if(x == mpt::endian::big) return "big"; if(x == mpt::endian::little) return "little"; return "unknown"; } }; template struct ToStringHelper > { std::string operator () (const FlagSet &x) { return mpt::afmt::val(x.GetRaw()); } }; template struct ToStringHelper > { std::string operator () (const enum_value_type &x) { return mpt::afmt::val(x.as_bits()); } }; template struct ToStringHelper > { std::string operator () (const std::pair &x) { return std::string("{") + mpt::afmt::val(x.first) + std::string(",") + mpt::afmt::val(x.second) + std::string("}"); } }; template struct ToStringHelper > { std::string operator () (const FPInt &x) { return std::string("FPInt<") + mpt::afmt::val(FRACT) + std::string(",") + mpt::afmt::val(typeid(T).name()) + std::string(">{") + mpt::afmt::val(x.GetInt()) + std::string(".") + mpt::afmt::val(x.GetFract()) + std::string("}"); } }; template<> struct ToStringHelper { std::string operator () (const SamplePosition &x) { return mpt::afmt::val(x.GetInt()) + std::string(".") + std::string("0x") + mpt::afmt::hex0<8>(x.GetFract()); } }; #endif // MPT_TEST_CXX11 namespace Test { class Testcase { private: Fatality const fatality; Verbosity const verbosity; const char * const desc; mpt::source_location const loc; public: Testcase(Fatality fatality, Verbosity verbosity, const char * const desc, const mpt::source_location &loc); public: std::string AsString() const; void ShowStart() const; void ShowProgress(const char * text) const; void ShowPass() const; void ShowFail(bool exception = false, const char * const text = nullptr) const; void ReportPassed(); void ReportFailed(); void ReportException(); private: template inline bool IsEqual(const Tx &x, const Ty &y, std::false_type, std::false_type) { return (x == y); } template inline bool IsEqual(const Tx &x, const Ty &y, std::false_type, std::true_type) { return (x == y); } template inline bool IsEqual(const Tx &x, const Ty &y, std::true_type, std::false_type) { return (x == y); } template inline bool IsEqual(const Tx &x, const Ty &y, std::true_type /* is_integer */, std::true_type /* is_integer */ ) { // Avoid signed-unsigned-comparison warnings and test equivalence in case of either type conversion direction. return ((x == static_cast(y)) && (static_cast(x) == y)); } template inline bool IsEqualEpsilon(const Tx &x, const Ty &y, const Teps &eps) { return std::abs(x - y) <= eps; } public: #ifdef MPT_TEST_CXX11 private: template MPT_NOINLINE void TypeCompareHelper(const Tx &x, const Ty &y) { if(!IsEqual(x, y, std::is_integral(), std::is_integral())) { throw TestFailed(MPT_AFORMAT("{} != {}")(ToStringHelper()(x), ToStringHelper()(y))); //throw TestFailed(); } } template MPT_NOINLINE void TypeCompareHelper(const Tx &x, const Ty &y, const Teps &eps) { if(!IsEqualEpsilon(x, y, eps)) { throw TestFailed(MPT_AFORMAT("{} != {}")(ToStringHelper()(x), ToStringHelper()(y))); //throw TestFailed(); } } public: template MPT_NOINLINE void operator () (const Tfx &fx, const Tfy &fy) { ShowStart(); try { ShowProgress("Calculate x ..."); const auto x = fx(); ShowProgress("Calculate y ..."); const auto y = fy(); ShowProgress("Compare ..."); TypeCompareHelper(x, y); ReportPassed(); } catch(...) { ReportFailed(); } } template MPT_NOINLINE void operator () (const Tfx &fx, const Tfy &fy, const Teps &eps) { ShowStart(); try { ShowProgress("Calculate x ..."); const auto x = fx(); ShowProgress("Calculate y ..."); const auto y = fy(); ShowProgress("Compare ..."); TypeCompareHelper(x, y, eps); ReportPassed(); } catch(...) { ReportFailed(); } } #define VERIFY_EQUAL(x,y) Test::Testcase(Test::FatalityContinue, Test::VerbosityNormal, #x " == " #y , MPT_SOURCE_LOCATION_CURRENT() )( [&](){return (x) ;}, [&](){return (y) ;} ) #define VERIFY_EQUAL_NONCONT(x,y) Test::Testcase(Test::FatalityStop , Test::VerbosityNormal, #x " == " #y , MPT_SOURCE_LOCATION_CURRENT() )( [&](){return (x) ;}, [&](){return (y) ;} ) #define VERIFY_EQUAL_QUIET_NONCONT(x,y) Test::Testcase(Test::FatalityStop , Test::VerbosityQuiet , #x " == " #y , MPT_SOURCE_LOCATION_CURRENT() )( [&](){return (x) ;}, [&](){return (y) ;} ) #define VERIFY_EQUAL_EPS(x,y,eps) Test::Testcase(Test::FatalityContinue, Test::VerbosityNormal, #x " == " #y , MPT_SOURCE_LOCATION_CURRENT() )( [&](){return (x) ;}, [&](){return (y) ;}, (eps) ) #else public: template MPT_NOINLINE void operator () (const Tx &x, const Ty &y) { ShowStart(); try { if(!IsEqual(x, y, std::is_integral(), std::is_integral())) { //throw TestFailed(MPT_AFORMAT("{} != {}")(x, y)); throw TestFailed(); } ReportPassed(); } catch(...) { ReportFailed(); } } template MPT_NOINLINE void operator () (const Tx &x, const Ty &y, const Teps &eps) { ShowStart(); try { if(!IsEqualEpsilon(x, y, eps)) { //throw TestFailed(MPT_AFORMAT("{} != {}")(x, y)); throw TestFailed(); } ReportPassed(); } catch(...) { ReportFailed(); } } #define VERIFY_EQUAL(x,y) Test::Testcase(Test::FatalityContinue, Test::VerbosityNormal, #x " == " #y , MPT_SOURCE_LOCATION_CURRENT() )( (x) , (y) ) #define VERIFY_EQUAL_NONCONT(x,y) Test::Testcase(Test::FatalityStop , Test::VerbosityNormal, #x " == " #y , MPT_SOURCE_LOCATION_CURRENT() )( (x) , (y) ) #define VERIFY_EQUAL_QUIET_NONCONT(x,y) Test::Testcase(Test::FatalityStop , Test::VerbosityQuiet , #x " == " #y , MPT_SOURCE_LOCATION_CURRENT() )( (x) , (y) ) #define VERIFY_EQUAL_EPS(x,y,eps) Test::Testcase(Test::FatalityContinue, Test::VerbosityNormal, #x " == " #y , MPT_SOURCE_LOCATION_CURRENT() )( (x) , (y), (eps) ) #endif }; #define DO_TEST(func) \ do { \ Test::Testcase test(Test::FatalityStop, Test::VerbosityVerbose, #func , MPT_SOURCE_LOCATION_CURRENT() ); \ try { \ test.ShowStart(); \ fail_count = 0; \ func(); \ if(fail_count > 0) { \ throw Test::TestFailed(); \ } \ test.ReportPassed(); \ } catch(...) { \ test.ReportException(); \ } \ } while(0) } // namespace Test OPENMPT_NAMESPACE_END #endif // !MODPLUG_TRACKER #endif // ENABLE_TESTS libopenmpt-0.6.1+release.autotools/test/TestToolsTracker.h0000644000175000017500000000342214052666041020665 00000000000000/* * TestToolsTracker.h * ------------------ * Purpose: Unit test framework for OpenMPT. * Notes : Really basic functionality that relies on a debugger that catches * exceptions and breaks right at the spot where it gets thrown. * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #ifdef ENABLE_TESTS #ifdef MODPLUG_TRACKER #include "mpt/test/test.hpp" OPENMPT_NAMESPACE_BEGIN namespace Test { #if MPT_COMPILER_MSVC // With MSVC, break directly using __debugbreak intrinsic instead of calling DebugBreak which breaks one stackframe deeper than we want #define MyDebugBreak() __debugbreak() #else #define MyDebugBreak() DebugBreak() #endif class mpt_test_reporter : public mpt::test::silent_reporter { public: mpt_test_reporter() = default; ~mpt_test_reporter() override = default; public: inline void immediate_breakpoint() override { MyDebugBreak(); } }; // Verify that given parameters are 'equal'. Break directly into the debugger if not. // The exact meaning of equality is based on operator== of the compared types. #define VERIFY_EQUAL(x,y) \ do { \ if(!((x) == (y))) { \ MyDebugBreak(); \ } \ } while(0) \ /**/ // Like VERIFY_EQUAL, only differs for libopenmpt #define VERIFY_EQUAL_NONCONT VERIFY_EQUAL // Like VERIFY_EQUAL, only differs for libopenmpt #define VERIFY_EQUAL_QUIET_NONCONT VERIFY_EQUAL #define VERIFY_EQUAL_EPS(x,y,eps) \ do { \ if(std::abs((x) - (y)) > (eps)) { \ MyDebugBreak(); \ } \ } while(0) \ /**/ #define DO_TEST(func) \ do { \ if(IsDebuggerPresent()) { \ func(); \ } \ } while(0) \ /**/ } // namespace Test OPENMPT_NAMESPACE_END #endif // MODPLUG_TRACKER #endif // ENABLE_TESTS libopenmpt-0.6.1+release.autotools/test/test.xm0000644000175000017500000001424412110562762016567 00000000000000Extended Module: Test Module OpenMPT 1.21.01.15 ‹ @ Wœ¯€€€€€€€€€€€€€€€€€€€„À€„Á€„€„À„Ä€„Å€„Æ€„Ç€„È€„É€„Ê€„Ë€„Ì€„Í€„΀„Ï€€€€€€€€€€€€=-eÿ @(@-:` 8 9&%=@.2%46  ? # Pulse Sample Empty Sample @€Unassigned Sample îtextÔOpenMPT Module Loader Test Suite Version 1 by Saga Musix Version History --------------- Version 3 Added pitch wheel depth to instrument settings Version 2 Added unassigned sample Version 1 initial test suiteMIDI FFFC9c n v9c n 0Cc pF0F001zF0F003zF0F00100F0F00108F0F00110F0F00118F0F00120F0F00128F0F00130F0F00138F0F00140F0F00148F0F00150F0F00158F0F00160F0F00168F0F00170F0F00178F0F00200F0F00208F0F00210F0F00218F0F00220F0F00228F0F00230F0F00238F0F00240F0F00248F0F00250F0F00258F0F00260F0F00268F0F00270F0F00278PNAM@First PatternSecond PatternCNAM(First ChannelSecond ChannelFX00°OMXD,“>ï First PluginEcho??¦>¦>DWRT¸…>PROGÿÿÿÿCHFXXTPM..RV°.PiM..CM..PM@..BM...P€..VG@..OF...R..SC..SR..MFÿNREPÿNREAÿNREVÿLTTPHEVPHOVPSTPM..TD‹.BPR.MPR ...C..MT.MMP.VWCVWSL!.APSVTSV*.VGD€..PR.FSMAMIM!hlibopenmpt-0.6.1+release.autotools/test/test.s3m0000644000175000017500000000160013303071630016627 00000000000000S3M_Test__________________X@'QSCRM þ!üŸŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿ ) /%*€€€€€€€€€€€€€€Filename_1_X/<< )#Sample_1__________________XSCRS@EmptySCRSFilename_3_X3€>Stereo / 16-BitSCRS7ÀðÒ³:« adlibSCRI¡ Aƒ {A@ƒ" þA€ƒQAÀƒDƒUƒfƒwƒˆƒ ™ƒ ªƒ »ƒ ̃ ݃îƒÿƒƒƒƒƒƒƒƒƒƒƒQ"€‚ƒÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€€€€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€€€€€€€€€€€€€€€€libopenmpt-0.6.1+release.autotools/test/test.mod0000644000175000017500000001243013774200751016721 00000000000000MOD_Test___________XSample_1_____________Xn@@@OpenMPT Module LoaderTest Suite Version 1@@@@@@@@@@@@@@@@@@@@@@@@@@@@M.K.á á`aa¬ Ö ¬`Öoâðþ   . @ S h}”¬Ö`¬c(8 H( `aaãÕÆ¸©š™””’‘ŽŽŠ‹Œ‹Ž”—™› ¤§ª°´¸½ÃÇÌÒ×Ýâéðôùÿ  %)-158<;9764310.-+*('%#"! þýüûúùø÷õôóòñðïîîììêêééççåæääâãàâßàÝßÜÞÛÝÚÜÙÛ×ÚÖÙÕÙÔØÓ×Ò×Ñ×Ð×ÍÚ¶®(ã3-IF,.5äéñâ¼±®«—ˆˆ‚€‚‰Œ’”ž¤­±»ÃÌÔÞåíóû "',048??@AABBCCDDDEEEEEEEEEEEEEEDDDDCCBBAA@@??>==<;;:98776543210/.-,+*)('&%$#"!  ÿþýüûúúùø÷ööõôóóòñððïïîííìëëêêééèèççææååääããããââááááàààßßßßÞÞÞÞÝÝÝÝÝÝÜÝÜÝÜÝÛÜÛÝÚÝÚÝÙݸåÉòóúçåöÿôÜÌÈÌÌÆ½¹¸¸··¹¼¾¾¿ÃÉÎÑÓÖÚàæêíïòöúýÿ  !""##$$$%%%&&&&&'''''''''''''&&&&&%%%%$$$###""!!!  ÿÿþþýýüüûûûúúùùùøøø÷÷÷öööõõõõôôôôóóóóóòòòòññññññññðððððððððïïïïïïïïïïïïïïïïïïïïïïîðîðîðàïëìòú õíîõùöíæäääåãââããããäæéëëìíïñôõ÷øùúüýþÿ libopenmpt-0.6.1+release.autotools/test/test.mptm0000644000175000017500000002601713722044575017130 00000000000000IMPMTest Module_____________X ‘ˆý@‹€¨-ˆd @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@þþÕÿ )#y#É#$i$·$Î>©FÎ>ð©DÎ>IªÚÎ>“«mÎ>ª«##?ó†g+?›~i@nÙfAã1#SB–ëZE£ økFpn¹F펡¿FÄÙÐF°e* 0HãþQMæFFFC9c n v9c n 0Cc pF0F001zF0F003zAcnzF0F00100F0F00108F0F00110F0F00118F0F00120F0F00128F0F00130F0F00138F0F00140F0F00148F0F00150F0F00158F0F00160F0F00168F0F00170F0F00178F0F00200F0F00208F0F00210F0F00218F0F00220F0F00228F0F00230F0F00238F0F00240F0F00248F0F00250F0F00258F0F00260F0F00268F0F00270F0F00278PNAM@First PatternSecond PatternCNAMxFirst ChannelSecond ChannelLast Channel______XFX00°OMXD,“>ï First PluginEcho??¦>¦>DWRT¸…>PROGÿÿÿÿCHFXOpenMPT Module Loader Test Suite Version 11 by Saga Musix Version History --------------- Version 11 Channel colors Version 10 Per-sequence restart position Version 9 Tempo Swing Version 9 Fixed point tempo Version 8 Added song artist and global resampling mode Version 7 Added sample cues Version 6 Added external sample Version 5 Added pitch wheel depth to instrument settings Version 4 Increased number of channels to test ChnS extension, duplicated instrument to check extended instrument property decoding Version 3 Added Stereo / 16-Bit and empty sample, poly aftertouch macro Version 2 Changed PC note, added normal pattern command Version 1 initial test suiteIMPI B@0)QFirst Instrument²ä?      !"#$%&'()*+,-./0123456789:;Hc=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvw @:-` ÷ ÷.24ù: BI÷PY ^÷e lõr wò ˆóŽ `àÀIMPI B@0)QSecond Instrument²ä?      !"#$%&'()*+,-./0123456789:;Hc=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvw @:-` ÷ ÷.24ù: BI÷PY ^÷e lõr wò ˆóŽ `àÀIMPS¹ ¨)#æ$IMPS@@Stereo / 16-Bit €>ñ$IMPS@@Empty « %IMPSExternal¹Overridden Name€@*7u'*7 %*%FF' ‚H-dÿ %¤+ €áh ÿl€ .\test.flacXTPM..RV°°.PiM..CM..PM@@..BM...RHEVPHOVP..OFDWPM...P@@..SC ..SR<<..MFLTTP‚‚PTTFÐÐ..EPLL.[PP˜ .24:BIPY^elrwˆŽš¥¦ª¯¶¹¼½ÃÇÌÏÔÙßæëïùú '+.2>DJNXakqx„‰Œ”•¡¨« .24:BIPY^elrwˆŽš¥¦ª¯¶¹¼½ÃÇÌÏÔÙßæëïùú '+.2>DJNXakqx„‰Œ”•¡¨«.[EPL 8 9&%=@%6,0!-*)+),,2!.!',0"$(.#/-$!,#!,*'".*+($ 8 9&%=@%6,0!-*)+),,2!.!',0"$(.#/-$!,#!,*'".*+($NREVSTPMDTFRç...CFSnhC @ @ @ @ @@..MT.MMP.VWCVWSL).APSVTSV*..PRRSMPCUES& (08@ CUES& (08@CUES&  (08@2SWNGýÿ¿@À@À@.FSM ÿÿÿÿÿ@ AUTHTesterAMIM!hCCOLÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ228mptm¤ 228mptPc¤ 228mptP¤ÿ eäÿpgmäÿ¾Ï¿@datalRPB.…RPM.•SWNG¥X228mptP¤+ücÈdatal˜p}í!num 228mptSeqC¤÷228mptSeq¤ëìFirst SequenceþÿþÿutnxHlÀaÈrà228mptSeq¤ïüSecond SequenceutnxLlÄaÌränxc|€9¹= mptPclemptSeqCÑAò)libopenmpt-0.6.1+release.autotools/Makefile.am0000644000175000017500000007206614107232445016325 00000000000000ACLOCAL_AMFLAGS = -I m4 --install EXTRA_DIST = EXTRA_DIST += .clang-format EXTRA_DIST += m4/emptydir EXTRA_DIST += libopenmpt/libopenmpt.pc.in EXTRA_DIST += LICENSE EXTRA_DIST += README.md EXTRA_DIST += Doxyfile.in EXTRA_DIST += doc/contributing.md EXTRA_DIST += doc/libopenmpt_styleguide.md EXTRA_DIST += doc/module_formats.md EXTRA_DIST += doc/openmpt_styleguide.md EXTRA_DIST += libopenmpt/.clang-format EXTRA_DIST += libopenmpt/dox/changelog.md EXTRA_DIST += libopenmpt/dox/dependencies.md EXTRA_DIST += libopenmpt/dox/gettingstarted.md EXTRA_DIST += libopenmpt/dox/index.dox EXTRA_DIST += libopenmpt/dox/packaging.md EXTRA_DIST += libopenmpt/dox/tests.md EXTRA_DIST += libopenmpt/libopenmpt_version.mk EXTRA_DIST += openmpt123/.clang-format EXTRA_DIST += src/mpt/.clang-format EXTRA_DIST += src/mpt/LICENSE.BSD-3-Clause.txt EXTRA_DIST += src/mpt/LICENSE.BSL-1.0.txt EXTRA_DIST += test/test.xm EXTRA_DIST += test/test.s3m EXTRA_DIST += test/test.mod EXTRA_DIST += test/test.mptm EXTRA_DIST += man/openmpt123.1 EXTRA_DIST += examples/.clang-format EXTRA_DIST += libopenmpt/bindings/freebasic/libopenmpt.bi MOSTLYCLEANFILES = dist_doc_DATA = dist_doc_DATA += LICENSE dist_doc_DATA += README.md nobase_dist_doc_DATA = nobase_dist_doc_DATA += examples/libopenmpt_example_cxx.cpp nobase_dist_doc_DATA += examples/libopenmpt_example_c_mem.c nobase_dist_doc_DATA += examples/libopenmpt_example_c_unsafe.c nobase_dist_doc_DATA += examples/libopenmpt_example_c.c nobase_dist_doc_DATA += examples/libopenmpt_example_c_probe.c nobase_dist_doc_DATA += examples/libopenmpt_example_c_stdout.c bin_PROGRAMS = check_PROGRAMS = lib_LTLIBRARIES = if ENABLE_TESTS TESTS = libopenmpttest endif if ENABLE_EXAMPLES check_PROGRAMS += libopenmpt_example_c_stdout check_PROGRAMS += libopenmpt_example_c_probe if HAVE_PORTAUDIO check_PROGRAMS += libopenmpt_example_c check_PROGRAMS += libopenmpt_example_c_mem check_PROGRAMS += libopenmpt_example_c_unsafe endif if HAVE_PORTAUDIOCPP check_PROGRAMS += libopenmpt_example_cxx endif libopenmpt_example_c_stdout_SOURCES = examples/libopenmpt_example_c_stdout.c libopenmpt_example_c_probe_SOURCES = examples/libopenmpt_example_c_probe.c if HAVE_PORTAUDIO libopenmpt_example_c_SOURCES = examples/libopenmpt_example_c.c libopenmpt_example_c_mem_SOURCES = examples/libopenmpt_example_c_mem.c libopenmpt_example_c_unsafe_SOURCES = examples/libopenmpt_example_c_unsafe.c endif if HAVE_PORTAUDIOCPP libopenmpt_example_cxx_SOURCES = examples/libopenmpt_example_cxx.cpp endif libopenmpt_example_c_stdout_CPPFLAGS = libopenmpt_example_c_probe_CPPFLAGS = if HAVE_PORTAUDIO libopenmpt_example_c_CPPFLAGS = $(PORTAUDIO_CFLAGS) libopenmpt_example_c_mem_CPPFLAGS = $(PORTAUDIO_CFLAGS) libopenmpt_example_c_unsafe_CPPFLAGS = $(PORTAUDIO_CFLAGS) endif if HAVE_PORTAUDIOCPP libopenmpt_example_cxx_CPPFLAGS = $(MINGWSTDTHREADS_CPPFLAGS) $(PORTAUDIOCPP_CFLAGS) endif libopenmpt_example_c_stdout_CFLAGS = $(WIN32_CONSOLE_CFLAGS) libopenmpt_example_c_probe_CFLAGS = $(WIN32_CONSOLE_CFLAGS) if HAVE_PORTAUDIO libopenmpt_example_c_CFLAGS = $(WIN32_CONSOLE_CFLAGS) libopenmpt_example_c_mem_CFLAGS = $(WIN32_CONSOLE_CFLAGS) libopenmpt_example_c_unsafe_CFLAGS = $(WIN32_CONSOLE_CFLAGS) endif if HAVE_PORTAUDIOCPP libopenmpt_example_cxx_CXXFLAGS = $(WIN32_CONSOLE_CXXFLAGS) endif libopenmpt_example_c_stdout_LDADD = $(lib_LTLIBRARIES) libopenmpt_example_c_probe_LDADD = $(lib_LTLIBRARIES) if HAVE_PORTAUDIO libopenmpt_example_c_LDADD = $(lib_LTLIBRARIES) $(PORTAUDIO_LIBS) libopenmpt_example_c_mem_LDADD = $(lib_LTLIBRARIES) $(PORTAUDIO_LIBS) libopenmpt_example_c_unsafe_LDADD = $(lib_LTLIBRARIES) $(PORTAUDIO_LIBS) endif if HAVE_PORTAUDIOCPP libopenmpt_example_cxx_LDADD = $(lib_LTLIBRARIES) $(PORTAUDIOCPP_LIBS) endif endif pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = nobase_include_HEADERS = MPT_FILES_SRC_MPT = MPT_FILES_SRC_MPT += src/mpt/audio/sample.hpp MPT_FILES_SRC_MPT += src/mpt/audio/span.hpp MPT_FILES_SRC_MPT += src/mpt/base/algorithm.hpp MPT_FILES_SRC_MPT += src/mpt/base/aligned_array.hpp MPT_FILES_SRC_MPT += src/mpt/base/alloc.hpp MPT_FILES_SRC_MPT += src/mpt/base/arithmetic_shift.hpp MPT_FILES_SRC_MPT += src/mpt/base/array.hpp MPT_FILES_SRC_MPT += src/mpt/base/bit.hpp MPT_FILES_SRC_MPT += src/mpt/base/check_platform.hpp MPT_FILES_SRC_MPT += src/mpt/base/compiletime_warning.hpp MPT_FILES_SRC_MPT += src/mpt/base/constexpr_throw.hpp MPT_FILES_SRC_MPT += src/mpt/base/detect.hpp MPT_FILES_SRC_MPT += src/mpt/base/detect_compiler.hpp MPT_FILES_SRC_MPT += src/mpt/base/detect_libc.hpp MPT_FILES_SRC_MPT += src/mpt/base/detect_libcxx.hpp MPT_FILES_SRC_MPT += src/mpt/base/detect_os.hpp MPT_FILES_SRC_MPT += src/mpt/base/detect_quirks.hpp MPT_FILES_SRC_MPT += src/mpt/base/floatingpoint.hpp MPT_FILES_SRC_MPT += src/mpt/base/integer.hpp MPT_FILES_SRC_MPT += src/mpt/base/macros.hpp MPT_FILES_SRC_MPT += src/mpt/base/math.hpp MPT_FILES_SRC_MPT += src/mpt/base/memory.hpp MPT_FILES_SRC_MPT += src/mpt/base/namespace.hpp MPT_FILES_SRC_MPT += src/mpt/base/numbers.hpp MPT_FILES_SRC_MPT += src/mpt/base/numeric.hpp MPT_FILES_SRC_MPT += src/mpt/base/pointer.hpp MPT_FILES_SRC_MPT += src/mpt/base/preprocessor.hpp MPT_FILES_SRC_MPT += src/mpt/base/saturate_cast.hpp MPT_FILES_SRC_MPT += src/mpt/base/saturate_round.hpp MPT_FILES_SRC_MPT += src/mpt/base/secure.hpp MPT_FILES_SRC_MPT += src/mpt/base/semantic_version.hpp MPT_FILES_SRC_MPT += src/mpt/base/source_location.hpp MPT_FILES_SRC_MPT += src/mpt/base/span.hpp MPT_FILES_SRC_MPT += src/mpt/base/utility.hpp MPT_FILES_SRC_MPT += src/mpt/base/version.hpp MPT_FILES_SRC_MPT += src/mpt/base/wrapping_divide.hpp MPT_FILES_SRC_MPT += src/mpt/binary/base64.hpp MPT_FILES_SRC_MPT += src/mpt/binary/base64url.hpp MPT_FILES_SRC_MPT += src/mpt/binary/hex.hpp MPT_FILES_SRC_MPT += src/mpt/check/libc.hpp MPT_FILES_SRC_MPT += src/mpt/check/mfc.hpp MPT_FILES_SRC_MPT += src/mpt/check/windows.hpp MPT_FILES_SRC_MPT += src/mpt/crc/crc.hpp #MPT_FILES_SRC_MPT += src/mpt/crypto/exception.hpp #MPT_FILES_SRC_MPT += src/mpt/crypto/hash.hpp #MPT_FILES_SRC_MPT += src/mpt/crypto/jwk.hpp MPT_FILES_SRC_MPT += src/mpt/detect/dl.hpp MPT_FILES_SRC_MPT += src/mpt/detect/ltdl.hpp MPT_FILES_SRC_MPT += src/mpt/detect/mfc.hpp MPT_FILES_SRC_MPT += src/mpt/detect/nlohmann_json.hpp MPT_FILES_SRC_MPT += src/mpt/endian/floatingpoint.hpp MPT_FILES_SRC_MPT += src/mpt/endian/int24.hpp MPT_FILES_SRC_MPT += src/mpt/endian/integer.hpp MPT_FILES_SRC_MPT += src/mpt/environment/environment.hpp MPT_FILES_SRC_MPT += src/mpt/exception_text/exception_text.hpp MPT_FILES_SRC_MPT += src/mpt/format/default_floatingpoint.hpp MPT_FILES_SRC_MPT += src/mpt/format/default_formatter.hpp MPT_FILES_SRC_MPT += src/mpt/format/default_integer.hpp MPT_FILES_SRC_MPT += src/mpt/format/default_string.hpp MPT_FILES_SRC_MPT += src/mpt/format/helpers.hpp MPT_FILES_SRC_MPT += src/mpt/format/message.hpp MPT_FILES_SRC_MPT += src/mpt/format/message_macros.hpp MPT_FILES_SRC_MPT += src/mpt/format/simple.hpp MPT_FILES_SRC_MPT += src/mpt/format/simple_floatingpoint.hpp MPT_FILES_SRC_MPT += src/mpt/format/simple_integer.hpp MPT_FILES_SRC_MPT += src/mpt/format/simple_spec.hpp MPT_FILES_SRC_MPT += src/mpt/io/base.hpp MPT_FILES_SRC_MPT += src/mpt/io/io.hpp MPT_FILES_SRC_MPT += src/mpt/io/io_span.hpp MPT_FILES_SRC_MPT += src/mpt/io/io_stdstream.hpp MPT_FILES_SRC_MPT += src/mpt/io/io_virtual_wrapper.hpp MPT_FILES_SRC_MPT += src/mpt/io_read/callbackstream.hpp MPT_FILES_SRC_MPT += src/mpt/io_read/filecursor.hpp MPT_FILES_SRC_MPT += src/mpt/io_read/filecursor_callbackstream.hpp MPT_FILES_SRC_MPT += src/mpt/io_read/filecursor_filename_traits.hpp MPT_FILES_SRC_MPT += src/mpt/io_read/filecursor_memory.hpp MPT_FILES_SRC_MPT += src/mpt/io_read/filecursor_stdstream.hpp MPT_FILES_SRC_MPT += src/mpt/io_read/filecursor_traits_filedata.hpp MPT_FILES_SRC_MPT += src/mpt/io_read/filecursor_traits_memory.hpp MPT_FILES_SRC_MPT += src/mpt/io_read/filedata.hpp MPT_FILES_SRC_MPT += src/mpt/io_read/filedata_base.hpp MPT_FILES_SRC_MPT += src/mpt/io_read/filedata_base_buffered.hpp MPT_FILES_SRC_MPT += src/mpt/io_read/filedata_base_seekable.hpp MPT_FILES_SRC_MPT += src/mpt/io_read/filedata_base_unseekable.hpp MPT_FILES_SRC_MPT += src/mpt/io_read/filedata_callbackstream.hpp MPT_FILES_SRC_MPT += src/mpt/io_read/filedata_memory.hpp MPT_FILES_SRC_MPT += src/mpt/io_read/filedata_stdstream.hpp MPT_FILES_SRC_MPT += src/mpt/io_read/filereader.hpp MPT_FILES_SRC_MPT += src/mpt/io_write/buffer.hpp #MPT_FILES_SRC_MPT += src/mpt/json/json.hpp #MPT_FILES_SRC_MPT += src/mpt/library/library.hpp MPT_FILES_SRC_MPT += src/mpt/mutex/mutex.hpp MPT_FILES_SRC_MPT += src/mpt/osinfo/class.hpp MPT_FILES_SRC_MPT += src/mpt/osinfo/windows_version.hpp MPT_FILES_SRC_MPT += src/mpt/out_of_memory/out_of_memory.hpp MPT_FILES_SRC_MPT += src/mpt/parse/parse.hpp #MPT_FILES_SRC_MPT += src/mpt/path/path.hpp MPT_FILES_SRC_MPT += src/mpt/random/crand.hpp MPT_FILES_SRC_MPT += src/mpt/random/default_engines.hpp MPT_FILES_SRC_MPT += src/mpt/random/device.hpp MPT_FILES_SRC_MPT += src/mpt/random/engine.hpp MPT_FILES_SRC_MPT += src/mpt/random/engine_lcg.hpp MPT_FILES_SRC_MPT += src/mpt/random/random.hpp MPT_FILES_SRC_MPT += src/mpt/random/seed.hpp MPT_FILES_SRC_MPT += src/mpt/string/buffer.hpp MPT_FILES_SRC_MPT += src/mpt/string/types.hpp MPT_FILES_SRC_MPT += src/mpt/string/utility.hpp MPT_FILES_SRC_MPT += src/mpt/string_transcode/transcode.hpp MPT_FILES_SRC_MPT += src/mpt/string_transcode/macros.hpp MPT_FILES_SRC_MPT += src/mpt/system_error/system_error.hpp MPT_FILES_SRC_MPT += src/mpt/test/test.hpp MPT_FILES_SRC_MPT += src/mpt/test/test_macros.hpp MPT_FILES_SRC_MPT += src/mpt/uuid/guid.hpp MPT_FILES_SRC_MPT += src/mpt/uuid/uuid.hpp #MPT_FILES_SRC_MPT += src/mpt/uuid_namespace/uuid_namespace.hpp MPT_FILES_SRC_MPT += src/mpt/base/tests/tests_base_arithmetic_shift.hpp MPT_FILES_SRC_MPT += src/mpt/base/tests/tests_base_bit.hpp MPT_FILES_SRC_MPT += src/mpt/base/tests/tests_base_math.hpp MPT_FILES_SRC_MPT += src/mpt/base/tests/tests_base_saturate_cast.hpp MPT_FILES_SRC_MPT += src/mpt/base/tests/tests_base_saturate_round.hpp MPT_FILES_SRC_MPT += src/mpt/base/tests/tests_base_wrapping_divide.hpp MPT_FILES_SRC_MPT += src/mpt/binary/tests/tests_binary.hpp MPT_FILES_SRC_MPT += src/mpt/crc/tests/tests_crc.hpp #MPT_FILES_SRC_MPT += src/mpt/crypto/tests/tests_crypto.hpp MPT_FILES_SRC_MPT += src/mpt/endian/tests/tests_endian_floatingpoint.hpp MPT_FILES_SRC_MPT += src/mpt/endian/tests/tests_endian_integer.hpp MPT_FILES_SRC_MPT += src/mpt/format/tests/tests_format_message.hpp MPT_FILES_SRC_MPT += src/mpt/format/tests/tests_format_simple.hpp MPT_FILES_SRC_MPT += src/mpt/io/tests/tests_io.hpp MPT_FILES_SRC_MPT += src/mpt/parse/tests/tests_parse.hpp MPT_FILES_SRC_MPT += src/mpt/random/tests/tests_random.hpp MPT_FILES_SRC_MPT += src/mpt/string/tests/tests_string_buffer.hpp MPT_FILES_SRC_MPT += src/mpt/string/tests/tests_string_utility.hpp MPT_FILES_SRC_MPT += src/mpt/string_transcode/tests/tests_string_transcode.hpp MPT_FILES_SRC_MPT += src/mpt/uuid/tests/tests_uuid.hpp #MPT_FILES_SRC_MPT += src/mpt/uuid_namespace/tests/tests_uuid_namespace.hpp MPT_FILES_SRC_OPENMPT = MPT_FILES_SRC_OPENMPT += src/openmpt/all/BuildSettings.hpp MPT_FILES_SRC_OPENMPT += src/openmpt/base/Endian.hpp MPT_FILES_SRC_OPENMPT += src/openmpt/base/FlagSet.hpp MPT_FILES_SRC_OPENMPT += src/openmpt/base/Int24.hpp MPT_FILES_SRC_OPENMPT += src/openmpt/base/Types.hpp MPT_FILES_SRC_OPENMPT += src/openmpt/logging/Logger.hpp MPT_FILES_SRC_OPENMPT += src/openmpt/random/ModPlug.hpp MPT_FILES_SRC_OPENMPT += src/openmpt/soundbase/Copy.hpp MPT_FILES_SRC_OPENMPT += src/openmpt/soundbase/CopyMix.hpp MPT_FILES_SRC_OPENMPT += src/openmpt/soundbase/Dither.hpp MPT_FILES_SRC_OPENMPT += src/openmpt/soundbase/DitherModPlug.hpp MPT_FILES_SRC_OPENMPT += src/openmpt/soundbase/DitherNone.hpp MPT_FILES_SRC_OPENMPT += src/openmpt/soundbase/DitherSimple.hpp MPT_FILES_SRC_OPENMPT += src/openmpt/soundbase/MixSample.hpp MPT_FILES_SRC_OPENMPT += src/openmpt/soundbase/MixSampleConvert.hpp MPT_FILES_SRC_OPENMPT += src/openmpt/soundbase/SampleClip.hpp MPT_FILES_SRC_OPENMPT += src/openmpt/soundbase/SampleClipFixedPoint.hpp MPT_FILES_SRC_OPENMPT += src/openmpt/soundbase/SampleConvert.hpp MPT_FILES_SRC_OPENMPT += src/openmpt/soundbase/SampleConvertFixedPoint.hpp MPT_FILES_SRC_OPENMPT += src/openmpt/soundbase/SampleDecode.hpp MPT_FILES_SRC_OPENMPT += src/openmpt/soundbase/SampleEncode.hpp MPT_FILES_SRC_OPENMPT += src/openmpt/soundbase/SampleFormat.hpp MPT_FILES_COMMON = MPT_FILES_COMMON += common/BuildSettings.h MPT_FILES_COMMON += common/ComponentManager.cpp MPT_FILES_COMMON += common/ComponentManager.h MPT_FILES_COMMON += common/Dither.h MPT_FILES_COMMON += common/FileReader.h MPT_FILES_COMMON += common/FileReaderFwd.h MPT_FILES_COMMON += common/Logging.cpp MPT_FILES_COMMON += common/Logging.h MPT_FILES_COMMON += common/misc_util.h MPT_FILES_COMMON += common/mptAssert.h MPT_FILES_COMMON += common/mptBaseMacros.h MPT_FILES_COMMON += common/mptBaseTypes.h MPT_FILES_COMMON += common/mptBaseUtils.h MPT_FILES_COMMON += common/mptFileIO.cpp MPT_FILES_COMMON += common/mptFileIO.h MPT_FILES_COMMON += common/mptPathString.cpp MPT_FILES_COMMON += common/mptPathString.h MPT_FILES_COMMON += common/mptRandom.cpp MPT_FILES_COMMON += common/mptRandom.h MPT_FILES_COMMON += common/mptString.cpp MPT_FILES_COMMON += common/mptString.h MPT_FILES_COMMON += common/mptStringBuffer.cpp MPT_FILES_COMMON += common/mptStringBuffer.h MPT_FILES_COMMON += common/mptStringFormat.cpp MPT_FILES_COMMON += common/mptStringFormat.h MPT_FILES_COMMON += common/mptStringParse.cpp MPT_FILES_COMMON += common/mptStringParse.h MPT_FILES_COMMON += common/mptTime.cpp MPT_FILES_COMMON += common/mptTime.h MPT_FILES_COMMON += common/Profiler.cpp MPT_FILES_COMMON += common/Profiler.h MPT_FILES_COMMON += common/serialization_utils.cpp MPT_FILES_COMMON += common/serialization_utils.h MPT_FILES_COMMON += common/stdafx.h MPT_FILES_COMMON += common/version.cpp MPT_FILES_COMMON += common/version.h MPT_FILES_COMMON += common/versionNumber.h MPT_FILES_SOUNDLIB = MPT_FILES_SOUNDLIB += soundlib/AudioCriticalSection.cpp MPT_FILES_SOUNDLIB += soundlib/AudioCriticalSection.h MPT_FILES_SOUNDLIB += soundlib/AudioReadTarget.h MPT_FILES_SOUNDLIB += soundlib/BitReader.h MPT_FILES_SOUNDLIB += soundlib/ContainerMMCMP.cpp MPT_FILES_SOUNDLIB += soundlib/ContainerPP20.cpp MPT_FILES_SOUNDLIB += soundlib/ContainerUMX.cpp MPT_FILES_SOUNDLIB += soundlib/ContainerXPK.cpp MPT_FILES_SOUNDLIB += soundlib/Container.h MPT_FILES_SOUNDLIB += soundlib/Dlsbank.cpp MPT_FILES_SOUNDLIB += soundlib/Dlsbank.h MPT_FILES_SOUNDLIB += soundlib/Fastmix.cpp MPT_FILES_SOUNDLIB += soundlib/FloatMixer.h MPT_FILES_SOUNDLIB += soundlib/InstrumentExtensions.cpp MPT_FILES_SOUNDLIB += soundlib/IntMixer.h MPT_FILES_SOUNDLIB += soundlib/ITCompression.cpp MPT_FILES_SOUNDLIB += soundlib/ITCompression.h MPT_FILES_SOUNDLIB += soundlib/ITTools.cpp MPT_FILES_SOUNDLIB += soundlib/ITTools.h MPT_FILES_SOUNDLIB += soundlib/Load_669.cpp MPT_FILES_SOUNDLIB += soundlib/Load_amf.cpp MPT_FILES_SOUNDLIB += soundlib/Load_ams.cpp MPT_FILES_SOUNDLIB += soundlib/Load_c67.cpp MPT_FILES_SOUNDLIB += soundlib/Load_dbm.cpp MPT_FILES_SOUNDLIB += soundlib/Load_digi.cpp MPT_FILES_SOUNDLIB += soundlib/Load_dmf.cpp MPT_FILES_SOUNDLIB += soundlib/Load_dsm.cpp MPT_FILES_SOUNDLIB += soundlib/Load_dsym.cpp MPT_FILES_SOUNDLIB += soundlib/Load_dtm.cpp MPT_FILES_SOUNDLIB += soundlib/Loaders.h MPT_FILES_SOUNDLIB += soundlib/Load_far.cpp MPT_FILES_SOUNDLIB += soundlib/Load_fmt.cpp MPT_FILES_SOUNDLIB += soundlib/Load_gdm.cpp MPT_FILES_SOUNDLIB += soundlib/Load_imf.cpp MPT_FILES_SOUNDLIB += soundlib/Load_it.cpp MPT_FILES_SOUNDLIB += soundlib/Load_itp.cpp MPT_FILES_SOUNDLIB += soundlib/load_j2b.cpp MPT_FILES_SOUNDLIB += soundlib/Load_mdl.cpp MPT_FILES_SOUNDLIB += soundlib/Load_med.cpp MPT_FILES_SOUNDLIB += soundlib/Load_mid.cpp MPT_FILES_SOUNDLIB += soundlib/Load_mo3.cpp MPT_FILES_SOUNDLIB += soundlib/Load_mod.cpp MPT_FILES_SOUNDLIB += soundlib/Load_mt2.cpp MPT_FILES_SOUNDLIB += soundlib/Load_mtm.cpp MPT_FILES_SOUNDLIB += soundlib/Load_mus_km.cpp MPT_FILES_SOUNDLIB += soundlib/Load_okt.cpp MPT_FILES_SOUNDLIB += soundlib/Load_plm.cpp MPT_FILES_SOUNDLIB += soundlib/Load_psm.cpp MPT_FILES_SOUNDLIB += soundlib/Load_ptm.cpp MPT_FILES_SOUNDLIB += soundlib/Load_s3m.cpp MPT_FILES_SOUNDLIB += soundlib/Load_sfx.cpp MPT_FILES_SOUNDLIB += soundlib/Load_stm.cpp MPT_FILES_SOUNDLIB += soundlib/Load_stp.cpp MPT_FILES_SOUNDLIB += soundlib/Load_symmod.cpp MPT_FILES_SOUNDLIB += soundlib/Load_uax.cpp MPT_FILES_SOUNDLIB += soundlib/Load_ult.cpp MPT_FILES_SOUNDLIB += soundlib/Load_wav.cpp MPT_FILES_SOUNDLIB += soundlib/Load_xm.cpp MPT_FILES_SOUNDLIB += soundlib/Message.cpp MPT_FILES_SOUNDLIB += soundlib/Message.h MPT_FILES_SOUNDLIB += soundlib/MIDIEvents.cpp MPT_FILES_SOUNDLIB += soundlib/MIDIEvents.h MPT_FILES_SOUNDLIB += soundlib/MIDIMacros.cpp MPT_FILES_SOUNDLIB += soundlib/MIDIMacros.h MPT_FILES_SOUNDLIB += soundlib/Mixer.h MPT_FILES_SOUNDLIB += soundlib/MixerInterface.h MPT_FILES_SOUNDLIB += soundlib/MixerLoops.cpp MPT_FILES_SOUNDLIB += soundlib/MixerLoops.h MPT_FILES_SOUNDLIB += soundlib/MixerSettings.cpp MPT_FILES_SOUNDLIB += soundlib/MixerSettings.h MPT_FILES_SOUNDLIB += soundlib/MixFuncTable.cpp MPT_FILES_SOUNDLIB += soundlib/MixFuncTable.h MPT_FILES_SOUNDLIB += soundlib/ModChannel.cpp MPT_FILES_SOUNDLIB += soundlib/ModChannel.h MPT_FILES_SOUNDLIB += soundlib/modcommand.cpp MPT_FILES_SOUNDLIB += soundlib/modcommand.h MPT_FILES_SOUNDLIB += soundlib/ModInstrument.cpp MPT_FILES_SOUNDLIB += soundlib/ModInstrument.h MPT_FILES_SOUNDLIB += soundlib/ModSample.cpp MPT_FILES_SOUNDLIB += soundlib/ModSample.h MPT_FILES_SOUNDLIB += soundlib/ModSampleCopy.h MPT_FILES_SOUNDLIB += soundlib/ModSequence.cpp MPT_FILES_SOUNDLIB += soundlib/ModSequence.h MPT_FILES_SOUNDLIB += soundlib/modsmp_ctrl.cpp MPT_FILES_SOUNDLIB += soundlib/modsmp_ctrl.h MPT_FILES_SOUNDLIB += soundlib/mod_specifications.cpp MPT_FILES_SOUNDLIB += soundlib/mod_specifications.h MPT_FILES_SOUNDLIB += soundlib/MPEGFrame.cpp MPT_FILES_SOUNDLIB += soundlib/MPEGFrame.h MPT_FILES_SOUNDLIB += soundlib/OggStream.cpp MPT_FILES_SOUNDLIB += soundlib/OggStream.h MPT_FILES_SOUNDLIB += soundlib/opal.h MPT_FILES_SOUNDLIB += soundlib/OPL.cpp MPT_FILES_SOUNDLIB += soundlib/OPL.h MPT_FILES_SOUNDLIB += soundlib/Paula.cpp MPT_FILES_SOUNDLIB += soundlib/Paula.h MPT_FILES_SOUNDLIB += soundlib/patternContainer.cpp MPT_FILES_SOUNDLIB += soundlib/patternContainer.h MPT_FILES_SOUNDLIB += soundlib/pattern.cpp MPT_FILES_SOUNDLIB += soundlib/pattern.h MPT_FILES_SOUNDLIB += soundlib/Resampler.h MPT_FILES_SOUNDLIB += soundlib/RowVisitor.cpp MPT_FILES_SOUNDLIB += soundlib/RowVisitor.h MPT_FILES_SOUNDLIB += soundlib/S3MTools.cpp MPT_FILES_SOUNDLIB += soundlib/S3MTools.h MPT_FILES_SOUNDLIB += soundlib/SampleCopy.h MPT_FILES_SOUNDLIB += soundlib/SampleFormats.cpp MPT_FILES_SOUNDLIB += soundlib/SampleFormatBRR.cpp MPT_FILES_SOUNDLIB += soundlib/SampleFormatFLAC.cpp MPT_FILES_SOUNDLIB += soundlib/SampleFormatMediaFoundation.cpp MPT_FILES_SOUNDLIB += soundlib/SampleFormatMP3.cpp MPT_FILES_SOUNDLIB += soundlib/SampleFormatOpus.cpp MPT_FILES_SOUNDLIB += soundlib/SampleFormatSFZ.cpp MPT_FILES_SOUNDLIB += soundlib/SampleFormatVorbis.cpp MPT_FILES_SOUNDLIB += soundlib/SampleIO.cpp MPT_FILES_SOUNDLIB += soundlib/SampleIO.h MPT_FILES_SOUNDLIB += soundlib/SampleNormalize.h MPT_FILES_SOUNDLIB += soundlib/Snd_defs.h MPT_FILES_SOUNDLIB += soundlib/Sndfile.cpp MPT_FILES_SOUNDLIB += soundlib/Sndfile.h MPT_FILES_SOUNDLIB += soundlib/Snd_flt.cpp MPT_FILES_SOUNDLIB += soundlib/Snd_fx.cpp MPT_FILES_SOUNDLIB += soundlib/Sndmix.cpp MPT_FILES_SOUNDLIB += soundlib/SoundFilePlayConfig.cpp MPT_FILES_SOUNDLIB += soundlib/SoundFilePlayConfig.h MPT_FILES_SOUNDLIB += soundlib/Tables.cpp MPT_FILES_SOUNDLIB += soundlib/Tables.h MPT_FILES_SOUNDLIB += soundlib/Tagging.cpp MPT_FILES_SOUNDLIB += soundlib/Tagging.h MPT_FILES_SOUNDLIB += soundlib/TinyFFT.cpp MPT_FILES_SOUNDLIB += soundlib/TinyFFT.h MPT_FILES_SOUNDLIB += soundlib/tuningbase.h MPT_FILES_SOUNDLIB += soundlib/tuningCollection.cpp MPT_FILES_SOUNDLIB += soundlib/tuningcollection.h MPT_FILES_SOUNDLIB += soundlib/tuning.cpp MPT_FILES_SOUNDLIB += soundlib/tuning.h MPT_FILES_SOUNDLIB += soundlib/UMXTools.cpp MPT_FILES_SOUNDLIB += soundlib/UMXTools.h MPT_FILES_SOUNDLIB += soundlib/UpgradeModule.cpp MPT_FILES_SOUNDLIB += soundlib/WAVTools.cpp MPT_FILES_SOUNDLIB += soundlib/WAVTools.h MPT_FILES_SOUNDLIB += soundlib/WindowedFIR.cpp MPT_FILES_SOUNDLIB += soundlib/WindowedFIR.h MPT_FILES_SOUNDLIB += soundlib/XMTools.cpp MPT_FILES_SOUNDLIB += soundlib/XMTools.h MPT_FILES_SOUNDLIB += soundlib/plugins/dmo/DMOPlugin.cpp MPT_FILES_SOUNDLIB += soundlib/plugins/dmo/DMOPlugin.h MPT_FILES_SOUNDLIB += soundlib/plugins/dmo/DMOUtils.cpp MPT_FILES_SOUNDLIB += soundlib/plugins/dmo/DMOUtils.h MPT_FILES_SOUNDLIB += soundlib/plugins/dmo/Chorus.cpp MPT_FILES_SOUNDLIB += soundlib/plugins/dmo/Chorus.h MPT_FILES_SOUNDLIB += soundlib/plugins/dmo/Compressor.cpp MPT_FILES_SOUNDLIB += soundlib/plugins/dmo/Compressor.h MPT_FILES_SOUNDLIB += soundlib/plugins/dmo/Distortion.cpp MPT_FILES_SOUNDLIB += soundlib/plugins/dmo/Distortion.h MPT_FILES_SOUNDLIB += soundlib/plugins/dmo/Echo.cpp MPT_FILES_SOUNDLIB += soundlib/plugins/dmo/Echo.h MPT_FILES_SOUNDLIB += soundlib/plugins/dmo/Flanger.cpp MPT_FILES_SOUNDLIB += soundlib/plugins/dmo/Flanger.h MPT_FILES_SOUNDLIB += soundlib/plugins/dmo/Gargle.cpp MPT_FILES_SOUNDLIB += soundlib/plugins/dmo/Gargle.h MPT_FILES_SOUNDLIB += soundlib/plugins/dmo/I3DL2Reverb.cpp MPT_FILES_SOUNDLIB += soundlib/plugins/dmo/I3DL2Reverb.h MPT_FILES_SOUNDLIB += soundlib/plugins/dmo/ParamEq.cpp MPT_FILES_SOUNDLIB += soundlib/plugins/dmo/ParamEq.h MPT_FILES_SOUNDLIB += soundlib/plugins/dmo/WavesReverb.cpp MPT_FILES_SOUNDLIB += soundlib/plugins/dmo/WavesReverb.h MPT_FILES_SOUNDLIB += soundlib/plugins/DigiBoosterEcho.cpp MPT_FILES_SOUNDLIB += soundlib/plugins/DigiBoosterEcho.h MPT_FILES_SOUNDLIB += soundlib/plugins/LFOPlugin.cpp MPT_FILES_SOUNDLIB += soundlib/plugins/LFOPlugin.h MPT_FILES_SOUNDLIB += soundlib/plugins/PluginManager.cpp MPT_FILES_SOUNDLIB += soundlib/plugins/PluginManager.h MPT_FILES_SOUNDLIB += soundlib/plugins/PluginMixBuffer.h MPT_FILES_SOUNDLIB += soundlib/plugins/PluginStructs.h MPT_FILES_SOUNDLIB += soundlib/plugins/PlugInterface.cpp MPT_FILES_SOUNDLIB += soundlib/plugins/PlugInterface.h MPT_FILES_SOUNDLIB += soundlib/plugins/SymMODEcho.cpp MPT_FILES_SOUNDLIB += soundlib/plugins/SymMODEcho.h MPT_FILES_SOUNDDSP = MPT_FILES_SOUNDDSP += sounddsp/AGC.cpp MPT_FILES_SOUNDDSP += sounddsp/AGC.h MPT_FILES_SOUNDDSP += sounddsp/DSP.cpp MPT_FILES_SOUNDDSP += sounddsp/DSP.h MPT_FILES_SOUNDDSP += sounddsp/EQ.cpp MPT_FILES_SOUNDDSP += sounddsp/EQ.h MPT_FILES_SOUNDDSP += sounddsp/Reverb.cpp MPT_FILES_SOUNDDSP += sounddsp/Reverb.h pkgconfig_DATA += libopenmpt/libopenmpt.pc lib_LTLIBRARIES += libopenmpt.la libopenmpt_la_LDFLAGS = -version-info $(LIBOPENMPT_LTVER_CURRENT):$(LIBOPENMPT_LTVER_REVISION):$(LIBOPENMPT_LTVER_AGE) -no-undefined nobase_include_HEADERS += libopenmpt/libopenmpt.h nobase_include_HEADERS += libopenmpt/libopenmpt.hpp nobase_include_HEADERS += libopenmpt/libopenmpt_version.h nobase_include_HEADERS += libopenmpt/libopenmpt_config.h nobase_include_HEADERS += libopenmpt/libopenmpt_stream_callbacks_buffer.h nobase_include_HEADERS += libopenmpt/libopenmpt_stream_callbacks_fd.h nobase_include_HEADERS += libopenmpt/libopenmpt_stream_callbacks_file.h nobase_include_HEADERS += libopenmpt/libopenmpt_ext.h nobase_include_HEADERS += libopenmpt/libopenmpt_ext.hpp libopenmpt_la_CPPFLAGS = $(MINGWSTDTHREADS_CPPFLAGS) -DLIBOPENMPT_BUILD -I$(srcdir)/build/svn_version -I$(srcdir)/ -I$(srcdir)/src -I$(srcdir)/common $(ZLIB_CFLAGS) $(MPG123_CFLAGS) $(OGG_CFLAGS) $(VORBIS_CFLAGS) $(VORBISFILE_CFLAGS) libopenmpt_la_CXXFLAGS = $(ZLIB_CFLAGS) $(MPG123_CFLAGS) $(OGG_CFLAGS) $(VORBIS_CFLAGS) $(VORBISFILE_CFLAGS) libopenmpt_la_CFLAGS = $(ZLIB_CFLAGS) $(MPG123_CFLAGS) $(OGG_CFLAGS) $(VORBIS_CFLAGS) $(VORBISFILE_CFLAGS) libopenmpt_la_LIBADD = $(ZLIB_LIBS) $(MPG123_LIBS) $(OGG_LIBS) $(VORBIS_LIBS) $(VORBISFILE_LIBS) $(LIBOPENMPT_WIN32_LIBS) libopenmpt_la_SOURCES = libopenmpt_la_SOURCES += build/svn_version/svn_version.h libopenmpt_la_SOURCES += $(MPT_FILES_SRC_MPT) libopenmpt_la_SOURCES += $(MPT_FILES_SRC_OPENMPT) libopenmpt_la_SOURCES += $(MPT_FILES_COMMON) libopenmpt_la_SOURCES += $(MPT_FILES_SOUNDBASE) libopenmpt_la_SOURCES += $(MPT_FILES_SOUNDLIB) libopenmpt_la_SOURCES += $(MPT_FILES_SOUNDDSP) libopenmpt_la_SOURCES += libopenmpt/libopenmpt_c.cpp libopenmpt_la_SOURCES += libopenmpt/libopenmpt_cxx.cpp libopenmpt_la_SOURCES += libopenmpt/libopenmpt_ext_impl.cpp libopenmpt_la_SOURCES += libopenmpt/libopenmpt_impl.cpp libopenmpt_la_SOURCES += libopenmpt/libopenmpt_config.h libopenmpt_la_SOURCES += libopenmpt/libopenmpt_ext.h libopenmpt_la_SOURCES += libopenmpt/libopenmpt_ext.hpp libopenmpt_la_SOURCES += libopenmpt/libopenmpt_ext_impl.hpp libopenmpt_la_SOURCES += libopenmpt/libopenmpt.h libopenmpt_la_SOURCES += libopenmpt/libopenmpt.hpp libopenmpt_la_SOURCES += libopenmpt/libopenmpt_impl.hpp libopenmpt_la_SOURCES += libopenmpt/libopenmpt_internal.h libopenmpt_la_SOURCES += libopenmpt/libopenmpt_stream_callbacks_buffer.h libopenmpt_la_SOURCES += libopenmpt/libopenmpt_stream_callbacks_fd.h libopenmpt_la_SOURCES += libopenmpt/libopenmpt_stream_callbacks_file.h libopenmpt_la_SOURCES += libopenmpt/libopenmpt_version.h if ENABLE_TESTS check_PROGRAMS += libopenmpttest libopenmpttest_CPPFLAGS = $(MINGWSTDTHREADS_CPPFLAGS) -DLIBOPENMPT_BUILD -DLIBOPENMPT_BUILD_TEST -I$(srcdir)/build/svn_version -I$(srcdir)/ -I$(srcdir)/src -I$(srcdir)/common $(ZLIB_CFLAGS) $(MPG123_CFLAGS) $(OGG_CFLAGS) $(VORBIS_CFLAGS) $(VORBISFILE_CFLAGS) libopenmpttest_CXXFLAGS = $(ZLIB_CFLAGS) $(MPG123_CFLAGS) $(OGG_CFLAGS) $(VORBIS_CFLAGS) $(VORBISFILE_CFLAGS) $(WIN32_CONSOLE_CXXFLAGS) libopenmpttest_CFLAGS = $(ZLIB_CFLAGS) $(MPG123_CFLAGS) $(OGG_CFLAGS) $(VORBIS_CFLAGS) $(VORBISFILE_CFLAGS) $(WIN32_CONSOLE_CFLAGS) libopenmpttest_LDADD = $(ZLIB_LIBS) $(MPG123_LIBS) $(OGG_LIBS) $(VORBIS_LIBS) $(VORBISFILE_LIBS) $(LIBOPENMPT_WIN32_LIB) libopenmpttest_SOURCES = libopenmpttest_SOURCES += libopenmpt/libopenmpt_test.cpp libopenmpttest_SOURCES += test/mpt_tests_base.cpp libopenmpttest_SOURCES += test/mpt_tests_binary.cpp libopenmpttest_SOURCES += test/mpt_tests_crc.cpp #libopenmpttest_SOURCES += test/mpt_tests_crypto.cpp libopenmpttest_SOURCES += test/mpt_tests_endian.cpp libopenmpttest_SOURCES += test/mpt_tests_format.cpp libopenmpttest_SOURCES += test/mpt_tests_io.cpp libopenmpttest_SOURCES += test/mpt_tests_parse.cpp libopenmpttest_SOURCES += test/mpt_tests_random.cpp libopenmpttest_SOURCES += test/mpt_tests_string.cpp libopenmpttest_SOURCES += test/mpt_tests_string_transcode.cpp libopenmpttest_SOURCES += test/mpt_tests_uuid.cpp #libopenmpttest_SOURCES += test/mpt_tests_uuid_namespace.cpp libopenmpttest_SOURCES += test/test.cpp libopenmpttest_SOURCES += test/test.h libopenmpttest_SOURCES += test/TestTools.h libopenmpttest_SOURCES += test/TestToolsLib.cpp libopenmpttest_SOURCES += test/TestToolsLib.h libopenmpttest_SOURCES += test/TestToolsTracker.h libopenmpttest_SOURCES += build/svn_version/svn_version.h libopenmpttest_SOURCES += $(MPT_FILES_SRC_MPT) libopenmpttest_SOURCES += $(MPT_FILES_SRC_OPENMPT) libopenmpttest_SOURCES += $(MPT_FILES_COMMON) libopenmpttest_SOURCES += $(MPT_FILES_SOUNDBASE) libopenmpttest_SOURCES += $(MPT_FILES_SOUNDLIB) libopenmpttest_SOURCES += $(MPT_FILES_SOUNDDSP) libopenmpttest_SOURCES += libopenmpt/libopenmpt_c.cpp libopenmpttest_SOURCES += libopenmpt/libopenmpt_cxx.cpp libopenmpttest_SOURCES += libopenmpt/libopenmpt_ext_impl.cpp libopenmpttest_SOURCES += libopenmpt/libopenmpt_impl.cpp libopenmpttest_SOURCES += libopenmpt/libopenmpt_config.h libopenmpttest_SOURCES += libopenmpt/libopenmpt_ext.h libopenmpttest_SOURCES += libopenmpt/libopenmpt_ext.hpp libopenmpttest_SOURCES += libopenmpt/libopenmpt_ext_impl.hpp libopenmpttest_SOURCES += libopenmpt/libopenmpt.h libopenmpttest_SOURCES += libopenmpt/libopenmpt.hpp libopenmpttest_SOURCES += libopenmpt/libopenmpt_impl.hpp libopenmpttest_SOURCES += libopenmpt/libopenmpt_internal.h libopenmpttest_SOURCES += libopenmpt/libopenmpt_stream_callbacks_buffer.h libopenmpttest_SOURCES += libopenmpt/libopenmpt_stream_callbacks_fd.h libopenmpttest_SOURCES += libopenmpt/libopenmpt_stream_callbacks_file.h libopenmpttest_SOURCES += libopenmpt/libopenmpt_version.h endif if ENABLE_OPENMPT123 bin_PROGRAMS += bin/openmpt123 bin_openmpt123_CPPFLAGS = $(MINGWSTDTHREADS_CPPFLAGS) -I$(srcdir)/src $(PORTAUDIO_CFLAGS) $(PULSEAUDIO_CFLAGS) $(SDL2_CFLAGS) $(SNDFILE_CFLAGS) $(FLAC_CFLAGS) bin_openmpt123_CXXFLAGS = $(WIN32_CONSOLE_CXXFLAGS) bin_openmpt123_LDADD = libopenmpt.la $(PORTAUDIO_LIBS) $(PULSEAUDIO_LIBS) $(SDL2_LIBS) $(SNDFILE_LIBS) $(FLAC_LIBS) $(OPENMPT123_WIN32_LIBS) bin_openmpt123_SOURCES = bin_openmpt123_SOURCES += $(MPT_FILES_SRC_MPT) bin_openmpt123_SOURCES += openmpt123/openmpt123_allegro42.hpp bin_openmpt123_SOURCES += openmpt123/openmpt123_config.hpp bin_openmpt123_SOURCES += openmpt123/openmpt123.cpp bin_openmpt123_SOURCES += openmpt123/openmpt123_flac.hpp bin_openmpt123_SOURCES += openmpt123/openmpt123.hpp bin_openmpt123_SOURCES += openmpt123/openmpt123_mmio.hpp bin_openmpt123_SOURCES += openmpt123/openmpt123_portaudio.hpp bin_openmpt123_SOURCES += openmpt123/openmpt123_pulseaudio.hpp bin_openmpt123_SOURCES += openmpt123/openmpt123_raw.hpp bin_openmpt123_SOURCES += openmpt123/openmpt123_sdl2.hpp bin_openmpt123_SOURCES += openmpt123/openmpt123_sndfile.hpp bin_openmpt123_SOURCES += openmpt123/openmpt123_stdout.hpp bin_openmpt123_SOURCES += openmpt123/openmpt123_waveout.hpp man1_MANS = man/openmpt123.1 endif @DX_RULES@ MOSTLYCLEANFILES += $(DX_CLEANFILES) libopenmpt-0.6.1+release.autotools/configure0000755000175000017500000262336014175541543016210 00000000000000#! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.69 for libopenmpt 0.6.1+release.autotools. # # Report bugs to . # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. # # # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # Use a proper internal environment variable to ensure we don't fall # into an infinite loop, continuously re-executing ourselves. if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then _as_can_reexec=no; export _as_can_reexec; # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 as_fn_exit 255 fi # We don't want this to propagate to other subprocesses. { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi " as_required="as_fn_return () { (exit \$1); } as_fn_success () { as_fn_return 0; } as_fn_failure () { as_fn_return 1; } as_fn_ret_success () { return 0; } as_fn_ret_failure () { return 1; } exitcode=0 as_fn_success || { exitcode=1; echo as_fn_success failed.; } as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : else exitcode=1; echo positional parameters were not saved. fi test x\$exitcode = x0 || exit 1 test -x / || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 test -n \"\${ZSH_VERSION+set}\${BASH_VERSION+set}\" || ( ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO PATH=/empty FPATH=/empty; export PATH FPATH test \"X\`printf %s \$ECHO\`\" = \"X\$ECHO\" \\ || test \"X\`print -r -- \$ECHO\`\" = \"X\$ECHO\" ) || exit 1 test \$(( 1 + 1 )) = 2 || exit 1" if (eval "$as_required") 2>/dev/null; then : as_have_required=yes else as_have_required=no fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. as_found=: case $as_dir in #( /*) for as_base in sh bash ksh sh5; do # Try only shells that exist, to save several forks. as_shell=$as_dir/$as_base if { test -f "$as_shell" || test -f "$as_shell.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : CONFIG_SHELL=$as_shell as_have_required=yes if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : break 2 fi fi done;; esac as_found=false done $as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : CONFIG_SHELL=$SHELL as_have_required=yes fi; } IFS=$as_save_IFS if test "x$CONFIG_SHELL" != x; then : export CONFIG_SHELL # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi if test x$as_have_required = xno; then : $as_echo "$0: This script requires a shell more modern than all" $as_echo "$0: the shells that I found on your system." if test x${ZSH_VERSION+set} = xset ; then $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" $as_echo "$0: be upgraded to zsh 4.3.4 or later." else $as_echo "$0: Please tell bug-autoconf@gnu.org and $0: https://bugs.openmpt.org/ about your system, including $0: any error possibly output before this message. Then $0: install a modern shell, or manually run the script $0: under such a shell if you do have one." fi exit 1 fi fi fi SHELL=${CONFIG_SHELL-/bin/sh} export SHELL # Unset more variables known to interfere with behavior of common tools. CLICOLOR_FORCE= GREP_OPTIONS= unset CLICOLOR_FORCE GREP_OPTIONS ## --------------------- ## ## M4sh Shell Functions. ## ## --------------------- ## # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits as_lineno_1=$LINENO as_lineno_1a=$LINENO as_lineno_2=$LINENO as_lineno_2a=$LINENO eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } # If we had to re-execute with $CONFIG_SHELL, we're ensured to have # already done that, so ensure we don't try to do so again and fall # in an infinite loop. This has already happened in practice. _as_can_reexec=no; export _as_can_reexec # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" SHELL=${CONFIG_SHELL-/bin/sh} test -n "$DJDIR" || exec 7<&0 &1 # Name of the host. # hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` # # Initializations. # ac_default_prefix=/usr/local ac_clean_files= ac_config_libobj_dir=. LIBOBJS= cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= # Identity of this package. PACKAGE_NAME='libopenmpt' PACKAGE_TARNAME='libopenmpt' PACKAGE_VERSION='0.6.1+release.autotools' PACKAGE_STRING='libopenmpt 0.6.1+release.autotools' PACKAGE_BUGREPORT='https://bugs.openmpt.org/' PACKAGE_URL='https://lib.openmpt.org/' # Factoring default headers for most tests. ac_includes_default="\ #include #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef STDC_HEADERS # include # include #else # ifdef HAVE_STDLIB_H # include # endif #endif #ifdef HAVE_STRING_H # if !defined STDC_HEADERS && defined HAVE_MEMORY_H # include # endif # include #endif #ifdef HAVE_STRINGS_H # include #endif #ifdef HAVE_INTTYPES_H # include #endif #ifdef HAVE_STDINT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif" ac_subst_vars='am__EXEEXT_FALSE am__EXEEXT_TRUE LTLIBOBJS LIBOBJS DX_RULES PAPER_SIZE DOXYGEN_PAPER_SIZE GENERATE_LATEX DX_PDFLATEX DX_FLAG_pdf DX_EGREP DX_DVIPS DX_MAKEINDEX DX_LATEX DX_FLAG_ps DX_FLAG_html GENERATE_CHI DX_FLAG_chi GENERATE_HTMLHELP GENERATE_HTML HHC_PATH DX_HHC DX_FLAG_chm GENERATE_XML DX_FLAG_xml GENERATE_RTF DX_FLAG_rtf GENERATE_MAN DX_FLAG_man DOT_PATH HAVE_DOT DX_DOT DX_FLAG_dot PERL_PATH DX_PERL DX_DOXYGEN DX_FLAG_doc PROJECT SRCDIR DX_ENV DX_DOCDIR DX_CONFIG DX_PROJECT MINGWSTDTHREADS_CPPFLAGS HAVE_CXX17 FLAC_LIBS FLAC_CFLAGS SNDFILE_LIBS SNDFILE_CFLAGS SDL2_LIBS SDL2_CFLAGS HAVE_PORTAUDIOCPP_FALSE HAVE_PORTAUDIOCPP_TRUE PORTAUDIOCPP_LIBS PORTAUDIOCPP_CFLAGS HAVE_PORTAUDIO_FALSE HAVE_PORTAUDIO_TRUE PORTAUDIO_LIBS PORTAUDIO_CFLAGS PULSEAUDIO_LIBS PULSEAUDIO_CFLAGS ENABLE_TESTS_FALSE ENABLE_TESTS_TRUE ENABLE_EXAMPLES_FALSE ENABLE_EXAMPLES_TRUE ENABLE_OPENMPT123_FALSE ENABLE_OPENMPT123_TRUE LIBOPENMPT_LIBS_PRIVATE LIBOPENMPT_REQUIRES_PRIVATE VORBISFILE_LIBS VORBISFILE_CFLAGS VORBIS_LIBS VORBIS_CFLAGS OGG_LIBS OGG_CFLAGS MPG123_LIBS MPG123_CFLAGS ZLIB_LIBS ZLIB_CFLAGS WIN32_CONSOLE_CFLAGS WIN32_CONSOLE_CXXFLAGS OPENMPT123_WIN32_LIBS LIBOPENMPT_WIN32_LIBS CXXSTDLIB_PCLIBSPRIVATE LIBOPENMPT_LTVER_AGE LIBOPENMPT_LTVER_REVISION LIBOPENMPT_LTVER_CURRENT CXXCPP am__fastdepCXX_FALSE am__fastdepCXX_TRUE CXXDEPMODE ac_ct_CXX CXXFLAGS CXX PKG_CONFIG_LIBDIR PKG_CONFIG_PATH PKG_CONFIG CPP LT_SYS_LIBRARY_PATH OTOOL64 OTOOL LIPO NMEDIT DSYMUTIL MANIFEST_TOOL RANLIB DLLTOOL OBJDUMP LN_S NM ac_ct_DUMPBIN DUMPBIN LD FGREP EGREP GREP SED host_os host_vendor host_cpu host build_os build_vendor build_cpu build LIBTOOL am__fastdepCC_FALSE am__fastdepCC_TRUE CCDEPMODE am__nodep AMDEPBACKSLASH AMDEP_FALSE AMDEP_TRUE am__include DEPDIR OBJEXT EXEEXT ac_ct_CC CPPFLAGS LDFLAGS CFLAGS CC ac_ct_AR AR AM_BACKSLASH AM_DEFAULT_VERBOSITY AM_DEFAULT_V AM_V am__untar am__tar AMTAR am__leading_dot SET_MAKE AWK mkdir_p MKDIR_P INSTALL_STRIP_PROGRAM STRIP install_sh MAKEINFO AUTOHEADER AUTOMAKE AUTOCONF ACLOCAL VERSION PACKAGE CYGPATH_W am__isrc INSTALL_DATA INSTALL_SCRIPT INSTALL_PROGRAM target_alias host_alias build_alias LIBS ECHO_T ECHO_N ECHO_C DEFS mandir localedir libdir psdir pdfdir dvidir htmldir infodir docdir oldincludedir includedir runstatedir localstatedir sharedstatedir sysconfdir datadir datarootdir libexecdir sbindir bindir program_transform_name prefix exec_prefix PACKAGE_URL PACKAGE_BUGREPORT PACKAGE_STRING PACKAGE_VERSION PACKAGE_TARNAME PACKAGE_NAME PATH_SEPARATOR SHELL am__quote' ac_subst_files='' ac_user_opts=' enable_option_checking enable_silent_rules enable_dependency_tracking enable_shared enable_static with_pic enable_fast_install with_aix_soname with_gnu_ld with_sysroot enable_libtool_lock enable_largefile with_zlib with_mpg123 with_ogg with_vorbis with_vorbisfile enable_openmpt123 enable_examples enable_tests with_pulseaudio with_portaudio with_portaudiocpp with_sdl2 with_sndfile with_flac enable_doxygen_doc enable_doxygen_dot enable_doxygen_man enable_doxygen_rtf enable_doxygen_xml enable_doxygen_chm enable_doxygen_chi enable_doxygen_html enable_doxygen_ps enable_doxygen_pdf ' ac_precious_vars='build_alias host_alias target_alias CC CFLAGS LDFLAGS LIBS CPPFLAGS LT_SYS_LIBRARY_PATH CPP PKG_CONFIG PKG_CONFIG_PATH PKG_CONFIG_LIBDIR CXX CXXFLAGS CCC CXXCPP CXXSTDLIB_PCLIBSPRIVATE ZLIB_CFLAGS ZLIB_LIBS MPG123_CFLAGS MPG123_LIBS OGG_CFLAGS OGG_LIBS VORBIS_CFLAGS VORBIS_LIBS VORBISFILE_CFLAGS VORBISFILE_LIBS PULSEAUDIO_CFLAGS PULSEAUDIO_LIBS PORTAUDIO_CFLAGS PORTAUDIO_LIBS PORTAUDIOCPP_CFLAGS PORTAUDIOCPP_LIBS SDL2_CFLAGS SDL2_LIBS SNDFILE_CFLAGS SNDFILE_LIBS FLAC_CFLAGS FLAC_LIBS DOXYGEN_PAPER_SIZE' # Initialize some variables set by options. ac_init_help= ac_init_version=false ac_unrecognized_opts= ac_unrecognized_sep= # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. # (The list follows the same order as the GNU Coding Standards.) bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datarootdir='${prefix}/share' datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' runstatedir='${localstatedir}/run' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' infodir='${datarootdir}/info' htmldir='${docdir}' dvidir='${docdir}' pdfdir='${docdir}' psdir='${docdir}' libdir='${exec_prefix}/lib' localedir='${datarootdir}/locale' mandir='${datarootdir}/man' ac_prev= ac_dashdash= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval $ac_prev=\$ac_option ac_prev= continue fi case $ac_option in *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; *=) ac_optarg= ;; *) ac_optarg=yes ;; esac # Accept the important Cygnus configure options, so we can diagnose typos. case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=*) datadir=$ac_optarg ;; -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ | --dataroo | --dataro | --datar) ac_prev=datarootdir ;; -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) datarootdir=$ac_optarg ;; -disable-* | --disable-*) ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=no ;; -docdir | --docdir | --docdi | --doc | --do) ac_prev=docdir ;; -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) docdir=$ac_optarg ;; -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) ac_prev=dvidir ;; -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) dvidir=$ac_optarg ;; -enable-* | --enable-*) ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=\$ac_optarg ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) ac_prev=htmldir ;; -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ | --ht=*) htmldir=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localedir | --localedir | --localedi | --localed | --locale) ac_prev=localedir ;; -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) localedir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst | --locals) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) ac_prev=pdfdir ;; -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) pdfdir=$ac_optarg ;; -psdir | --psdir | --psdi | --psd | --ps) ac_prev=psdir ;; -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) psdir=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -runstatedir | --runstatedir | --runstatedi | --runstated \ | --runstate | --runstat | --runsta | --runst | --runs \ | --run | --ru | --r) ac_prev=runstatedir ;; -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ | --run=* | --ru=* | --r=*) runstatedir=$ac_optarg ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=\$ac_optarg ;; -without-* | --without-*) ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=no ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) as_fn_error $? "unrecognized option: \`$ac_option' Try \`$0 --help' for more information" ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. case $ac_envvar in #( '' | [0-9]* | *[!_$as_cr_alnum]* ) as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` as_fn_error $? "missing argument to $ac_option" fi if test -n "$ac_unrecognized_opts"; then case $enable_option_checking in no) ;; fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi # Check all directory arguments for consistency. for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ libdir localedir mandir runstatedir do eval ac_val=\$$ac_var # Remove trailing slashes. case $ac_val in */ ) ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` eval $ac_var=\$ac_val;; esac # Be sure to have absolute directory names. case $ac_val in [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" done # There might be people who depend on the old broken behavior: `$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || as_fn_error $? "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || as_fn_error $? "pwd does not report name of working directory" # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then the parent directory. ac_confdir=`$as_dirname -- "$as_myself" || $as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_myself" : 'X\(//\)[^/]' \| \ X"$as_myself" : 'X\(//\)$' \| \ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_myself" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` srcdir=$ac_confdir if test ! -r "$srcdir/$ac_unique_file"; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then srcdir=. fi # Remove unnecessary trailing slashes from srcdir. # Double slashes in file names in object file debugging info # mess up M-x gdb in Emacs. case $srcdir in */) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; esac for ac_var in $ac_precious_vars; do eval ac_env_${ac_var}_set=\${${ac_var}+set} eval ac_env_${ac_var}_value=\$${ac_var} eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} eval ac_cv_env_${ac_var}_value=\$${ac_var} done # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures libopenmpt 0.6.1+release.autotools to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print \`checking ...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or \`..'] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify an installation prefix other than \`$ac_default_prefix' using \`--prefix', for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] --datadir=DIR read-only architecture-independent data [DATAROOTDIR] --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] --docdir=DIR documentation root [DATAROOTDIR/doc/libopenmpt] --htmldir=DIR html documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] --psdir=DIR ps documentation [DOCDIR] _ACEOF cat <<\_ACEOF Program names: --program-prefix=PREFIX prepend PREFIX to installed program names --program-suffix=SUFFIX append SUFFIX to installed program names --program-transform-name=PROGRAM run sed PROGRAM on installed program names System types: --build=BUILD configure for building on BUILD [guessed] --host=HOST cross-compile to build programs to run on HOST [BUILD] _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in short | recursive ) echo "Configuration of libopenmpt 0.6.1+release.autotools:";; esac cat <<\_ACEOF Optional Features: --disable-option-checking ignore unrecognized --enable/--with options --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --enable-silent-rules less verbose build output (undo: "make V=1") --disable-silent-rules verbose build output (undo: "make V=0") --enable-dependency-tracking do not reject slow dependency extractors --disable-dependency-tracking speeds up one-time build --enable-shared[=PKGS] build shared libraries [default=yes] --enable-static[=PKGS] build static libraries [default=yes] --enable-fast-install[=PKGS] optimize for fast installation [default=yes] --disable-libtool-lock avoid locking (might break parallel builds) --disable-largefile omit support for large files --disable-openmpt123 Disable the openmpt123 command line player. --disable-examples Disable the example programs. --disable-tests Disable the test suite. --disable-doxygen-doc don't generate any doxygen documentation --enable-doxygen-dot generate graphics for doxygen documentation --enable-doxygen-man generate doxygen manual pages --enable-doxygen-rtf generate doxygen RTF documentation --enable-doxygen-xml generate doxygen XML documentation --enable-doxygen-chm generate doxygen compressed HTML help documentation --enable-doxygen-chi generate doxygen separate compressed HTML help index file --disable-doxygen-html don't generate doxygen plain HTML documentation --enable-doxygen-ps generate doxygen PostScript documentation --enable-doxygen-pdf generate doxygen PDF documentation Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --with-pic[=PKGS] try to use only PIC/non-PIC objects [default=use both] --with-aix-soname=aix|svr4|both shared library versioning (aka "SONAME") variant to provide on AIX, [default=aix]. --with-gnu-ld assume the C compiler uses GNU ld [default=no] --with-sysroot[=DIR] Search for dependent libraries within DIR (or the compiler's sysroot if not specified). --without-zlib Disable use of zlib. --without-mpg123 Disable use of libmpg123. --without-ogg Disable use of libogg. --without-vorbis Disable use of libvorbis. --without-vorbisfile Disable use of libvorbisfile. --with-pulseaudio Enable use of libpulse and libpulse-simple (enabled by default on Linux). --without-portaudio Disable use of libportaudio. --without-portaudiocpp Disable use of libportaudiocpp. --with-sdl2 Enable use of libsdl2. --without-sndfile Disable use of libsndfile. --without-flac Disable use of libflac. Some influential environment variables: CC C compiler command CFLAGS C compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory LIBS libraries to pass to the linker, e.g. -l CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory LT_SYS_LIBRARY_PATH User-defined run-time library search path. CPP C preprocessor PKG_CONFIG path to pkg-config utility PKG_CONFIG_PATH directories to add to pkg-config's search path PKG_CONFIG_LIBDIR path overriding pkg-config's built-in search path CXX C++ compiler command CXXFLAGS C++ compiler flags CXXCPP C++ preprocessor CXXSTDLIB_PCLIBSPRIVATE C++ standard library (or libraries) required for static linking. This will be put in the pkg-config file libopenmpt.pc Libs.private field and used for nothing else. ZLIB_CFLAGS C compiler flags for ZLIB, overriding pkg-config ZLIB_LIBS linker flags for ZLIB, overriding pkg-config MPG123_CFLAGS C compiler flags for MPG123, overriding pkg-config MPG123_LIBS linker flags for MPG123, overriding pkg-config OGG_CFLAGS C compiler flags for OGG, overriding pkg-config OGG_LIBS linker flags for OGG, overriding pkg-config VORBIS_CFLAGS C compiler flags for VORBIS, overriding pkg-config VORBIS_LIBS linker flags for VORBIS, overriding pkg-config VORBISFILE_CFLAGS C compiler flags for VORBISFILE, overriding pkg-config VORBISFILE_LIBS linker flags for VORBISFILE, overriding pkg-config PULSEAUDIO_CFLAGS C compiler flags for PULSEAUDIO, overriding pkg-config PULSEAUDIO_LIBS linker flags for PULSEAUDIO, overriding pkg-config PORTAUDIO_CFLAGS C compiler flags for PORTAUDIO, overriding pkg-config PORTAUDIO_LIBS linker flags for PORTAUDIO, overriding pkg-config PORTAUDIOCPP_CFLAGS C compiler flags for PORTAUDIOCPP, overriding pkg-config PORTAUDIOCPP_LIBS linker flags for PORTAUDIOCPP, overriding pkg-config SDL2_CFLAGS C compiler flags for SDL2, overriding pkg-config SDL2_LIBS linker flags for SDL2, overriding pkg-config SNDFILE_CFLAGS C compiler flags for SNDFILE, overriding pkg-config SNDFILE_LIBS linker flags for SNDFILE, overriding pkg-config FLAC_CFLAGS C compiler flags for FLAC, overriding pkg-config FLAC_LIBS linker flags for FLAC, overriding pkg-config DOXYGEN_PAPER_SIZE a4wide (default), a4, letter, legal or executive Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. Report bugs to . libopenmpt home page: . _ACEOF ac_status=$? fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d "$ac_dir" || { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || continue ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "$ac_dir" || { ac_status=$?; continue; } # Check for guested configure. if test -f "$ac_srcdir/configure.gnu"; then echo && $SHELL "$ac_srcdir/configure.gnu" --help=recursive elif test -f "$ac_srcdir/configure"; then echo && $SHELL "$ac_srcdir/configure" --help=recursive else $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF libopenmpt configure 0.6.1+release.autotools generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit fi ## ------------------------ ## ## Autoconf initialization. ## ## ------------------------ ## # ac_fn_c_try_compile LINENO # -------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_compile # ac_fn_c_try_link LINENO # ----------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_link () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest$ac_exeext if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || test -x conftest$ac_exeext }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_link # ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists and can be compiled using the include files in # INCLUDES, setting the cache variable VAR accordingly. ac_fn_c_check_header_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_compile # ac_fn_c_try_cpp LINENO # ---------------------- # Try to preprocess conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_cpp () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } > conftest.i && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_cpp # ac_fn_c_try_run LINENO # ---------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. Assumes # that executables *can* be run. ac_fn_c_try_run () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then : ac_retval=0 else $as_echo "$as_me: program exited with status $ac_status" >&5 $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=$ac_status fi rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_run # ac_fn_c_check_func LINENO FUNC VAR # ---------------------------------- # Tests whether FUNC exists, setting the cache variable VAR accordingly ac_fn_c_check_func () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Define $2 to an innocuous variant, in case declares $2. For example, HP-UX 11i declares gettimeofday. */ #define $2 innocuous_$2 /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $2 (); below. Prefer to if __STDC__ is defined, since exists even on freestanding compilers. */ #ifdef __STDC__ # include #else # include #endif #undef $2 /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char $2 (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined __stub_$2 || defined __stub___$2 choke me #endif int main () { return $2 (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_func # ac_fn_cxx_try_compile LINENO # ---------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_cxx_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_cxx_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_cxx_try_compile # ac_fn_cxx_try_cpp LINENO # ------------------------ # Try to preprocess conftest.$ac_ext, and return whether this succeeded. ac_fn_cxx_try_cpp () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } > conftest.i && { test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" || test ! -s conftest.err }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_cxx_try_cpp # ac_fn_cxx_try_link LINENO # ------------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. ac_fn_cxx_try_link () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest$ac_exeext if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_cxx_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || test -x conftest$ac_exeext }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_cxx_try_link # ac_fn_cxx_check_header_mongrel LINENO HEADER VAR INCLUDES # --------------------------------------------------------- # Tests whether HEADER exists, giving a warning if it cannot be compiled using # the include files in INCLUDES and setting the cache variable VAR # accordingly. ac_fn_cxx_check_header_mongrel () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if eval \${$3+:} false; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } else # Is the header compilable? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 $as_echo_n "checking $2 usability... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_header_compiler=yes else ac_header_compiler=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 $as_echo "$ac_header_compiler" >&6; } # Is the header present? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 $as_echo_n "checking $2 presence... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include <$2> _ACEOF if ac_fn_cxx_try_cpp "$LINENO"; then : ac_header_preproc=yes else ac_header_preproc=no fi rm -f conftest.err conftest.i conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 $as_echo "$ac_header_preproc" >&6; } # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_cxx_preproc_warn_flag in #(( yes:no: ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 $as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ;; no:yes:* ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 $as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 $as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 $as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 $as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ( $as_echo "## ---------------------------------------- ## ## Report this to https://bugs.openmpt.org/ ## ## ---------------------------------------- ##" ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else eval "$3=\$ac_header_compiler" fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_cxx_check_header_mongrel cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by libopenmpt $as_me 0.6.1+release.autotools, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ _ACEOF exec 5>>config.log { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` /usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. $as_echo "PATH: $as_dir" done IFS=$as_save_IFS } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *\'*) ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; 2) as_fn_append ac_configure_args1 " '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi as_fn_append ac_configure_args " '$ac_arg'" ;; esac done done { ac_configure_args0=; unset ac_configure_args0;} { ac_configure_args1=; unset ac_configure_args1;} # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Use '\'' to represent an apostrophe within the trap. # WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. trap 'exit_status=$? # Save into config.log some information that might help in debugging. { echo $as_echo "## ---------------- ## ## Cache variables. ## ## ---------------- ##" echo # The following way of writing the cache mishandles newlines in values, ( for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( *${as_nl}ac_space=\ *) sed -n \ "s/'\''/'\''\\\\'\'''\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" ;; #( *) sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) echo $as_echo "## ----------------- ## ## Output variables. ## ## ----------------- ##" echo for ac_var in $ac_subst_vars do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo if test -n "$ac_subst_files"; then $as_echo "## ------------------- ## ## File substitutions. ## ## ------------------- ##" echo for ac_var in $ac_subst_files do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo fi if test -s confdefs.h; then $as_echo "## ----------- ## ## confdefs.h. ## ## ----------- ##" echo cat confdefs.h echo fi test "$ac_signal" != 0 && $as_echo "$as_me: caught signal $ac_signal" $as_echo "$as_me: exit $exit_status" } >&5 rm -f core *.core core.conftest.* && rm -f -r conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -f -r conftest* confdefs.h $as_echo "/* confdefs.h */" > confdefs.h # Predefined preprocessor variables. cat >>confdefs.h <<_ACEOF #define PACKAGE_NAME "$PACKAGE_NAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_TARNAME "$PACKAGE_TARNAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_VERSION "$PACKAGE_VERSION" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_STRING "$PACKAGE_STRING" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_URL "$PACKAGE_URL" _ACEOF # Let the site file select an alternate cache file if it wants to. # Prefer an explicitly selected file to automatically selected ones. ac_site_file1=NONE ac_site_file2=NONE if test -n "$CONFIG_SITE"; then # We do not want a PATH search for config.site. case $CONFIG_SITE in #(( -*) ac_site_file1=./$CONFIG_SITE;; */*) ac_site_file1=$CONFIG_SITE;; *) ac_site_file1=./$CONFIG_SITE;; esac elif test "x$prefix" != xNONE; then ac_site_file1=$prefix/share/config.site ac_site_file2=$prefix/etc/config.site else ac_site_file1=$ac_default_prefix/share/config.site ac_site_file2=$ac_default_prefix/etc/config.site fi for ac_site_file in "$ac_site_file1" "$ac_site_file2" do test "x$ac_site_file" = xNONE && continue if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 $as_echo "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" \ || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "failed to load site script $ac_site_file See \`config.log' for more details" "$LINENO" 5; } fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special files # actually), so we avoid doing that. DJGPP emulates it as a regular file. if test /dev/null != "$cache_file" && test -f "$cache_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 $as_echo "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 $as_echo "$as_me: creating cache $cache_file" >&6;} >$cache_file fi # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in $ac_precious_vars; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val=\$ac_cv_env_${ac_var}_value eval ac_new_val=\$ac_env_${ac_var}_value case $ac_old_set,$ac_new_set in set,) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then # differences in whitespace do not lead to failure. ac_old_val_w=`echo x $ac_old_val` ac_new_val_w=`echo x $ac_new_val` if test "$ac_old_val_w" != "$ac_new_val_w"; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 $as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} ac_cache_corrupted=: else { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 $as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} eval $ac_var=\$ac_old_val fi { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 $as_echo "$as_me: former value: \`$ac_old_val'" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 $as_echo "$as_me: current value: \`$ac_new_val'" >&2;} fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) as_fn_append ac_configure_args " '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 $as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 fi ## -------------------- ## ## Main body of script. ## ## -------------------- ## ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_aux_dir= for ac_dir in build-aux "$srcdir"/build-aux; do if test -f "$ac_dir/install-sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install-sh -c" break elif test -f "$ac_dir/install.sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install.sh -c" break elif test -f "$ac_dir/shtool"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/shtool install -c" break fi done if test -z "$ac_aux_dir"; then as_fn_error $? "cannot find install-sh, install.sh, or shtool in build-aux \"$srcdir\"/build-aux" "$LINENO" 5 fi # These three variables are undocumented and unsupported, # and are intended to be withdrawn in a future Autoconf release. # They can cause serious problems if a builder's source tree is in a directory # whose full name contains unusual characters. ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. ac_config_files="$ac_config_files Makefile libopenmpt/libopenmpt.pc Doxyfile" am__api_version='1.16' # Find a good install program. We prefer a C program (faster), # so one script is as good as another. But avoid the broken or # incompatible versions: # SysV /etc/install, /usr/sbin/install # SunOS /usr/etc/install # IRIX /sbin/install # AIX /bin/install # AmigaOS /C/install, which installs bootblocks on floppy discs # AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag # AFS /usr/afsws/bin/install, which mishandles nonexistent args # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # OS/2's system install, which has a completely different semantic # ./install, which can be erroneously created by make from ./install.sh. # Reject install programs that cannot install multiple files. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 $as_echo_n "checking for a BSD-compatible install... " >&6; } if test -z "$INSTALL"; then if ${ac_cv_path_install+:} false; then : $as_echo_n "(cached) " >&6 else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. # Account for people who put trailing slashes in PATH elements. case $as_dir/ in #(( ./ | .// | /[cC]/* | \ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ /usr/ucb/* ) ;; *) # OSF1 and SCO ODT 3.0 have their own names for install. # Don't use installbsd from OSF since it installs stuff as root # by default. for ac_prog in ginstall scoinst install; do for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then if test $ac_prog = install && grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # AIX install. It has an incompatible calling convention. : elif test $ac_prog = install && grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # program-specific install script used by HP pwplus--don't use. : else rm -rf conftest.one conftest.two conftest.dir echo one > conftest.one echo two > conftest.two mkdir conftest.dir if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && test -s conftest.one && test -s conftest.two && test -s conftest.dir/conftest.one && test -s conftest.dir/conftest.two then ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" break 3 fi fi fi done done ;; esac done IFS=$as_save_IFS rm -rf conftest.one conftest.two conftest.dir fi if test "${ac_cv_path_install+set}" = set; then INSTALL=$ac_cv_path_install else # As a last resort, use the slow shell script. Don't cache a # value for INSTALL within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. INSTALL=$ac_install_sh fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 $as_echo "$INSTALL" >&6; } # Use test -z because SunOS4 sh mishandles braces in ${var-val}. # It thinks the first close brace ends the variable substitution. test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5 $as_echo_n "checking whether build environment is sane... " >&6; } # Reject unsafe characters in $srcdir or the absolute working directory # name. Accept space and tab only in the latter. am_lf=' ' case `pwd` in *[\\\"\#\$\&\'\`$am_lf]*) as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;; esac case $srcdir in *[\\\"\#\$\&\'\`$am_lf\ \ ]*) as_fn_error $? "unsafe srcdir value: '$srcdir'" "$LINENO" 5;; esac # Do 'set' in a subshell so we don't clobber the current shell's # arguments. Must try -L first in case configure is actually a # symlink; some systems play weird games with the mod time of symlinks # (eg FreeBSD returns the mod time of the symlink's containing # directory). if ( am_has_slept=no for am_try in 1 2; do echo "timestamp, slept: $am_has_slept" > conftest.file set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` if test "$*" = "X"; then # -L didn't work. set X `ls -t "$srcdir/configure" conftest.file` fi if test "$*" != "X $srcdir/configure conftest.file" \ && test "$*" != "X conftest.file $srcdir/configure"; then # If neither matched, then we have a broken ls. This can happen # if, for instance, CONFIG_SHELL is bash and it inherits a # broken ls alias from the environment. This has actually # happened. Such a system could not be considered "sane". as_fn_error $? "ls -t appears to fail. Make sure there is not a broken alias in your environment" "$LINENO" 5 fi if test "$2" = conftest.file || test $am_try -eq 2; then break fi # Just in case. sleep 1 am_has_slept=yes done test "$2" = conftest.file ) then # Ok. : else as_fn_error $? "newly created file is older than distributed files! Check your system clock" "$LINENO" 5 fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } # If we didn't sleep, we still need to ensure time stamps of config.status and # generated files are strictly newer. am_sleep_pid= if grep 'slept: no' conftest.file >/dev/null 2>&1; then ( sleep 1 ) & am_sleep_pid=$! fi rm -f conftest.file test "$program_prefix" != NONE && program_transform_name="s&^&$program_prefix&;$program_transform_name" # Use a double $ so make ignores it. test "$program_suffix" != NONE && program_transform_name="s&\$&$program_suffix&;$program_transform_name" # Double any \ or $. # By default was `s,x,x', remove it if useless. ac_script='s/[\\$]/&&/g;s/;s,x,x,$//' program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"` # Expand $ac_aux_dir to an absolute path. am_aux_dir=`cd "$ac_aux_dir" && pwd` if test x"${MISSING+set}" != xset; then MISSING="\${SHELL} '$am_aux_dir/missing'" fi # Use eval to expand $SHELL if eval "$MISSING --is-lightweight"; then am_missing_run="$MISSING " else am_missing_run= { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 'missing' script is too old or missing" >&5 $as_echo "$as_me: WARNING: 'missing' script is too old or missing" >&2;} fi if test x"${install_sh+set}" != xset; then case $am_aux_dir in *\ * | *\ *) install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; *) install_sh="\${SHELL} $am_aux_dir/install-sh" esac fi # Installed binaries are usually stripped using 'strip' when the user # run "make install-strip". However 'strip' might not be the right # tool to use in cross-compilation environments, therefore Automake # will honor the 'STRIP' environment variable to overrule this program. if test "$cross_compiling" != no; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. set dummy ${ac_tool_prefix}strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_STRIP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$STRIP"; then ac_cv_prog_STRIP="$STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_STRIP="${ac_tool_prefix}strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi STRIP=$ac_cv_prog_STRIP if test -n "$STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 $as_echo "$STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_STRIP"; then ac_ct_STRIP=$STRIP # Extract the first word of "strip", so it can be a program name with args. set dummy strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_STRIP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_STRIP"; then ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_STRIP="strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP if test -n "$ac_ct_STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 $as_echo "$ac_ct_STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_STRIP" = x; then STRIP=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac STRIP=$ac_ct_STRIP fi else STRIP="$ac_cv_prog_STRIP" fi fi INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5 $as_echo_n "checking for a thread-safe mkdir -p... " >&6; } if test -z "$MKDIR_P"; then if ${ac_cv_path_mkdir+:} false; then : $as_echo_n "(cached) " >&6 else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in mkdir gmkdir; do for ac_exec_ext in '' $ac_executable_extensions; do as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext" || continue case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #( 'mkdir (GNU coreutils) '* | \ 'mkdir (coreutils) '* | \ 'mkdir (fileutils) '4.1*) ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext break 3;; esac done done done IFS=$as_save_IFS fi test -d ./--version && rmdir ./--version if test "${ac_cv_path_mkdir+set}" = set; then MKDIR_P="$ac_cv_path_mkdir -p" else # As a last resort, use the slow shell script. Don't cache a # value for MKDIR_P within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. MKDIR_P="$ac_install_sh -d" fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5 $as_echo "$MKDIR_P" >&6; } for ac_prog in gawk mawk nawk awk do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_AWK+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$AWK"; then ac_cv_prog_AWK="$AWK" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_AWK="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi AWK=$ac_cv_prog_AWK if test -n "$AWK"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 $as_echo "$AWK" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$AWK" && break done { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 $as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } set x ${MAKE-make} ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : $as_echo_n "(cached) " >&6 else cat >conftest.make <<\_ACEOF SHELL = /bin/sh all: @echo '@@@%%%=$(MAKE)=@@@%%%' _ACEOF # GNU make sometimes prints "make[1]: Entering ...", which would confuse us. case `${MAKE-make} -f conftest.make 2>/dev/null` in *@@@%%%=?*=@@@%%%*) eval ac_cv_prog_make_${ac_make}_set=yes;; *) eval ac_cv_prog_make_${ac_make}_set=no;; esac rm -f conftest.make fi if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } SET_MAKE= else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } SET_MAKE="MAKE=${MAKE-make}" fi rm -rf .tst 2>/dev/null mkdir .tst 2>/dev/null if test -d .tst; then am__leading_dot=. else am__leading_dot=_ fi rmdir .tst 2>/dev/null # Check whether --enable-silent-rules was given. if test "${enable_silent_rules+set}" = set; then : enableval=$enable_silent_rules; fi case $enable_silent_rules in # ((( yes) AM_DEFAULT_VERBOSITY=0;; no) AM_DEFAULT_VERBOSITY=1;; *) AM_DEFAULT_VERBOSITY=1;; esac am_make=${MAKE-make} { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5 $as_echo_n "checking whether $am_make supports nested variables... " >&6; } if ${am_cv_make_support_nested_variables+:} false; then : $as_echo_n "(cached) " >&6 else if $as_echo 'TRUE=$(BAR$(V)) BAR0=false BAR1=true V=1 am__doit: @$(TRUE) .PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then am_cv_make_support_nested_variables=yes else am_cv_make_support_nested_variables=no fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5 $as_echo "$am_cv_make_support_nested_variables" >&6; } if test $am_cv_make_support_nested_variables = yes; then AM_V='$(V)' AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' else AM_V=$AM_DEFAULT_VERBOSITY AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY fi AM_BACKSLASH='\' if test "`cd $srcdir && pwd`" != "`pwd`"; then # Use -I$(srcdir) only when $(srcdir) != ., so that make's output # is not polluted with repeated "-I." am__isrc=' -I$(srcdir)' # test to see if srcdir already configured if test -f $srcdir/config.status; then as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5 fi fi # test whether we have cygpath if test -z "$CYGPATH_W"; then if (cygpath --version) >/dev/null 2>/dev/null; then CYGPATH_W='cygpath -w' else CYGPATH_W=echo fi fi # Define the identity of the package. PACKAGE='libopenmpt' VERSION='0.6.1+release.autotools' cat >>confdefs.h <<_ACEOF #define PACKAGE "$PACKAGE" _ACEOF cat >>confdefs.h <<_ACEOF #define VERSION "$VERSION" _ACEOF # Some tools Automake needs. ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"} AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"} AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"} AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"} MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} # For better backward compatibility. To be removed once Automake 1.9.x # dies out for good. For more background, see: # # mkdir_p='$(MKDIR_P)' # We need awk for the "check" target (and possibly the TAP driver). The # system "awk" is bad on some platforms. # Always define AMTAR for backward compatibility. Yes, it's still used # in the wild :-( We should find a proper way to deprecate it ... AMTAR='$${TAR-tar}' # We'll loop over all known methods to create a tar archive until one works. _am_tools='gnutar pax cpio none' am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -' # POSIX will say in a future version that running "rm -f" with no argument # is OK; and we want to be able to make that assumption in our Makefile # recipes. So use an aggressive probe to check that the usage we want is # actually supported "in the wild" to an acceptable degree. # See automake bug#10828. # To make any issue more visible, cause the running configure to be aborted # by default if the 'rm' program in use doesn't match our expectations; the # user can still override this though. if rm -f && rm -fr && rm -rf; then : OK; else cat >&2 <<'END' Oops! Your 'rm' program seems unable to run without file operands specified on the command line, even when the '-f' option is present. This is contrary to the behaviour of most rm programs out there, and not conforming with the upcoming POSIX standard: Please tell bug-automake@gnu.org about your system, including the value of your $PATH and any error possibly output before this message. This can help us improve future automake versions. END if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then echo 'Configuration will proceed anyway, since you have set the' >&2 echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 echo >&2 else cat >&2 <<'END' Aborting the configuration process, to ensure you take notice of the issue. You can download and install GNU coreutils to get an 'rm' implementation that behaves properly: . If you want to complete the configuration process using your problematic 'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM to "yes", and re-run configure. END as_fn_error $? "Your 'rm' program is bad, sorry." "$LINENO" 5 fi fi DEPDIR="${am__leading_dot}deps" ac_config_commands="$ac_config_commands depfiles" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} supports the include directive" >&5 $as_echo_n "checking whether ${MAKE-make} supports the include directive... " >&6; } cat > confinc.mk << 'END' am__doit: @echo this is the am__doit target >confinc.out .PHONY: am__doit END am__include="#" am__quote= # BSD make does it like this. echo '.include "confinc.mk" # ignored' > confmf.BSD # Other make implementations (GNU, Solaris 10, AIX) do it like this. echo 'include confinc.mk # ignored' > confmf.GNU _am_result=no for s in GNU BSD; do { echo "$as_me:$LINENO: ${MAKE-make} -f confmf.$s && cat confinc.out" >&5 (${MAKE-make} -f confmf.$s && cat confinc.out) >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } case $?:`cat confinc.out 2>/dev/null` in #( '0:this is the am__doit target') : case $s in #( BSD) : am__include='.include' am__quote='"' ;; #( *) : am__include='include' am__quote='' ;; esac ;; #( *) : ;; esac if test "$am__include" != "#"; then _am_result="yes ($s style)" break fi done rm -f confinc.* confmf.* { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${_am_result}" >&5 $as_echo "${_am_result}" >&6; } # Check whether --enable-dependency-tracking was given. if test "${enable_dependency_tracking+set}" = set; then : enableval=$enable_dependency_tracking; fi if test "x$enable_dependency_tracking" != xno; then am_depcomp="$ac_aux_dir/depcomp" AMDEPBACKSLASH='\' am__nodep='_no' fi if test "x$enable_dependency_tracking" != xno; then AMDEP_TRUE= AMDEP_FALSE='#' else AMDEP_TRUE='#' AMDEP_FALSE= fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See \`config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 $as_echo_n "checking whether the C compiler works... " >&6; } ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` # The possible output files: ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" ac_rmfiles= for ac_file in $ac_files do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; * ) ac_rmfiles="$ac_rmfiles $ac_file";; esac done rm -f $ac_rmfiles if { { ac_try="$ac_link_default" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link_default") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. # So ignore a value of `no', otherwise this would lead to `EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, # so that the user can short-circuit this test for compilers unknown to # Autoconf. for ac_file in $ac_files '' do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; then :; else ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi # We set ac_cv_exeext here because the later test for it is not # safe: cross compilers may not add the suffix if given an `-o' # argument, so we may need to know it at that point already. # Even if this section looks crufty: it has the advantage of # actually working. break;; * ) break;; esac done test "$ac_cv_exeext" = no && ac_cv_exeext= else ac_file='' fi if test -z "$ac_file"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "C compiler cannot create executables See \`config.log' for more details" "$LINENO" 5; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 $as_echo_n "checking for C compiler default output file name... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 $as_echo "$ac_file" >&6; } ac_exeext=$ac_cv_exeext rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 $as_echo_n "checking for suffix of executables... " >&6; } if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with # `rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` break;; * ) break;; esac done else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of executables: cannot compile and link See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest conftest$ac_cv_exeext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 $as_echo "$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { FILE *f = fopen ("conftest.out", "w"); return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF ac_clean_files="$ac_clean_files conftest.out" # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 $as_echo_n "checking whether we are cross compiling... " >&6; } if test "$cross_compiling" != yes; then { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if { ac_try='./conftest$ac_cv_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details" "$LINENO" 5; } fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 $as_echo "$cross_compiling" >&6; } rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 $as_echo_n "checking for suffix of object files... " >&6; } if ${ac_cv_objext+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : for ac_file in conftest.o conftest.obj conftest.*; do test -f "$ac_file" || continue; case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of object files: cannot compile See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 $as_echo "$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 $as_echo_n "checking whether we are using the GNU C compiler... " >&6; } if ${ac_cv_c_compiler_gnu+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 $as_echo "$ac_cv_c_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+set} ac_save_CFLAGS=$CFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 $as_echo_n "checking whether $CC accepts -g... " >&6; } if ${ac_cv_prog_cc_g+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes else CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : else ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 $as_echo "$ac_cv_prog_cc_g" >&6; } if test "$ac_test_CFLAGS" = set; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 $as_echo_n "checking for $CC option to accept ISO C89... " >&6; } if ${ac_cv_prog_cc_c89+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include struct stat; /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not '\xHH' hex character constants. These don't provoke an error unfortunately, instead are silently treated as 'x'. The following induces an error, until -std is added to get proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an array size at least. It's necessary to write '\x00'==0 to get something that's true only with -std. */ int osf4_cc_array ['\x00' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) 'x' int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); int argc; char **argv; int main () { return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; ; return 0; } _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c89" in x) { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } ;; xno) { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c89" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 $as_echo "$ac_cv_prog_cc_c89" >&6; } ;; esac if test "x$ac_cv_prog_cc_c89" != xno; then : fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5 $as_echo_n "checking whether $CC understands -c and -o together... " >&6; } if ${am_cv_prog_cc_c_o+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF # Make sure it works both with $CC and with simple cc. # Following AC_PROG_CC_C_O, we do the test twice because some # compilers refuse to overwrite an existing .o file with -o, # though they will create one. am_cv_prog_cc_c_o=yes for am_i in 1 2; do if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5 ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } \ && test -f conftest2.$ac_objext; then : OK else am_cv_prog_cc_c_o=no break fi done rm -f core conftest* unset am_i fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5 $as_echo "$am_cv_prog_cc_c_o" >&6; } if test "$am_cv_prog_cc_c_o" != yes; then # Losing compiler, so override with the script. # FIXME: It is wrong to rewrite CC. # But if we don't then we get into trouble of one sort or another. # A longer-term fix would be to have automake use am__CC in this case, # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" CC="$am_aux_dir/compile $CC" fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu depcc="$CC" am_compiler_list= { $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 $as_echo_n "checking dependency style of $depcc... " >&6; } if ${am_cv_CC_dependencies_compiler_type+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For # instance it was reported that on HP-UX the gcc test will end up # making a dummy file named 'D' -- because '-MD' means "put the output # in D". rm -rf conftest.dir mkdir conftest.dir # Copy depcomp to subdir because otherwise we won't find it if we're # using a relative directory. cp "$am_depcomp" conftest.dir cd conftest.dir # We will build objects and dependencies in a subdirectory because # it helps to detect inapplicable dependency modes. For instance # both Tru64's cc and ICC support -MD to output dependencies as a # side effect of compilation, but ICC will put the dependencies in # the current directory while Tru64 will put them in the object # directory. mkdir sub am_cv_CC_dependencies_compiler_type=none if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` fi am__universal=false case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac for depmode in $am_compiler_list; do # Setup a source with many dependencies, because some compilers # like to wrap large dependency lists on column 80 (with \), and # we should not choose a depcomp mode which is confused by this. # # We need to recreate these files for each test, as the compiler may # overwrite some of them when testing with obscure command lines. # This happens at least with the AIX C compiler. : > sub/conftest.c for i in 1 2 3 4 5 6; do echo '#include "conftst'$i'.h"' >> sub/conftest.c # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with # Solaris 10 /bin/sh. echo '/* dummy */' > sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf # We check with '-c' and '-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly # handle '-M -o', and we need to detect this. Also, some Intel # versions had trouble with output in subdirs. am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in gcc) # This depmode causes a compiler race in universal mode. test "$am__universal" = false || continue ;; nosideeffect) # After this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested. if test "x$enable_dependency_tracking" = xyes; then continue else break fi ;; msvc7 | msvc7msys | msvisualcpp | msvcmsys) # This compiler won't grok '-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} am__minus_obj= ;; none) break ;; esac if depmode=$depmode \ source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && grep $am__obj sub/conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then # icc doesn't choke on unknown options, it will just issue warnings # or remarks (even with -Werror). So we grep stderr for any message # that says an option was ignored or not supported. # When given -MP, icc 7.0 and 7.1 complain thusly: # icc: Command line warning: ignoring option '-M'; no argument required # The diagnosis changed in icc 8.0: # icc: Command line remark: option '-MP' not supported if (grep 'ignoring option' conftest.err || grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else am_cv_CC_dependencies_compiler_type=$depmode break fi fi done cd .. rm -rf conftest.dir else am_cv_CC_dependencies_compiler_type=none fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 $as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type if test "x$enable_dependency_tracking" != xno \ && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then am__fastdepCC_TRUE= am__fastdepCC_FALSE='#' else am__fastdepCC_TRUE='#' am__fastdepCC_FALSE= fi if test -n "$ac_tool_prefix"; then for ac_prog in ar lib "link -lib" do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_AR+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$AR"; then ac_cv_prog_AR="$AR" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_AR="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi AR=$ac_cv_prog_AR if test -n "$AR"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 $as_echo "$AR" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$AR" && break done fi if test -z "$AR"; then ac_ct_AR=$AR for ac_prog in ar lib "link -lib" do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_AR+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_AR"; then ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_AR="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_AR=$ac_cv_prog_ac_ct_AR if test -n "$ac_ct_AR"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5 $as_echo "$ac_ct_AR" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_AR" && break done if test "x$ac_ct_AR" = x; then AR="false" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac AR=$ac_ct_AR fi fi : ${AR=ar} { $as_echo "$as_me:${as_lineno-$LINENO}: checking the archiver ($AR) interface" >&5 $as_echo_n "checking the archiver ($AR) interface... " >&6; } if ${am_cv_ar_interface+:} false; then : $as_echo_n "(cached) " >&6 else ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu am_cv_ar_interface=ar cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int some_variable = 0; _ACEOF if ac_fn_c_try_compile "$LINENO"; then : am_ar_try='$AR cru libconftest.a conftest.$ac_objext >&5' { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$am_ar_try\""; } >&5 (eval $am_ar_try) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if test "$ac_status" -eq 0; then am_cv_ar_interface=ar else am_ar_try='$AR -NOLOGO -OUT:conftest.lib conftest.$ac_objext >&5' { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$am_ar_try\""; } >&5 (eval $am_ar_try) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if test "$ac_status" -eq 0; then am_cv_ar_interface=lib else am_cv_ar_interface=unknown fi fi rm -f conftest.lib libconftest.a fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_ar_interface" >&5 $as_echo "$am_cv_ar_interface" >&6; } case $am_cv_ar_interface in ar) ;; lib) # Microsoft lib, so override with the ar-lib wrapper script. # FIXME: It is wrong to rewrite AR. # But if we don't then we get into trouble of one sort or another. # A longer-term fix would be to have automake use am__AR in this case, # and then we could set am__AR="$am_aux_dir/ar-lib \$(AR)" or something # similar. AR="$am_aux_dir/ar-lib $AR" ;; unknown) as_fn_error $? "could not determine $AR interface" "$LINENO" 5 ;; esac case `pwd` in *\ * | *\ *) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&5 $as_echo "$as_me: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&2;} ;; esac macro_version='2.4.6' macro_revision='2.4.6' ltmain=$ac_aux_dir/ltmain.sh # Make sure we can run config.sub. $SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 $as_echo_n "checking build system type... " >&6; } if ${ac_cv_build+:} false; then : $as_echo_n "(cached) " >&6 else ac_build_alias=$build_alias test "x$ac_build_alias" = x && ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` test "x$ac_build_alias" = x && as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 $as_echo "$ac_cv_build" >&6; } case $ac_cv_build in *-*-*) ;; *) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; esac build=$ac_cv_build ac_save_IFS=$IFS; IFS='-' set x $ac_cv_build shift build_cpu=$1 build_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: build_os=$* IFS=$ac_save_IFS case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 $as_echo_n "checking host system type... " >&6; } if ${ac_cv_host+:} false; then : $as_echo_n "(cached) " >&6 else if test "x$host_alias" = x; then ac_cv_host=$ac_cv_build else ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 $as_echo "$ac_cv_host" >&6; } case $ac_cv_host in *-*-*) ;; *) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; esac host=$ac_cv_host ac_save_IFS=$IFS; IFS='-' set x $ac_cv_host shift host_cpu=$1 host_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: host_os=$* IFS=$ac_save_IFS case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac # Backslashify metacharacters that are still active within # double-quoted strings. sed_quote_subst='s/\(["`$\\]\)/\\\1/g' # Same as above, but do not quote variable references. double_quote_subst='s/\(["`\\]\)/\\\1/g' # Sed substitution to delay expansion of an escaped shell variable in a # double_quote_subst'ed string. delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' # Sed substitution to delay expansion of an escaped single quote. delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g' # Sed substitution to avoid accidental globbing in evaled expressions no_glob_subst='s/\*/\\\*/g' ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to print strings" >&5 $as_echo_n "checking how to print strings... " >&6; } # Test print first, because it will be a builtin if present. if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \ test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then ECHO='print -r --' elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then ECHO='printf %s\n' else # Use this function as a fallback that always works. func_fallback_echo () { eval 'cat <<_LTECHO_EOF $1 _LTECHO_EOF' } ECHO='func_fallback_echo' fi # func_echo_all arg... # Invoke $ECHO with all args, space-separated. func_echo_all () { $ECHO "" } case $ECHO in printf*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: printf" >&5 $as_echo "printf" >&6; } ;; print*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: print -r" >&5 $as_echo "print -r" >&6; } ;; *) { $as_echo "$as_me:${as_lineno-$LINENO}: result: cat" >&5 $as_echo "cat" >&6; } ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5 $as_echo_n "checking for a sed that does not truncate output... " >&6; } if ${ac_cv_path_SED+:} false; then : $as_echo_n "(cached) " >&6 else ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ for ac_i in 1 2 3 4 5 6 7; do ac_script="$ac_script$as_nl$ac_script" done echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed { ac_script=; unset ac_script;} if test -z "$SED"; then ac_path_SED_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in sed gsed; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_SED="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_SED" || continue # Check for GNU ac_path_SED and select it if it is found. # Check for GNU $ac_path_SED case `"$ac_path_SED" --version 2>&1` in *GNU*) ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo '' >> "conftest.nl" "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_SED_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_SED="$ac_path_SED" ac_path_SED_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_SED_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_SED"; then as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5 fi else ac_cv_path_SED=$SED fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5 $as_echo "$ac_cv_path_SED" >&6; } SED="$ac_cv_path_SED" rm -f conftest.sed test -z "$SED" && SED=sed Xsed="$SED -e 1s/^X//" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 $as_echo_n "checking for grep that handles long lines and -e... " >&6; } if ${ac_cv_path_GREP+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$GREP"; then ac_path_GREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in grep ggrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_GREP" || continue # Check for GNU ac_path_GREP and select it if it is found. # Check for GNU $ac_path_GREP case `"$ac_path_GREP" --version 2>&1` in *GNU*) ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'GREP' >> "conftest.nl" "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_GREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_GREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_GREP"; then as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_GREP=$GREP fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 $as_echo "$ac_cv_path_GREP" >&6; } GREP="$ac_cv_path_GREP" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 $as_echo_n "checking for egrep... " >&6; } if ${ac_cv_path_EGREP+:} false; then : $as_echo_n "(cached) " >&6 else if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 then ac_cv_path_EGREP="$GREP -E" else if test -z "$EGREP"; then ac_path_EGREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in egrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_EGREP" || continue # Check for GNU ac_path_EGREP and select it if it is found. # Check for GNU $ac_path_EGREP case `"$ac_path_EGREP" --version 2>&1` in *GNU*) ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'EGREP' >> "conftest.nl" "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_EGREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_EGREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_EGREP"; then as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_EGREP=$EGREP fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 $as_echo "$ac_cv_path_EGREP" >&6; } EGREP="$ac_cv_path_EGREP" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for fgrep" >&5 $as_echo_n "checking for fgrep... " >&6; } if ${ac_cv_path_FGREP+:} false; then : $as_echo_n "(cached) " >&6 else if echo 'ab*c' | $GREP -F 'ab*c' >/dev/null 2>&1 then ac_cv_path_FGREP="$GREP -F" else if test -z "$FGREP"; then ac_path_FGREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in fgrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_FGREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_FGREP" || continue # Check for GNU ac_path_FGREP and select it if it is found. # Check for GNU $ac_path_FGREP case `"$ac_path_FGREP" --version 2>&1` in *GNU*) ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'FGREP' >> "conftest.nl" "$ac_path_FGREP" FGREP < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_FGREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_FGREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_FGREP"; then as_fn_error $? "no acceptable fgrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_FGREP=$FGREP fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_FGREP" >&5 $as_echo "$ac_cv_path_FGREP" >&6; } FGREP="$ac_cv_path_FGREP" test -z "$GREP" && GREP=grep # Check whether --with-gnu-ld was given. if test "${with_gnu_ld+set}" = set; then : withval=$with_gnu_ld; test no = "$withval" || with_gnu_ld=yes else with_gnu_ld=no fi ac_prog=ld if test yes = "$GCC"; then # Check if gcc -print-prog-name=ld gives a path. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5 $as_echo_n "checking for ld used by $CC... " >&6; } case $host in *-*-mingw*) # gcc leaves a trailing carriage return, which upsets mingw ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; *) ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; esac case $ac_prog in # Accept absolute paths. [\\/]* | ?:[\\/]*) re_direlt='/[^/][^/]*/\.\./' # Canonicalize the pathname of ld ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` done test -z "$LD" && LD=$ac_prog ;; "") # If it fails, then pretend we aren't using GCC. ac_prog=ld ;; *) # If it is relative, then search for the first ld in PATH. with_gnu_ld=unknown ;; esac elif test yes = "$with_gnu_ld"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5 $as_echo_n "checking for GNU ld... " >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5 $as_echo_n "checking for non-GNU ld... " >&6; } fi if ${lt_cv_path_LD+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$LD"; then lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR for ac_dir in $PATH; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then lt_cv_path_LD=$ac_dir/$ac_prog # Check to see if the program is GNU ld. I'd rather use --version, # but apparently some variants of GNU ld only accept -v. # Break only if it was the GNU/non-GNU ld that we prefer. case `"$lt_cv_path_LD" -v 2>&1 &5 $as_echo "$LD" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5 $as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; } if ${lt_cv_prog_gnu_ld+:} false; then : $as_echo_n "(cached) " >&6 else # I'd rather use --version here, but apparently some GNU lds only accept -v. case `$LD -v 2>&1 &5 $as_echo "$lt_cv_prog_gnu_ld" >&6; } with_gnu_ld=$lt_cv_prog_gnu_ld { $as_echo "$as_me:${as_lineno-$LINENO}: checking for BSD- or MS-compatible name lister (nm)" >&5 $as_echo_n "checking for BSD- or MS-compatible name lister (nm)... " >&6; } if ${lt_cv_path_NM+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$NM"; then # Let the user override the test. lt_cv_path_NM=$NM else lt_nm_to_check=${ac_tool_prefix}nm if test -n "$ac_tool_prefix" && test "$build" = "$host"; then lt_nm_to_check="$lt_nm_to_check nm" fi for lt_tmp_nm in $lt_nm_to_check; do lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. tmp_nm=$ac_dir/$lt_tmp_nm if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext"; then # Check to see if the nm accepts a BSD-compat flag. # Adding the 'sed 1q' prevents false positives on HP-UX, which says: # nm: unknown option "B" ignored # Tru64's nm complains that /dev/null is an invalid object file # MSYS converts /dev/null to NUL, MinGW nm treats NUL as empty case $build_os in mingw*) lt_bad_file=conftest.nm/nofile ;; *) lt_bad_file=/dev/null ;; esac case `"$tmp_nm" -B $lt_bad_file 2>&1 | sed '1q'` in *$lt_bad_file* | *'Invalid file or object type'*) lt_cv_path_NM="$tmp_nm -B" break 2 ;; *) case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in */dev/null*) lt_cv_path_NM="$tmp_nm -p" break 2 ;; *) lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but continue # so that we can try to find one that supports BSD flags ;; esac ;; esac fi done IFS=$lt_save_ifs done : ${lt_cv_path_NM=no} fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_NM" >&5 $as_echo "$lt_cv_path_NM" >&6; } if test no != "$lt_cv_path_NM"; then NM=$lt_cv_path_NM else # Didn't find any BSD compatible name lister, look for dumpbin. if test -n "$DUMPBIN"; then : # Let the user override the test. else if test -n "$ac_tool_prefix"; then for ac_prog in dumpbin "link -dump" do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_DUMPBIN+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$DUMPBIN"; then ac_cv_prog_DUMPBIN="$DUMPBIN" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_DUMPBIN="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi DUMPBIN=$ac_cv_prog_DUMPBIN if test -n "$DUMPBIN"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DUMPBIN" >&5 $as_echo "$DUMPBIN" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$DUMPBIN" && break done fi if test -z "$DUMPBIN"; then ac_ct_DUMPBIN=$DUMPBIN for ac_prog in dumpbin "link -dump" do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_DUMPBIN+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_DUMPBIN"; then ac_cv_prog_ac_ct_DUMPBIN="$ac_ct_DUMPBIN" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_DUMPBIN="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_DUMPBIN=$ac_cv_prog_ac_ct_DUMPBIN if test -n "$ac_ct_DUMPBIN"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DUMPBIN" >&5 $as_echo "$ac_ct_DUMPBIN" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_DUMPBIN" && break done if test "x$ac_ct_DUMPBIN" = x; then DUMPBIN=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac DUMPBIN=$ac_ct_DUMPBIN fi fi case `$DUMPBIN -symbols -headers /dev/null 2>&1 | sed '1q'` in *COFF*) DUMPBIN="$DUMPBIN -symbols -headers" ;; *) DUMPBIN=: ;; esac fi if test : != "$DUMPBIN"; then NM=$DUMPBIN fi fi test -z "$NM" && NM=nm { $as_echo "$as_me:${as_lineno-$LINENO}: checking the name lister ($NM) interface" >&5 $as_echo_n "checking the name lister ($NM) interface... " >&6; } if ${lt_cv_nm_interface+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_nm_interface="BSD nm" echo "int some_variable = 0;" > conftest.$ac_ext (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&5) (eval "$ac_compile" 2>conftest.err) cat conftest.err >&5 (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&5) (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) cat conftest.err >&5 (eval echo "\"\$as_me:$LINENO: output\"" >&5) cat conftest.out >&5 if $GREP 'External.*some_variable' conftest.out > /dev/null; then lt_cv_nm_interface="MS dumpbin" fi rm -f conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_nm_interface" >&5 $as_echo "$lt_cv_nm_interface" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5 $as_echo_n "checking whether ln -s works... " >&6; } LN_S=$as_ln_s if test "$LN_S" = "ln -s"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5 $as_echo "no, using $LN_S" >&6; } fi # find the maximum length of command line arguments { $as_echo "$as_me:${as_lineno-$LINENO}: checking the maximum length of command line arguments" >&5 $as_echo_n "checking the maximum length of command line arguments... " >&6; } if ${lt_cv_sys_max_cmd_len+:} false; then : $as_echo_n "(cached) " >&6 else i=0 teststring=ABCD case $build_os in msdosdjgpp*) # On DJGPP, this test can blow up pretty badly due to problems in libc # (any single argument exceeding 2000 bytes causes a buffer overrun # during glob expansion). Even if it were fixed, the result of this # check would be larger than it should be. lt_cv_sys_max_cmd_len=12288; # 12K is about right ;; gnu*) # Under GNU Hurd, this test is not required because there is # no limit to the length of command line arguments. # Libtool will interpret -1 as no limit whatsoever lt_cv_sys_max_cmd_len=-1; ;; cygwin* | mingw* | cegcc*) # On Win9x/ME, this test blows up -- it succeeds, but takes # about 5 minutes as the teststring grows exponentially. # Worse, since 9x/ME are not pre-emptively multitasking, # you end up with a "frozen" computer, even though with patience # the test eventually succeeds (with a max line length of 256k). # Instead, let's just punt: use the minimum linelength reported by # all of the supported platforms: 8192 (on NT/2K/XP). lt_cv_sys_max_cmd_len=8192; ;; mint*) # On MiNT this can take a long time and run out of memory. lt_cv_sys_max_cmd_len=8192; ;; amigaos*) # On AmigaOS with pdksh, this test takes hours, literally. # So we just punt and use a minimum line length of 8192. lt_cv_sys_max_cmd_len=8192; ;; bitrig* | darwin* | dragonfly* | freebsd* | netbsd* | openbsd*) # This has been around since 386BSD, at least. Likely further. if test -x /sbin/sysctl; then lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` elif test -x /usr/sbin/sysctl; then lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` else lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs fi # And add a safety zone lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` ;; interix*) # We know the value 262144 and hardcode it with a safety zone (like BSD) lt_cv_sys_max_cmd_len=196608 ;; os2*) # The test takes a long time on OS/2. lt_cv_sys_max_cmd_len=8192 ;; osf*) # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not # nice to cause kernel panics so lets avoid the loop below. # First set a reasonable default. lt_cv_sys_max_cmd_len=16384 # if test -x /sbin/sysconfig; then case `/sbin/sysconfig -q proc exec_disable_arg_limit` in *1*) lt_cv_sys_max_cmd_len=-1 ;; esac fi ;; sco3.2v5*) lt_cv_sys_max_cmd_len=102400 ;; sysv5* | sco5v6* | sysv4.2uw2*) kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` if test -n "$kargmax"; then lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[ ]//'` else lt_cv_sys_max_cmd_len=32768 fi ;; *) lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null` if test -n "$lt_cv_sys_max_cmd_len" && \ test undefined != "$lt_cv_sys_max_cmd_len"; then lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` else # Make teststring a little bigger before we do anything with it. # a 1K string should be a reasonable start. for i in 1 2 3 4 5 6 7 8; do teststring=$teststring$teststring done SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} # If test is not a shell built-in, we'll probably end up computing a # maximum length that is only half of the actual maximum length, but # we can't tell. while { test X`env echo "$teststring$teststring" 2>/dev/null` \ = "X$teststring$teststring"; } >/dev/null 2>&1 && test 17 != "$i" # 1/2 MB should be enough do i=`expr $i + 1` teststring=$teststring$teststring done # Only check the string length outside the loop. lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1` teststring= # Add a significant safety factor because C++ compilers can tack on # massive amounts of additional arguments before passing them to the # linker. It appears as though 1/2 is a usable value. lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` fi ;; esac fi if test -n "$lt_cv_sys_max_cmd_len"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sys_max_cmd_len" >&5 $as_echo "$lt_cv_sys_max_cmd_len" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: none" >&5 $as_echo "none" >&6; } fi max_cmd_len=$lt_cv_sys_max_cmd_len : ${CP="cp -f"} : ${MV="mv -f"} : ${RM="rm -f"} if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then lt_unset=unset else lt_unset=false fi # test EBCDIC or ASCII case `echo X|tr X '\101'` in A) # ASCII based system # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr lt_SP2NL='tr \040 \012' lt_NL2SP='tr \015\012 \040\040' ;; *) # EBCDIC based system lt_SP2NL='tr \100 \n' lt_NL2SP='tr \r\n \100\100' ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to $host format" >&5 $as_echo_n "checking how to convert $build file names to $host format... " >&6; } if ${lt_cv_to_host_file_cmd+:} false; then : $as_echo_n "(cached) " >&6 else case $host in *-*-mingw* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32 ;; *-*-cygwin* ) lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32 ;; * ) # otherwise, assume *nix lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32 ;; esac ;; *-*-cygwin* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin ;; *-*-cygwin* ) lt_cv_to_host_file_cmd=func_convert_file_noop ;; * ) # otherwise, assume *nix lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin ;; esac ;; * ) # unhandled hosts (and "normal" native builds) lt_cv_to_host_file_cmd=func_convert_file_noop ;; esac fi to_host_file_cmd=$lt_cv_to_host_file_cmd { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_host_file_cmd" >&5 $as_echo "$lt_cv_to_host_file_cmd" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to toolchain format" >&5 $as_echo_n "checking how to convert $build file names to toolchain format... " >&6; } if ${lt_cv_to_tool_file_cmd+:} false; then : $as_echo_n "(cached) " >&6 else #assume ordinary cross tools, or native build. lt_cv_to_tool_file_cmd=func_convert_file_noop case $host in *-*-mingw* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32 ;; esac ;; esac fi to_tool_file_cmd=$lt_cv_to_tool_file_cmd { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_tool_file_cmd" >&5 $as_echo "$lt_cv_to_tool_file_cmd" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $LD option to reload object files" >&5 $as_echo_n "checking for $LD option to reload object files... " >&6; } if ${lt_cv_ld_reload_flag+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_ld_reload_flag='-r' fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_reload_flag" >&5 $as_echo "$lt_cv_ld_reload_flag" >&6; } reload_flag=$lt_cv_ld_reload_flag case $reload_flag in "" | " "*) ;; *) reload_flag=" $reload_flag" ;; esac reload_cmds='$LD$reload_flag -o $output$reload_objs' case $host_os in cygwin* | mingw* | pw32* | cegcc*) if test yes != "$GCC"; then reload_cmds=false fi ;; darwin*) if test yes = "$GCC"; then reload_cmds='$LTCC $LTCFLAGS -nostdlib $wl-r -o $output$reload_objs' else reload_cmds='$LD$reload_flag -o $output$reload_objs' fi ;; esac if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}objdump", so it can be a program name with args. set dummy ${ac_tool_prefix}objdump; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_OBJDUMP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$OBJDUMP"; then ac_cv_prog_OBJDUMP="$OBJDUMP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_OBJDUMP="${ac_tool_prefix}objdump" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi OBJDUMP=$ac_cv_prog_OBJDUMP if test -n "$OBJDUMP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OBJDUMP" >&5 $as_echo "$OBJDUMP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_OBJDUMP"; then ac_ct_OBJDUMP=$OBJDUMP # Extract the first word of "objdump", so it can be a program name with args. set dummy objdump; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_OBJDUMP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_OBJDUMP"; then ac_cv_prog_ac_ct_OBJDUMP="$ac_ct_OBJDUMP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_OBJDUMP="objdump" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_OBJDUMP=$ac_cv_prog_ac_ct_OBJDUMP if test -n "$ac_ct_OBJDUMP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OBJDUMP" >&5 $as_echo "$ac_ct_OBJDUMP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_OBJDUMP" = x; then OBJDUMP="false" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac OBJDUMP=$ac_ct_OBJDUMP fi else OBJDUMP="$ac_cv_prog_OBJDUMP" fi test -z "$OBJDUMP" && OBJDUMP=objdump { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to recognize dependent libraries" >&5 $as_echo_n "checking how to recognize dependent libraries... " >&6; } if ${lt_cv_deplibs_check_method+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_file_magic_cmd='$MAGIC_CMD' lt_cv_file_magic_test_file= lt_cv_deplibs_check_method='unknown' # Need to set the preceding variable on all platforms that support # interlibrary dependencies. # 'none' -- dependencies not supported. # 'unknown' -- same as none, but documents that we really don't know. # 'pass_all' -- all dependencies passed with no checks. # 'test_compile' -- check by making test program. # 'file_magic [[regex]]' -- check by looking for files in library path # that responds to the $file_magic_cmd with a given extended regex. # If you have 'file' or equivalent on your system and you're not sure # whether 'pass_all' will *always* work, you probably want this one. case $host_os in aix[4-9]*) lt_cv_deplibs_check_method=pass_all ;; beos*) lt_cv_deplibs_check_method=pass_all ;; bsdi[45]*) lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib)' lt_cv_file_magic_cmd='/usr/bin/file -L' lt_cv_file_magic_test_file=/shlib/libc.so ;; cygwin*) # func_win32_libid is a shell function defined in ltmain.sh lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' lt_cv_file_magic_cmd='func_win32_libid' ;; mingw* | pw32*) # Base MSYS/MinGW do not provide the 'file' command needed by # func_win32_libid shell function, so use a weaker test based on 'objdump', # unless we find 'file', for example because we are cross-compiling. if ( file / ) >/dev/null 2>&1; then lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' lt_cv_file_magic_cmd='func_win32_libid' else # Keep this pattern in sync with the one in func_win32_libid. lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' lt_cv_file_magic_cmd='$OBJDUMP -f' fi ;; cegcc*) # use the weaker test based on 'objdump'. See mingw*. lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?' lt_cv_file_magic_cmd='$OBJDUMP -f' ;; darwin* | rhapsody*) lt_cv_deplibs_check_method=pass_all ;; freebsd* | dragonfly*) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then case $host_cpu in i*86 ) # Not sure whether the presence of OpenBSD here was a mistake. # Let's accept both of them until this is cleared up. lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[3-9]86 (compact )?demand paged shared library' lt_cv_file_magic_cmd=/usr/bin/file lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` ;; esac else lt_cv_deplibs_check_method=pass_all fi ;; haiku*) lt_cv_deplibs_check_method=pass_all ;; hpux10.20* | hpux11*) lt_cv_file_magic_cmd=/usr/bin/file case $host_cpu in ia64*) lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - IA64' lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so ;; hppa*64*) lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]' lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl ;; *) lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|PA-RISC[0-9]\.[0-9]) shared library' lt_cv_file_magic_test_file=/usr/lib/libc.sl ;; esac ;; interix[3-9]*) # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|\.a)$' ;; irix5* | irix6* | nonstopux*) case $LD in *-32|*"-32 ") libmagic=32-bit;; *-n32|*"-n32 ") libmagic=N32;; *-64|*"-64 ") libmagic=64-bit;; *) libmagic=never-match;; esac lt_cv_deplibs_check_method=pass_all ;; # This must be glibc/ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) lt_cv_deplibs_check_method=pass_all ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|_pic\.a)$' fi ;; newos6*) lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (executable|dynamic lib)' lt_cv_file_magic_cmd=/usr/bin/file lt_cv_file_magic_test_file=/usr/lib/libnls.so ;; *nto* | *qnx*) lt_cv_deplibs_check_method=pass_all ;; openbsd* | bitrig*) if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|\.so|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$' fi ;; osf3* | osf4* | osf5*) lt_cv_deplibs_check_method=pass_all ;; rdos*) lt_cv_deplibs_check_method=pass_all ;; solaris*) lt_cv_deplibs_check_method=pass_all ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) lt_cv_deplibs_check_method=pass_all ;; sysv4 | sysv4.3*) case $host_vendor in motorola) lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib) M[0-9][0-9]* Version [0-9]' lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` ;; ncr) lt_cv_deplibs_check_method=pass_all ;; sequent) lt_cv_file_magic_cmd='/bin/file' lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB (shared object|dynamic lib )' ;; sni) lt_cv_file_magic_cmd='/bin/file' lt_cv_deplibs_check_method="file_magic ELF [0-9][0-9]*-bit [LM]SB dynamic lib" lt_cv_file_magic_test_file=/lib/libc.so ;; siemens) lt_cv_deplibs_check_method=pass_all ;; pc) lt_cv_deplibs_check_method=pass_all ;; esac ;; tpf*) lt_cv_deplibs_check_method=pass_all ;; os2*) lt_cv_deplibs_check_method=pass_all ;; esac fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_deplibs_check_method" >&5 $as_echo "$lt_cv_deplibs_check_method" >&6; } file_magic_glob= want_nocaseglob=no if test "$build" = "$host"; then case $host_os in mingw* | pw32*) if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then want_nocaseglob=yes else file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[\1]\/[\1]\/g;/g"` fi ;; esac fi file_magic_cmd=$lt_cv_file_magic_cmd deplibs_check_method=$lt_cv_deplibs_check_method test -z "$deplibs_check_method" && deplibs_check_method=unknown if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}dlltool", so it can be a program name with args. set dummy ${ac_tool_prefix}dlltool; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_DLLTOOL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$DLLTOOL"; then ac_cv_prog_DLLTOOL="$DLLTOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_DLLTOOL="${ac_tool_prefix}dlltool" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi DLLTOOL=$ac_cv_prog_DLLTOOL if test -n "$DLLTOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DLLTOOL" >&5 $as_echo "$DLLTOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_DLLTOOL"; then ac_ct_DLLTOOL=$DLLTOOL # Extract the first word of "dlltool", so it can be a program name with args. set dummy dlltool; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_DLLTOOL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_DLLTOOL"; then ac_cv_prog_ac_ct_DLLTOOL="$ac_ct_DLLTOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_DLLTOOL="dlltool" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_DLLTOOL=$ac_cv_prog_ac_ct_DLLTOOL if test -n "$ac_ct_DLLTOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DLLTOOL" >&5 $as_echo "$ac_ct_DLLTOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_DLLTOOL" = x; then DLLTOOL="false" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac DLLTOOL=$ac_ct_DLLTOOL fi else DLLTOOL="$ac_cv_prog_DLLTOOL" fi test -z "$DLLTOOL" && DLLTOOL=dlltool { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to associate runtime and link libraries" >&5 $as_echo_n "checking how to associate runtime and link libraries... " >&6; } if ${lt_cv_sharedlib_from_linklib_cmd+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_sharedlib_from_linklib_cmd='unknown' case $host_os in cygwin* | mingw* | pw32* | cegcc*) # two different shell functions defined in ltmain.sh; # decide which one to use based on capabilities of $DLLTOOL case `$DLLTOOL --help 2>&1` in *--identify-strict*) lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib ;; *) lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback ;; esac ;; *) # fallback: assume linklib IS sharedlib lt_cv_sharedlib_from_linklib_cmd=$ECHO ;; esac fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sharedlib_from_linklib_cmd" >&5 $as_echo "$lt_cv_sharedlib_from_linklib_cmd" >&6; } sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO if test -n "$ac_tool_prefix"; then for ac_prog in ar do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_AR+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$AR"; then ac_cv_prog_AR="$AR" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_AR="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi AR=$ac_cv_prog_AR if test -n "$AR"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 $as_echo "$AR" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$AR" && break done fi if test -z "$AR"; then ac_ct_AR=$AR for ac_prog in ar do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_AR+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_AR"; then ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_AR="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_AR=$ac_cv_prog_ac_ct_AR if test -n "$ac_ct_AR"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5 $as_echo "$ac_ct_AR" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_AR" && break done if test "x$ac_ct_AR" = x; then AR="false" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac AR=$ac_ct_AR fi fi : ${AR=ar} : ${AR_FLAGS=cr} { $as_echo "$as_me:${as_lineno-$LINENO}: checking for archiver @FILE support" >&5 $as_echo_n "checking for archiver @FILE support... " >&6; } if ${lt_cv_ar_at_file+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_ar_at_file=no cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : echo conftest.$ac_objext > conftest.lst lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&5' { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5 (eval $lt_ar_try) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if test 0 -eq "$ac_status"; then # Ensure the archiver fails upon bogus file names. rm -f conftest.$ac_objext libconftest.a { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5 (eval $lt_ar_try) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if test 0 -ne "$ac_status"; then lt_cv_ar_at_file=@ fi fi rm -f conftest.* libconftest.a fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ar_at_file" >&5 $as_echo "$lt_cv_ar_at_file" >&6; } if test no = "$lt_cv_ar_at_file"; then archiver_list_spec= else archiver_list_spec=$lt_cv_ar_at_file fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. set dummy ${ac_tool_prefix}strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_STRIP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$STRIP"; then ac_cv_prog_STRIP="$STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_STRIP="${ac_tool_prefix}strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi STRIP=$ac_cv_prog_STRIP if test -n "$STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 $as_echo "$STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_STRIP"; then ac_ct_STRIP=$STRIP # Extract the first word of "strip", so it can be a program name with args. set dummy strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_STRIP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_STRIP"; then ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_STRIP="strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP if test -n "$ac_ct_STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 $as_echo "$ac_ct_STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_STRIP" = x; then STRIP=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac STRIP=$ac_ct_STRIP fi else STRIP="$ac_cv_prog_STRIP" fi test -z "$STRIP" && STRIP=: if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. set dummy ${ac_tool_prefix}ranlib; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_RANLIB+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$RANLIB"; then ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi RANLIB=$ac_cv_prog_RANLIB if test -n "$RANLIB"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 $as_echo "$RANLIB" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_RANLIB"; then ac_ct_RANLIB=$RANLIB # Extract the first word of "ranlib", so it can be a program name with args. set dummy ranlib; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_RANLIB+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_RANLIB"; then ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_RANLIB="ranlib" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB if test -n "$ac_ct_RANLIB"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 $as_echo "$ac_ct_RANLIB" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_RANLIB" = x; then RANLIB=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac RANLIB=$ac_ct_RANLIB fi else RANLIB="$ac_cv_prog_RANLIB" fi test -z "$RANLIB" && RANLIB=: # Determine commands to create old-style static archives. old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs' old_postinstall_cmds='chmod 644 $oldlib' old_postuninstall_cmds= if test -n "$RANLIB"; then case $host_os in bitrig* | openbsd*) old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib" ;; *) old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib" ;; esac old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib" fi case $host_os in darwin*) lock_old_archive_extraction=yes ;; *) lock_old_archive_extraction=no ;; esac # If no C compiler was specified, use CC. LTCC=${LTCC-"$CC"} # If no C compiler flags were specified, use CFLAGS. LTCFLAGS=${LTCFLAGS-"$CFLAGS"} # Allow CC to be a program name with arguments. compiler=$CC # Check for command to grab the raw symbol name followed by C symbol from nm. { $as_echo "$as_me:${as_lineno-$LINENO}: checking command to parse $NM output from $compiler object" >&5 $as_echo_n "checking command to parse $NM output from $compiler object... " >&6; } if ${lt_cv_sys_global_symbol_pipe+:} false; then : $as_echo_n "(cached) " >&6 else # These are sane defaults that work on at least a few old systems. # [They come from Ultrix. What could be older than Ultrix?!! ;)] # Character class describing NM global symbol codes. symcode='[BCDEGRST]' # Regexp to match symbols that can be accessed directly from C. sympat='\([_A-Za-z][_A-Za-z0-9]*\)' # Define system-specific variables. case $host_os in aix*) symcode='[BCDT]' ;; cygwin* | mingw* | pw32* | cegcc*) symcode='[ABCDGISTW]' ;; hpux*) if test ia64 = "$host_cpu"; then symcode='[ABCDEGRST]' fi ;; irix* | nonstopux*) symcode='[BCDEGRST]' ;; osf*) symcode='[BCDEGQRST]' ;; solaris*) symcode='[BDRT]' ;; sco3.2v5*) symcode='[DT]' ;; sysv4.2uw2*) symcode='[DT]' ;; sysv5* | sco5v6* | unixware* | OpenUNIX*) symcode='[ABDT]' ;; sysv4) symcode='[DFNSTU]' ;; esac # If we're using GNU nm, then use its standard symbol codes. case `$NM -V 2>&1` in *GNU* | *'with BFD'*) symcode='[ABCDGIRSTW]' ;; esac if test "$lt_cv_nm_interface" = "MS dumpbin"; then # Gets list of data symbols to import. lt_cv_sys_global_symbol_to_import="sed -n -e 's/^I .* \(.*\)$/\1/p'" # Adjust the below global symbol transforms to fixup imported variables. lt_cdecl_hook=" -e 's/^I .* \(.*\)$/extern __declspec(dllimport) char \1;/p'" lt_c_name_hook=" -e 's/^I .* \(.*\)$/ {\"\1\", (void *) 0},/p'" lt_c_name_lib_hook="\ -e 's/^I .* \(lib.*\)$/ {\"\1\", (void *) 0},/p'\ -e 's/^I .* \(.*\)$/ {\"lib\1\", (void *) 0},/p'" else # Disable hooks by default. lt_cv_sys_global_symbol_to_import= lt_cdecl_hook= lt_c_name_hook= lt_c_name_lib_hook= fi # Transform an extracted symbol line into a proper C declaration. # Some systems (esp. on ia64) link data and code symbols differently, # so use this general approach. lt_cv_sys_global_symbol_to_cdecl="sed -n"\ $lt_cdecl_hook\ " -e 's/^T .* \(.*\)$/extern int \1();/p'"\ " -e 's/^$symcode$symcode* .* \(.*\)$/extern char \1;/p'" # Transform an extracted symbol line into symbol name and symbol address lt_cv_sys_global_symbol_to_c_name_address="sed -n"\ $lt_c_name_hook\ " -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ " -e 's/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/p'" # Transform an extracted symbol line into symbol name with lib prefix and # symbol address. lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n"\ $lt_c_name_lib_hook\ " -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ " -e 's/^$symcode$symcode* .* \(lib.*\)$/ {\"\1\", (void *) \&\1},/p'"\ " -e 's/^$symcode$symcode* .* \(.*\)$/ {\"lib\1\", (void *) \&\1},/p'" # Handle CRLF in mingw tool chain opt_cr= case $build_os in mingw*) opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp ;; esac # Try without a prefix underscore, then with it. for ac_symprfx in "" "_"; do # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. symxfrm="\\1 $ac_symprfx\\2 \\2" # Write the raw and C identifiers. if test "$lt_cv_nm_interface" = "MS dumpbin"; then # Fake it for dumpbin and say T for any non-static function, # D for any global variable and I for any imported variable. # Also find C++ and __fastcall symbols from MSVC++, # which start with @ or ?. lt_cv_sys_global_symbol_pipe="$AWK '"\ " {last_section=section; section=\$ 3};"\ " /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\ " /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ " /^ *Symbol name *: /{split(\$ 0,sn,\":\"); si=substr(sn[2],2)};"\ " /^ *Type *: code/{print \"T\",si,substr(si,length(prfx))};"\ " /^ *Type *: data/{print \"I\",si,substr(si,length(prfx))};"\ " \$ 0!~/External *\|/{next};"\ " / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ " {if(hide[section]) next};"\ " {f=\"D\"}; \$ 0~/\(\).*\|/{f=\"T\"};"\ " {split(\$ 0,a,/\||\r/); split(a[2],s)};"\ " s[1]~/^[@?]/{print f,s[1],s[1]; next};"\ " s[1]~prfx {split(s[1],t,\"@\"); print f,t[1],substr(t[1],length(prfx))}"\ " ' prfx=^$ac_symprfx" else lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[ ]\($symcode$symcode*\)[ ][ ]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" fi lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'" # Check to see that the pipe works correctly. pipe_works=no rm -f conftest* cat > conftest.$ac_ext <<_LT_EOF #ifdef __cplusplus extern "C" { #endif char nm_test_var; void nm_test_func(void); void nm_test_func(void){} #ifdef __cplusplus } #endif int main(){nm_test_var='a';nm_test_func();return(0);} _LT_EOF if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then # Now try to grab the symbols. nlist=conftest.nm $ECHO "$as_me:$LINENO: $NM conftest.$ac_objext | $lt_cv_sys_global_symbol_pipe > $nlist" >&5 if eval "$NM" conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist 2>&5 && test -s "$nlist"; then # Try sorting and uniquifying the output. if sort "$nlist" | uniq > "$nlist"T; then mv -f "$nlist"T "$nlist" else rm -f "$nlist"T fi # Make sure that we snagged all the symbols we need. if $GREP ' nm_test_var$' "$nlist" >/dev/null; then if $GREP ' nm_test_func$' "$nlist" >/dev/null; then cat <<_LT_EOF > conftest.$ac_ext /* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ #if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE /* DATA imports from DLLs on WIN32 can't be const, because runtime relocations are performed -- see ld's documentation on pseudo-relocs. */ # define LT_DLSYM_CONST #elif defined __osf__ /* This system does not cope well with relocations in const data. */ # define LT_DLSYM_CONST #else # define LT_DLSYM_CONST const #endif #ifdef __cplusplus extern "C" { #endif _LT_EOF # Now generate the symbol file. eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext' cat <<_LT_EOF >> conftest.$ac_ext /* The mapping between symbol names and symbols. */ LT_DLSYM_CONST struct { const char *name; void *address; } lt__PROGRAM__LTX_preloaded_symbols[] = { { "@PROGRAM@", (void *) 0 }, _LT_EOF $SED "s/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext cat <<\_LT_EOF >> conftest.$ac_ext {0, (void *) 0} }; /* This works around a problem in FreeBSD linker */ #ifdef FREEBSD_WORKAROUND static const void *lt_preloaded_setup() { return lt__PROGRAM__LTX_preloaded_symbols; } #endif #ifdef __cplusplus } #endif _LT_EOF # Now try linking the two files. mv conftest.$ac_objext conftstm.$ac_objext lt_globsym_save_LIBS=$LIBS lt_globsym_save_CFLAGS=$CFLAGS LIBS=conftstm.$ac_objext CFLAGS="$CFLAGS$lt_prog_compiler_no_builtin_flag" if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 (eval $ac_link) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && test -s conftest$ac_exeext; then pipe_works=yes fi LIBS=$lt_globsym_save_LIBS CFLAGS=$lt_globsym_save_CFLAGS else echo "cannot find nm_test_func in $nlist" >&5 fi else echo "cannot find nm_test_var in $nlist" >&5 fi else echo "cannot run $lt_cv_sys_global_symbol_pipe" >&5 fi else echo "$progname: failed program was:" >&5 cat conftest.$ac_ext >&5 fi rm -rf conftest* conftst* # Do not use the global_symbol_pipe unless it works. if test yes = "$pipe_works"; then break else lt_cv_sys_global_symbol_pipe= fi done fi if test -z "$lt_cv_sys_global_symbol_pipe"; then lt_cv_sys_global_symbol_to_cdecl= fi if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: failed" >&5 $as_echo "failed" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5 $as_echo "ok" >&6; } fi # Response file support. if test "$lt_cv_nm_interface" = "MS dumpbin"; then nm_file_list_spec='@' elif $NM --help 2>/dev/null | grep '[@]FILE' >/dev/null; then nm_file_list_spec='@' fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sysroot" >&5 $as_echo_n "checking for sysroot... " >&6; } # Check whether --with-sysroot was given. if test "${with_sysroot+set}" = set; then : withval=$with_sysroot; else with_sysroot=no fi lt_sysroot= case $with_sysroot in #( yes) if test yes = "$GCC"; then lt_sysroot=`$CC --print-sysroot 2>/dev/null` fi ;; #( /*) lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"` ;; #( no|'') ;; #( *) { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_sysroot" >&5 $as_echo "$with_sysroot" >&6; } as_fn_error $? "The sysroot must be an absolute path." "$LINENO" 5 ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${lt_sysroot:-no}" >&5 $as_echo "${lt_sysroot:-no}" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a working dd" >&5 $as_echo_n "checking for a working dd... " >&6; } if ${ac_cv_path_lt_DD+:} false; then : $as_echo_n "(cached) " >&6 else printf 0123456789abcdef0123456789abcdef >conftest.i cat conftest.i conftest.i >conftest2.i : ${lt_DD:=$DD} if test -z "$lt_DD"; then ac_path_lt_DD_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in dd; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_lt_DD="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_lt_DD" || continue if "$ac_path_lt_DD" bs=32 count=1 conftest.out 2>/dev/null; then cmp -s conftest.i conftest.out \ && ac_cv_path_lt_DD="$ac_path_lt_DD" ac_path_lt_DD_found=: fi $ac_path_lt_DD_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_lt_DD"; then : fi else ac_cv_path_lt_DD=$lt_DD fi rm -f conftest.i conftest2.i conftest.out fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_lt_DD" >&5 $as_echo "$ac_cv_path_lt_DD" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to truncate binary pipes" >&5 $as_echo_n "checking how to truncate binary pipes... " >&6; } if ${lt_cv_truncate_bin+:} false; then : $as_echo_n "(cached) " >&6 else printf 0123456789abcdef0123456789abcdef >conftest.i cat conftest.i conftest.i >conftest2.i lt_cv_truncate_bin= if "$ac_cv_path_lt_DD" bs=32 count=1 conftest.out 2>/dev/null; then cmp -s conftest.i conftest.out \ && lt_cv_truncate_bin="$ac_cv_path_lt_DD bs=4096 count=1" fi rm -f conftest.i conftest2.i conftest.out test -z "$lt_cv_truncate_bin" && lt_cv_truncate_bin="$SED -e 4q" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_truncate_bin" >&5 $as_echo "$lt_cv_truncate_bin" >&6; } # Calculate cc_basename. Skip known compiler wrappers and cross-prefix. func_cc_basename () { for cc_temp in $*""; do case $cc_temp in compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; \-*) ;; *) break;; esac done func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` } # Check whether --enable-libtool-lock was given. if test "${enable_libtool_lock+set}" = set; then : enableval=$enable_libtool_lock; fi test no = "$enable_libtool_lock" || enable_libtool_lock=yes # Some flags need to be propagated to the compiler or linker for good # libtool support. case $host in ia64-*-hpux*) # Find out what ABI is being produced by ac_compile, and set mode # options accordingly. echo 'int i;' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then case `/usr/bin/file conftest.$ac_objext` in *ELF-32*) HPUX_IA64_MODE=32 ;; *ELF-64*) HPUX_IA64_MODE=64 ;; esac fi rm -rf conftest* ;; *-*-irix6*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. echo '#line '$LINENO' "configure"' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then if test yes = "$lt_cv_prog_gnu_ld"; then case `/usr/bin/file conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -melf32bsmip" ;; *N32*) LD="${LD-ld} -melf32bmipn32" ;; *64-bit*) LD="${LD-ld} -melf64bmip" ;; esac else case `/usr/bin/file conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -32" ;; *N32*) LD="${LD-ld} -n32" ;; *64-bit*) LD="${LD-ld} -64" ;; esac fi fi rm -rf conftest* ;; mips64*-*linux*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. echo '#line '$LINENO' "configure"' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then emul=elf case `/usr/bin/file conftest.$ac_objext` in *32-bit*) emul="${emul}32" ;; *64-bit*) emul="${emul}64" ;; esac case `/usr/bin/file conftest.$ac_objext` in *MSB*) emul="${emul}btsmip" ;; *LSB*) emul="${emul}ltsmip" ;; esac case `/usr/bin/file conftest.$ac_objext` in *N32*) emul="${emul}n32" ;; esac LD="${LD-ld} -m $emul" fi rm -rf conftest* ;; x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \ s390*-*linux*|s390*-*tpf*|sparc*-*linux*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. Note that the listed cases only cover the # situations where additional linker options are needed (such as when # doing 32-bit compilation for a host where ld defaults to 64-bit, or # vice versa); the common cases where no linker options are needed do # not appear in the list. echo 'int i;' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then case `/usr/bin/file conftest.o` in *32-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_i386_fbsd" ;; x86_64-*linux*) case `/usr/bin/file conftest.o` in *x86-64*) LD="${LD-ld} -m elf32_x86_64" ;; *) LD="${LD-ld} -m elf_i386" ;; esac ;; powerpc64le-*linux*) LD="${LD-ld} -m elf32lppclinux" ;; powerpc64-*linux*) LD="${LD-ld} -m elf32ppclinux" ;; s390x-*linux*) LD="${LD-ld} -m elf_s390" ;; sparc64-*linux*) LD="${LD-ld} -m elf32_sparc" ;; esac ;; *64-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_x86_64_fbsd" ;; x86_64-*linux*) LD="${LD-ld} -m elf_x86_64" ;; powerpcle-*linux*) LD="${LD-ld} -m elf64lppc" ;; powerpc-*linux*) LD="${LD-ld} -m elf64ppc" ;; s390*-*linux*|s390*-*tpf*) LD="${LD-ld} -m elf64_s390" ;; sparc*-*linux*) LD="${LD-ld} -m elf64_sparc" ;; esac ;; esac fi rm -rf conftest* ;; *-*-sco3.2v5*) # On SCO OpenServer 5, we need -belf to get full-featured binaries. SAVE_CFLAGS=$CFLAGS CFLAGS="$CFLAGS -belf" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler needs -belf" >&5 $as_echo_n "checking whether the C compiler needs -belf... " >&6; } if ${lt_cv_cc_needs_belf+:} false; then : $as_echo_n "(cached) " >&6 else ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : lt_cv_cc_needs_belf=yes else lt_cv_cc_needs_belf=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_cc_needs_belf" >&5 $as_echo "$lt_cv_cc_needs_belf" >&6; } if test yes != "$lt_cv_cc_needs_belf"; then # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf CFLAGS=$SAVE_CFLAGS fi ;; *-*solaris*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. echo 'int i;' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then case `/usr/bin/file conftest.o` in *64-bit*) case $lt_cv_prog_gnu_ld in yes*) case $host in i?86-*-solaris*|x86_64-*-solaris*) LD="${LD-ld} -m elf_x86_64" ;; sparc*-*-solaris*) LD="${LD-ld} -m elf64_sparc" ;; esac # GNU ld 2.21 introduced _sol2 emulations. Use them if available. if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then LD=${LD-ld}_sol2 fi ;; *) if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then LD="${LD-ld} -64" fi ;; esac ;; esac fi rm -rf conftest* ;; esac need_locks=$enable_libtool_lock if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}mt", so it can be a program name with args. set dummy ${ac_tool_prefix}mt; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_MANIFEST_TOOL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$MANIFEST_TOOL"; then ac_cv_prog_MANIFEST_TOOL="$MANIFEST_TOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_MANIFEST_TOOL="${ac_tool_prefix}mt" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi MANIFEST_TOOL=$ac_cv_prog_MANIFEST_TOOL if test -n "$MANIFEST_TOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MANIFEST_TOOL" >&5 $as_echo "$MANIFEST_TOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_MANIFEST_TOOL"; then ac_ct_MANIFEST_TOOL=$MANIFEST_TOOL # Extract the first word of "mt", so it can be a program name with args. set dummy mt; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_MANIFEST_TOOL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_MANIFEST_TOOL"; then ac_cv_prog_ac_ct_MANIFEST_TOOL="$ac_ct_MANIFEST_TOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_MANIFEST_TOOL="mt" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_MANIFEST_TOOL=$ac_cv_prog_ac_ct_MANIFEST_TOOL if test -n "$ac_ct_MANIFEST_TOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_MANIFEST_TOOL" >&5 $as_echo "$ac_ct_MANIFEST_TOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_MANIFEST_TOOL" = x; then MANIFEST_TOOL=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac MANIFEST_TOOL=$ac_ct_MANIFEST_TOOL fi else MANIFEST_TOOL="$ac_cv_prog_MANIFEST_TOOL" fi test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $MANIFEST_TOOL is a manifest tool" >&5 $as_echo_n "checking if $MANIFEST_TOOL is a manifest tool... " >&6; } if ${lt_cv_path_mainfest_tool+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_path_mainfest_tool=no echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&5 $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out cat conftest.err >&5 if $GREP 'Manifest Tool' conftest.out > /dev/null; then lt_cv_path_mainfest_tool=yes fi rm -f conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_mainfest_tool" >&5 $as_echo "$lt_cv_path_mainfest_tool" >&6; } if test yes != "$lt_cv_path_mainfest_tool"; then MANIFEST_TOOL=: fi case $host_os in rhapsody* | darwin*) if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}dsymutil", so it can be a program name with args. set dummy ${ac_tool_prefix}dsymutil; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_DSYMUTIL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$DSYMUTIL"; then ac_cv_prog_DSYMUTIL="$DSYMUTIL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_DSYMUTIL="${ac_tool_prefix}dsymutil" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi DSYMUTIL=$ac_cv_prog_DSYMUTIL if test -n "$DSYMUTIL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DSYMUTIL" >&5 $as_echo "$DSYMUTIL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_DSYMUTIL"; then ac_ct_DSYMUTIL=$DSYMUTIL # Extract the first word of "dsymutil", so it can be a program name with args. set dummy dsymutil; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_DSYMUTIL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_DSYMUTIL"; then ac_cv_prog_ac_ct_DSYMUTIL="$ac_ct_DSYMUTIL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_DSYMUTIL="dsymutil" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_DSYMUTIL=$ac_cv_prog_ac_ct_DSYMUTIL if test -n "$ac_ct_DSYMUTIL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DSYMUTIL" >&5 $as_echo "$ac_ct_DSYMUTIL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_DSYMUTIL" = x; then DSYMUTIL=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac DSYMUTIL=$ac_ct_DSYMUTIL fi else DSYMUTIL="$ac_cv_prog_DSYMUTIL" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}nmedit", so it can be a program name with args. set dummy ${ac_tool_prefix}nmedit; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_NMEDIT+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$NMEDIT"; then ac_cv_prog_NMEDIT="$NMEDIT" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_NMEDIT="${ac_tool_prefix}nmedit" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi NMEDIT=$ac_cv_prog_NMEDIT if test -n "$NMEDIT"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $NMEDIT" >&5 $as_echo "$NMEDIT" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_NMEDIT"; then ac_ct_NMEDIT=$NMEDIT # Extract the first word of "nmedit", so it can be a program name with args. set dummy nmedit; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_NMEDIT+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_NMEDIT"; then ac_cv_prog_ac_ct_NMEDIT="$ac_ct_NMEDIT" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_NMEDIT="nmedit" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_NMEDIT=$ac_cv_prog_ac_ct_NMEDIT if test -n "$ac_ct_NMEDIT"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_NMEDIT" >&5 $as_echo "$ac_ct_NMEDIT" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_NMEDIT" = x; then NMEDIT=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac NMEDIT=$ac_ct_NMEDIT fi else NMEDIT="$ac_cv_prog_NMEDIT" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}lipo", so it can be a program name with args. set dummy ${ac_tool_prefix}lipo; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_LIPO+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$LIPO"; then ac_cv_prog_LIPO="$LIPO" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_LIPO="${ac_tool_prefix}lipo" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi LIPO=$ac_cv_prog_LIPO if test -n "$LIPO"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIPO" >&5 $as_echo "$LIPO" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_LIPO"; then ac_ct_LIPO=$LIPO # Extract the first word of "lipo", so it can be a program name with args. set dummy lipo; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_LIPO+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_LIPO"; then ac_cv_prog_ac_ct_LIPO="$ac_ct_LIPO" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_LIPO="lipo" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_LIPO=$ac_cv_prog_ac_ct_LIPO if test -n "$ac_ct_LIPO"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_LIPO" >&5 $as_echo "$ac_ct_LIPO" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_LIPO" = x; then LIPO=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac LIPO=$ac_ct_LIPO fi else LIPO="$ac_cv_prog_LIPO" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}otool", so it can be a program name with args. set dummy ${ac_tool_prefix}otool; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_OTOOL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$OTOOL"; then ac_cv_prog_OTOOL="$OTOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_OTOOL="${ac_tool_prefix}otool" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi OTOOL=$ac_cv_prog_OTOOL if test -n "$OTOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL" >&5 $as_echo "$OTOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_OTOOL"; then ac_ct_OTOOL=$OTOOL # Extract the first word of "otool", so it can be a program name with args. set dummy otool; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_OTOOL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_OTOOL"; then ac_cv_prog_ac_ct_OTOOL="$ac_ct_OTOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_OTOOL="otool" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_OTOOL=$ac_cv_prog_ac_ct_OTOOL if test -n "$ac_ct_OTOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL" >&5 $as_echo "$ac_ct_OTOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_OTOOL" = x; then OTOOL=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac OTOOL=$ac_ct_OTOOL fi else OTOOL="$ac_cv_prog_OTOOL" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}otool64", so it can be a program name with args. set dummy ${ac_tool_prefix}otool64; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_OTOOL64+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$OTOOL64"; then ac_cv_prog_OTOOL64="$OTOOL64" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_OTOOL64="${ac_tool_prefix}otool64" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi OTOOL64=$ac_cv_prog_OTOOL64 if test -n "$OTOOL64"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL64" >&5 $as_echo "$OTOOL64" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_OTOOL64"; then ac_ct_OTOOL64=$OTOOL64 # Extract the first word of "otool64", so it can be a program name with args. set dummy otool64; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_OTOOL64+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_OTOOL64"; then ac_cv_prog_ac_ct_OTOOL64="$ac_ct_OTOOL64" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_OTOOL64="otool64" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_OTOOL64=$ac_cv_prog_ac_ct_OTOOL64 if test -n "$ac_ct_OTOOL64"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL64" >&5 $as_echo "$ac_ct_OTOOL64" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_OTOOL64" = x; then OTOOL64=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac OTOOL64=$ac_ct_OTOOL64 fi else OTOOL64="$ac_cv_prog_OTOOL64" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -single_module linker flag" >&5 $as_echo_n "checking for -single_module linker flag... " >&6; } if ${lt_cv_apple_cc_single_mod+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_apple_cc_single_mod=no if test -z "$LT_MULTI_MODULE"; then # By default we will add the -single_module flag. You can override # by either setting the environment variable LT_MULTI_MODULE # non-empty at configure time, or by adding -multi_module to the # link flags. rm -rf libconftest.dylib* echo "int foo(void){return 1;}" > conftest.c echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c" >&5 $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c 2>conftest.err _lt_result=$? # If there is a non-empty error log, and "single_module" # appears in it, assume the flag caused a linker warning if test -s conftest.err && $GREP single_module conftest.err; then cat conftest.err >&5 # Otherwise, if the output was created with a 0 exit code from # the compiler, it worked. elif test -f libconftest.dylib && test 0 = "$_lt_result"; then lt_cv_apple_cc_single_mod=yes else cat conftest.err >&5 fi rm -rf libconftest.dylib* rm -f conftest.* fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_apple_cc_single_mod" >&5 $as_echo "$lt_cv_apple_cc_single_mod" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -exported_symbols_list linker flag" >&5 $as_echo_n "checking for -exported_symbols_list linker flag... " >&6; } if ${lt_cv_ld_exported_symbols_list+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_ld_exported_symbols_list=no save_LDFLAGS=$LDFLAGS echo "_main" > conftest.sym LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : lt_cv_ld_exported_symbols_list=yes else lt_cv_ld_exported_symbols_list=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LDFLAGS=$save_LDFLAGS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_exported_symbols_list" >&5 $as_echo "$lt_cv_ld_exported_symbols_list" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -force_load linker flag" >&5 $as_echo_n "checking for -force_load linker flag... " >&6; } if ${lt_cv_ld_force_load+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_ld_force_load=no cat > conftest.c << _LT_EOF int forced_loaded() { return 2;} _LT_EOF echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&5 $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&5 echo "$AR cr libconftest.a conftest.o" >&5 $AR cr libconftest.a conftest.o 2>&5 echo "$RANLIB libconftest.a" >&5 $RANLIB libconftest.a 2>&5 cat > conftest.c << _LT_EOF int main() { return 0;} _LT_EOF echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&5 $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err _lt_result=$? if test -s conftest.err && $GREP force_load conftest.err; then cat conftest.err >&5 elif test -f conftest && test 0 = "$_lt_result" && $GREP forced_load conftest >/dev/null 2>&1; then lt_cv_ld_force_load=yes else cat conftest.err >&5 fi rm -f conftest.err libconftest.a conftest conftest.c rm -rf conftest.dSYM fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_force_load" >&5 $as_echo "$lt_cv_ld_force_load" >&6; } case $host_os in rhapsody* | darwin1.[012]) _lt_dar_allow_undefined='$wl-undefined ${wl}suppress' ;; darwin1.*) _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; darwin*) # darwin 5.x on # if running on 10.5 or later, the deployment target defaults # to the OS version, if on x86, and 10.4, the deployment # target defaults to 10.4. Don't you love it? case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in 10.0,*86*-darwin8*|10.0,*-darwin[912]*) _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; 10.[012][,.]*) _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; 10.*|11.*) _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; esac ;; esac if test yes = "$lt_cv_apple_cc_single_mod"; then _lt_dar_single_mod='$single_module' fi if test yes = "$lt_cv_ld_exported_symbols_list"; then _lt_dar_export_syms=' $wl-exported_symbols_list,$output_objdir/$libname-symbols.expsym' else _lt_dar_export_syms='~$NMEDIT -s $output_objdir/$libname-symbols.expsym $lib' fi if test : != "$DSYMUTIL" && test no = "$lt_cv_ld_force_load"; then _lt_dsymutil='~$DSYMUTIL $lib || :' else _lt_dsymutil= fi ;; esac # func_munge_path_list VARIABLE PATH # ----------------------------------- # VARIABLE is name of variable containing _space_ separated list of # directories to be munged by the contents of PATH, which is string # having a format: # "DIR[:DIR]:" # string "DIR[ DIR]" will be prepended to VARIABLE # ":DIR[:DIR]" # string "DIR[ DIR]" will be appended to VARIABLE # "DIRP[:DIRP]::[DIRA:]DIRA" # string "DIRP[ DIRP]" will be prepended to VARIABLE and string # "DIRA[ DIRA]" will be appended to VARIABLE # "DIR[:DIR]" # VARIABLE will be replaced by "DIR[ DIR]" func_munge_path_list () { case x$2 in x) ;; *:) eval $1=\"`$ECHO $2 | $SED 's/:/ /g'` \$$1\" ;; x:*) eval $1=\"\$$1 `$ECHO $2 | $SED 's/:/ /g'`\" ;; *::*) eval $1=\"\$$1\ `$ECHO $2 | $SED -e 's/.*:://' -e 's/:/ /g'`\" eval $1=\"`$ECHO $2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \$$1\" ;; *) eval $1=\"`$ECHO $2 | $SED 's/:/ /g'`\" ;; esac } ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 $as_echo_n "checking how to run the C preprocessor... " >&6; } # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= fi if test -z "$CPP"; then if ${ac_cv_prog_CPP+:} false; then : $as_echo_n "(cached) " >&6 else # Double quotes because CPP needs to be expanded for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" do ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : break fi done ac_cv_prog_CPP=$CPP fi CPP=$ac_cv_prog_CPP else ac_cv_prog_CPP=$CPP fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 $as_echo "$CPP" >&6; } ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "C preprocessor \"$CPP\" fails sanity check See \`config.log' for more details" "$LINENO" 5; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 $as_echo_n "checking for ANSI C header files... " >&6; } if ${ac_cv_header_stdc+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_header_stdc=yes else ac_cv_header_stdc=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "memchr" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "free" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. if test "$cross_compiling" = yes; then : : else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #if ((' ' & 0x0FF) == 0x020) # define ISLOWER(c) ('a' <= (c) && (c) <= 'z') # define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) #else # define ISLOWER(c) \ (('a' <= (c) && (c) <= 'i') \ || ('j' <= (c) && (c) <= 'r') \ || ('s' <= (c) && (c) <= 'z')) # define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) #endif #define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) int main () { int i; for (i = 0; i < 256; i++) if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) return 2; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : else ac_cv_header_stdc=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 $as_echo "$ac_cv_header_stdc" >&6; } if test $ac_cv_header_stdc = yes; then $as_echo "#define STDC_HEADERS 1" >>confdefs.h fi # On IRIX 5.3, sys/types and inttypes.h are conflicting. for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ inttypes.h stdint.h unistd.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default " if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done for ac_header in dlfcn.h do : ac_fn_c_check_header_compile "$LINENO" "dlfcn.h" "ac_cv_header_dlfcn_h" "$ac_includes_default " if test "x$ac_cv_header_dlfcn_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_DLFCN_H 1 _ACEOF fi done # Set options enable_dlopen=no enable_win32_dll=no # Check whether --enable-shared was given. if test "${enable_shared+set}" = set; then : enableval=$enable_shared; p=${PACKAGE-default} case $enableval in yes) enable_shared=yes ;; no) enable_shared=no ;; *) enable_shared=no # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for pkg in $enableval; do IFS=$lt_save_ifs if test "X$pkg" = "X$p"; then enable_shared=yes fi done IFS=$lt_save_ifs ;; esac else enable_shared=yes fi # Check whether --enable-static was given. if test "${enable_static+set}" = set; then : enableval=$enable_static; p=${PACKAGE-default} case $enableval in yes) enable_static=yes ;; no) enable_static=no ;; *) enable_static=no # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for pkg in $enableval; do IFS=$lt_save_ifs if test "X$pkg" = "X$p"; then enable_static=yes fi done IFS=$lt_save_ifs ;; esac else enable_static=yes fi # Check whether --with-pic was given. if test "${with_pic+set}" = set; then : withval=$with_pic; lt_p=${PACKAGE-default} case $withval in yes|no) pic_mode=$withval ;; *) pic_mode=default # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for lt_pkg in $withval; do IFS=$lt_save_ifs if test "X$lt_pkg" = "X$lt_p"; then pic_mode=yes fi done IFS=$lt_save_ifs ;; esac else pic_mode=default fi # Check whether --enable-fast-install was given. if test "${enable_fast_install+set}" = set; then : enableval=$enable_fast_install; p=${PACKAGE-default} case $enableval in yes) enable_fast_install=yes ;; no) enable_fast_install=no ;; *) enable_fast_install=no # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for pkg in $enableval; do IFS=$lt_save_ifs if test "X$pkg" = "X$p"; then enable_fast_install=yes fi done IFS=$lt_save_ifs ;; esac else enable_fast_install=yes fi shared_archive_member_spec= case $host,$enable_shared in power*-*-aix[5-9]*,yes) { $as_echo "$as_me:${as_lineno-$LINENO}: checking which variant of shared library versioning to provide" >&5 $as_echo_n "checking which variant of shared library versioning to provide... " >&6; } # Check whether --with-aix-soname was given. if test "${with_aix_soname+set}" = set; then : withval=$with_aix_soname; case $withval in aix|svr4|both) ;; *) as_fn_error $? "Unknown argument to --with-aix-soname" "$LINENO" 5 ;; esac lt_cv_with_aix_soname=$with_aix_soname else if ${lt_cv_with_aix_soname+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_with_aix_soname=aix fi with_aix_soname=$lt_cv_with_aix_soname fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_aix_soname" >&5 $as_echo "$with_aix_soname" >&6; } if test aix != "$with_aix_soname"; then # For the AIX way of multilib, we name the shared archive member # based on the bitwidth used, traditionally 'shr.o' or 'shr_64.o', # and 'shr.imp' or 'shr_64.imp', respectively, for the Import File. # Even when GNU compilers ignore OBJECT_MODE but need '-maix64' flag, # the AIX toolchain works better with OBJECT_MODE set (default 32). if test 64 = "${OBJECT_MODE-32}"; then shared_archive_member_spec=shr_64 else shared_archive_member_spec=shr fi fi ;; *) with_aix_soname=aix ;; esac # This can be used to rebuild libtool when needed LIBTOOL_DEPS=$ltmain # Always use our own libtool. LIBTOOL='$(SHELL) $(top_builddir)/libtool' test -z "$LN_S" && LN_S="ln -s" if test -n "${ZSH_VERSION+set}"; then setopt NO_GLOB_SUBST fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for objdir" >&5 $as_echo_n "checking for objdir... " >&6; } if ${lt_cv_objdir+:} false; then : $as_echo_n "(cached) " >&6 else rm -f .libs 2>/dev/null mkdir .libs 2>/dev/null if test -d .libs; then lt_cv_objdir=.libs else # MS-DOS does not allow filenames that begin with a dot. lt_cv_objdir=_libs fi rmdir .libs 2>/dev/null fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_objdir" >&5 $as_echo "$lt_cv_objdir" >&6; } objdir=$lt_cv_objdir cat >>confdefs.h <<_ACEOF #define LT_OBJDIR "$lt_cv_objdir/" _ACEOF case $host_os in aix3*) # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. if test set != "${COLLECT_NAMES+set}"; then COLLECT_NAMES= export COLLECT_NAMES fi ;; esac # Global variables: ofile=libtool can_build_shared=yes # All known linkers require a '.a' archive for static linking (except MSVC, # which needs '.lib'). libext=a with_gnu_ld=$lt_cv_prog_gnu_ld old_CC=$CC old_CFLAGS=$CFLAGS # Set sane defaults for various variables test -z "$CC" && CC=cc test -z "$LTCC" && LTCC=$CC test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS test -z "$LD" && LD=ld test -z "$ac_objext" && ac_objext=o func_cc_basename $compiler cc_basename=$func_cc_basename_result # Only perform the check for file, if the check method requires it test -z "$MAGIC_CMD" && MAGIC_CMD=file case $deplibs_check_method in file_magic*) if test "$file_magic_cmd" = '$MAGIC_CMD'; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${ac_tool_prefix}file" >&5 $as_echo_n "checking for ${ac_tool_prefix}file... " >&6; } if ${lt_cv_path_MAGIC_CMD+:} false; then : $as_echo_n "(cached) " >&6 else case $MAGIC_CMD in [\\/*] | ?:[\\/]*) lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path. ;; *) lt_save_MAGIC_CMD=$MAGIC_CMD lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" for ac_dir in $ac_dummy; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/${ac_tool_prefix}file"; then lt_cv_path_MAGIC_CMD=$ac_dir/"${ac_tool_prefix}file" if test -n "$file_magic_test_file"; then case $deplibs_check_method in "file_magic "*) file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` MAGIC_CMD=$lt_cv_path_MAGIC_CMD if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | $EGREP "$file_magic_regex" > /dev/null; then : else cat <<_LT_EOF 1>&2 *** Warning: the command libtool uses to detect shared libraries, *** $file_magic_cmd, produces output that libtool cannot recognize. *** The result is that libtool may fail to recognize shared libraries *** as such. This will affect the creation of libtool libraries that *** depend on shared libraries, but programs linked with such libtool *** libraries will work regardless of this problem. Nevertheless, you *** may want to report the problem to your system manager and/or to *** bug-libtool@gnu.org _LT_EOF fi ;; esac fi break fi done IFS=$lt_save_ifs MAGIC_CMD=$lt_save_MAGIC_CMD ;; esac fi MAGIC_CMD=$lt_cv_path_MAGIC_CMD if test -n "$MAGIC_CMD"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5 $as_echo "$MAGIC_CMD" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test -z "$lt_cv_path_MAGIC_CMD"; then if test -n "$ac_tool_prefix"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for file" >&5 $as_echo_n "checking for file... " >&6; } if ${lt_cv_path_MAGIC_CMD+:} false; then : $as_echo_n "(cached) " >&6 else case $MAGIC_CMD in [\\/*] | ?:[\\/]*) lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path. ;; *) lt_save_MAGIC_CMD=$MAGIC_CMD lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" for ac_dir in $ac_dummy; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/file"; then lt_cv_path_MAGIC_CMD=$ac_dir/"file" if test -n "$file_magic_test_file"; then case $deplibs_check_method in "file_magic "*) file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` MAGIC_CMD=$lt_cv_path_MAGIC_CMD if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | $EGREP "$file_magic_regex" > /dev/null; then : else cat <<_LT_EOF 1>&2 *** Warning: the command libtool uses to detect shared libraries, *** $file_magic_cmd, produces output that libtool cannot recognize. *** The result is that libtool may fail to recognize shared libraries *** as such. This will affect the creation of libtool libraries that *** depend on shared libraries, but programs linked with such libtool *** libraries will work regardless of this problem. Nevertheless, you *** may want to report the problem to your system manager and/or to *** bug-libtool@gnu.org _LT_EOF fi ;; esac fi break fi done IFS=$lt_save_ifs MAGIC_CMD=$lt_save_MAGIC_CMD ;; esac fi MAGIC_CMD=$lt_cv_path_MAGIC_CMD if test -n "$MAGIC_CMD"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5 $as_echo "$MAGIC_CMD" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi else MAGIC_CMD=: fi fi fi ;; esac # Use C for the default configuration in the libtool script lt_save_CC=$CC ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu # Source file extension for C test sources. ac_ext=c # Object file extension for compiled C test sources. objext=o objext=$objext # Code to be used in simple compile tests lt_simple_compile_test_code="int some_variable = 0;" # Code to be used in simple link tests lt_simple_link_test_code='int main(){return(0);}' # If no C compiler was specified, use CC. LTCC=${LTCC-"$CC"} # If no C compiler flags were specified, use CFLAGS. LTCFLAGS=${LTCFLAGS-"$CFLAGS"} # Allow CC to be a program name with arguments. compiler=$CC # Save the default compiler, since it gets overwritten when the other # tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP. compiler_DEFAULT=$CC # save warnings/boilerplate of simple test code ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" >conftest.$ac_ext eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_compiler_boilerplate=`cat conftest.err` $RM conftest* ac_outfile=conftest.$ac_objext echo "$lt_simple_link_test_code" >conftest.$ac_ext eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_linker_boilerplate=`cat conftest.err` $RM -r conftest* ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... if test -n "$compiler"; then lt_prog_compiler_no_builtin_flag= if test yes = "$GCC"; then case $cc_basename in nvcc*) lt_prog_compiler_no_builtin_flag=' -Xcompiler -fno-builtin' ;; *) lt_prog_compiler_no_builtin_flag=' -fno-builtin' ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -fno-rtti -fno-exceptions" >&5 $as_echo_n "checking if $compiler supports -fno-rtti -fno-exceptions... " >&6; } if ${lt_cv_prog_compiler_rtti_exceptions+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_rtti_exceptions=no ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-fno-rtti -fno-exceptions" ## exclude from sc_useless_quotes_in_assignment # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. # The option is referenced via a variable to avoid confusing sed. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler_rtti_exceptions=yes fi fi $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_rtti_exceptions" >&5 $as_echo "$lt_cv_prog_compiler_rtti_exceptions" >&6; } if test yes = "$lt_cv_prog_compiler_rtti_exceptions"; then lt_prog_compiler_no_builtin_flag="$lt_prog_compiler_no_builtin_flag -fno-rtti -fno-exceptions" else : fi fi lt_prog_compiler_wl= lt_prog_compiler_pic= lt_prog_compiler_static= if test yes = "$GCC"; then lt_prog_compiler_wl='-Wl,' lt_prog_compiler_static='-static' case $host_os in aix*) # All AIX code is PIC. if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor lt_prog_compiler_static='-Bstatic' fi lt_prog_compiler_pic='-fPIC' ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support lt_prog_compiler_pic='-fPIC' ;; m68k) # FIXME: we need at least 68020 code to build shared libraries, but # adding the '-m68020' flag to GCC prevents building anything better, # like '-m68040'. lt_prog_compiler_pic='-m68020 -resident32 -malways-restore-a4' ;; esac ;; beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) # PIC is the default for these OSes. ;; mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). # Although the cygwin gcc ignores -fPIC, still need this for old-style # (--disable-auto-import) libraries lt_prog_compiler_pic='-DDLL_EXPORT' case $host_os in os2*) lt_prog_compiler_static='$wl-static' ;; esac ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files lt_prog_compiler_pic='-fno-common' ;; haiku*) # PIC is the default for Haiku. # The "-static" flag exists, but is broken. lt_prog_compiler_static= ;; hpux*) # PIC is the default for 64-bit PA HP-UX, but not for 32-bit # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag # sets the default TLS model and affects inlining. case $host_cpu in hppa*64*) # +Z the default ;; *) lt_prog_compiler_pic='-fPIC' ;; esac ;; interix[3-9]*) # Interix 3.x gcc -fpic/-fPIC options generate broken code. # Instead, we relocate shared libraries at runtime. ;; msdosdjgpp*) # Just because we use GCC doesn't mean we suddenly get shared libraries # on systems that don't support them. lt_prog_compiler_can_build_shared=no enable_shared=no ;; *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. lt_prog_compiler_pic='-fPIC -shared' ;; sysv4*MP*) if test -d /usr/nec; then lt_prog_compiler_pic=-Kconform_pic fi ;; *) lt_prog_compiler_pic='-fPIC' ;; esac case $cc_basename in nvcc*) # Cuda Compiler Driver 2.2 lt_prog_compiler_wl='-Xlinker ' if test -n "$lt_prog_compiler_pic"; then lt_prog_compiler_pic="-Xcompiler $lt_prog_compiler_pic" fi ;; esac else # PORTME Check for flag to pass linker flags through the system compiler. case $host_os in aix*) lt_prog_compiler_wl='-Wl,' if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor lt_prog_compiler_static='-Bstatic' else lt_prog_compiler_static='-bnso -bI:/lib/syscalls.exp' fi ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files lt_prog_compiler_pic='-fno-common' case $cc_basename in nagfor*) # NAG Fortran compiler lt_prog_compiler_wl='-Wl,-Wl,,' lt_prog_compiler_pic='-PIC' lt_prog_compiler_static='-Bstatic' ;; esac ;; mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). lt_prog_compiler_pic='-DDLL_EXPORT' case $host_os in os2*) lt_prog_compiler_static='$wl-static' ;; esac ;; hpux9* | hpux10* | hpux11*) lt_prog_compiler_wl='-Wl,' # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but # not for PA HP-UX. case $host_cpu in hppa*64*|ia64*) # +Z the default ;; *) lt_prog_compiler_pic='+Z' ;; esac # Is there a better lt_prog_compiler_static that works with the bundled CC? lt_prog_compiler_static='$wl-a ${wl}archive' ;; irix5* | irix6* | nonstopux*) lt_prog_compiler_wl='-Wl,' # PIC (with -KPIC) is the default. lt_prog_compiler_static='-non_shared' ;; linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in # old Intel for x86_64, which still supported -KPIC. ecc*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-static' ;; # flang / f18. f95 an alias for gfortran or flang on Debian flang* | f18* | f95*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fPIC' lt_prog_compiler_static='-static' ;; # icc used to be incompatible with GCC. # ICC 10 doesn't accept -KPIC any more. icc* | ifort*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fPIC' lt_prog_compiler_static='-static' ;; # Lahey Fortran 8.1. lf95*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='--shared' lt_prog_compiler_static='--static' ;; nagfor*) # NAG Fortran compiler lt_prog_compiler_wl='-Wl,-Wl,,' lt_prog_compiler_pic='-PIC' lt_prog_compiler_static='-Bstatic' ;; tcc*) # Fabrice Bellard et al's Tiny C Compiler lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fPIC' lt_prog_compiler_static='-static' ;; pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*) # Portland Group compilers (*not* the Pentium gcc compiler, # which looks to be a dead project) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fpic' lt_prog_compiler_static='-Bstatic' ;; ccc*) lt_prog_compiler_wl='-Wl,' # All Alpha code is PIC. lt_prog_compiler_static='-non_shared' ;; xl* | bgxl* | bgf* | mpixl*) # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-qpic' lt_prog_compiler_static='-qstaticlink' ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [1-7].* | *Sun*Fortran*\ 8.[0-3]*) # Sun Fortran 8.3 passes all unrecognized flags to the linker lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' lt_prog_compiler_wl='' ;; *Sun\ F* | *Sun*Fortran*) lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' lt_prog_compiler_wl='-Qoption ld ' ;; *Sun\ C*) # Sun C 5.9 lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' lt_prog_compiler_wl='-Wl,' ;; *Intel*\ [CF]*Compiler*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fPIC' lt_prog_compiler_static='-static' ;; *Portland\ Group*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fpic' lt_prog_compiler_static='-Bstatic' ;; esac ;; esac ;; newsos6) lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' ;; *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. lt_prog_compiler_pic='-fPIC -shared' ;; osf3* | osf4* | osf5*) lt_prog_compiler_wl='-Wl,' # All OSF/1 code is PIC. lt_prog_compiler_static='-non_shared' ;; rdos*) lt_prog_compiler_static='-non_shared' ;; solaris*) lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' case $cc_basename in f77* | f90* | f95* | sunf77* | sunf90* | sunf95*) lt_prog_compiler_wl='-Qoption ld ';; *) lt_prog_compiler_wl='-Wl,';; esac ;; sunos4*) lt_prog_compiler_wl='-Qoption ld ' lt_prog_compiler_pic='-PIC' lt_prog_compiler_static='-Bstatic' ;; sysv4 | sysv4.2uw2* | sysv4.3*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' ;; sysv4*MP*) if test -d /usr/nec; then lt_prog_compiler_pic='-Kconform_pic' lt_prog_compiler_static='-Bstatic' fi ;; sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' ;; unicos*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_can_build_shared=no ;; uts4*) lt_prog_compiler_pic='-pic' lt_prog_compiler_static='-Bstatic' ;; *) lt_prog_compiler_can_build_shared=no ;; esac fi case $host_os in # For platforms that do not support PIC, -DPIC is meaningless: *djgpp*) lt_prog_compiler_pic= ;; *) lt_prog_compiler_pic="$lt_prog_compiler_pic -DPIC" ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5 $as_echo_n "checking for $compiler option to produce PIC... " >&6; } if ${lt_cv_prog_compiler_pic+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_pic=$lt_prog_compiler_pic fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic" >&5 $as_echo "$lt_cv_prog_compiler_pic" >&6; } lt_prog_compiler_pic=$lt_cv_prog_compiler_pic # # Check to make sure the PIC flag actually works. # if test -n "$lt_prog_compiler_pic"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic works" >&5 $as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic works... " >&6; } if ${lt_cv_prog_compiler_pic_works+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_pic_works=no ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="$lt_prog_compiler_pic -DPIC" ## exclude from sc_useless_quotes_in_assignment # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. # The option is referenced via a variable to avoid confusing sed. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler_pic_works=yes fi fi $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works" >&5 $as_echo "$lt_cv_prog_compiler_pic_works" >&6; } if test yes = "$lt_cv_prog_compiler_pic_works"; then case $lt_prog_compiler_pic in "" | " "*) ;; *) lt_prog_compiler_pic=" $lt_prog_compiler_pic" ;; esac else lt_prog_compiler_pic= lt_prog_compiler_can_build_shared=no fi fi # # Check to make sure the static flag actually works. # wl=$lt_prog_compiler_wl eval lt_tmp_static_flag=\"$lt_prog_compiler_static\" { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5 $as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; } if ${lt_cv_prog_compiler_static_works+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_static_works=no save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS $lt_tmp_static_flag" echo "$lt_simple_link_test_code" > conftest.$ac_ext if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then # The linker can only warn and ignore the option if not recognized # So say no if there are warnings if test -s conftest.err; then # Append any errors to the config.log. cat conftest.err 1>&5 $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler_static_works=yes fi else lt_cv_prog_compiler_static_works=yes fi fi $RM -r conftest* LDFLAGS=$save_LDFLAGS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works" >&5 $as_echo "$lt_cv_prog_compiler_static_works" >&6; } if test yes = "$lt_cv_prog_compiler_static_works"; then : else lt_prog_compiler_static= fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 $as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } if ${lt_cv_prog_compiler_c_o+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_c_o=no $RM -r conftest 2>/dev/null mkdir conftest cd conftest mkdir out echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-o out/conftest2.$ac_objext" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then lt_cv_prog_compiler_c_o=yes fi fi chmod u+w . 2>&5 $RM conftest* # SGI C++ compiler will create directory out/ii_files/ for # template instantiation test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files $RM out/* && rmdir out cd .. $RM -r conftest $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5 $as_echo "$lt_cv_prog_compiler_c_o" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 $as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } if ${lt_cv_prog_compiler_c_o+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_c_o=no $RM -r conftest 2>/dev/null mkdir conftest cd conftest mkdir out echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-o out/conftest2.$ac_objext" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then lt_cv_prog_compiler_c_o=yes fi fi chmod u+w . 2>&5 $RM conftest* # SGI C++ compiler will create directory out/ii_files/ for # template instantiation test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files $RM out/* && rmdir out cd .. $RM -r conftest $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5 $as_echo "$lt_cv_prog_compiler_c_o" >&6; } hard_links=nottested if test no = "$lt_cv_prog_compiler_c_o" && test no != "$need_locks"; then # do not overwrite the value of need_locks provided by the user { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5 $as_echo_n "checking if we can lock with hard links... " >&6; } hard_links=yes $RM conftest* ln conftest.a conftest.b 2>/dev/null && hard_links=no touch conftest.a ln conftest.a conftest.b 2>&5 || hard_links=no ln conftest.a conftest.b 2>/dev/null && hard_links=no { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5 $as_echo "$hard_links" >&6; } if test no = "$hard_links"; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&5 $as_echo "$as_me: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&2;} need_locks=warn fi else need_locks=no fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 $as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } runpath_var= allow_undefined_flag= always_export_symbols=no archive_cmds= archive_expsym_cmds= compiler_needs_object=no enable_shared_with_static_runtimes=no export_dynamic_flag_spec= export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' hardcode_automatic=no hardcode_direct=no hardcode_direct_absolute=no hardcode_libdir_flag_spec= hardcode_libdir_separator= hardcode_minus_L=no hardcode_shlibpath_var=unsupported inherit_rpath=no link_all_deplibs=unknown module_cmds= module_expsym_cmds= old_archive_from_new_cmds= old_archive_from_expsyms_cmds= thread_safe_flag_spec= whole_archive_flag_spec= # include_expsyms should be a list of space-separated symbols to be *always* # included in the symbol list include_expsyms= # exclude_expsyms can be an extended regexp of symbols to exclude # it will be wrapped by ' (' and ')$', so one must not match beginning or # end of line. Example: 'a|bc|.*d.*' will exclude the symbols 'a' and 'bc', # as well as any symbol that contains 'd'. exclude_expsyms='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*' # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out # platforms (ab)use it in PIC code, but their linkers get confused if # the symbol is explicitly referenced. Since portable code cannot # rely on this symbol name, it's probably fine to never include it in # preloaded symbol tables. # Exclude shared library initialization/finalization symbols. extract_expsyms_cmds= case $host_os in cygwin* | mingw* | pw32* | cegcc*) # FIXME: the MSVC++ port hasn't been tested in a loooong time # When not using gcc, we currently assume that we are using # Microsoft Visual C++. if test yes != "$GCC"; then with_gnu_ld=no fi ;; interix*) # we just hope/assume this is gcc and not c89 (= MSVC++) with_gnu_ld=yes ;; openbsd* | bitrig*) with_gnu_ld=no ;; linux* | k*bsd*-gnu | gnu*) link_all_deplibs=no ;; esac ld_shlibs=yes # On some targets, GNU ld is compatible enough with the native linker # that we're better off using the native interface for both. lt_use_gnu_ld_interface=no if test yes = "$with_gnu_ld"; then case $host_os in aix*) # The AIX port of GNU ld has always aspired to compatibility # with the native linker. However, as the warning in the GNU ld # block says, versions before 2.19.5* couldn't really create working # shared libraries, regardless of the interface used. case `$LD -v 2>&1` in *\ \(GNU\ Binutils\)\ 2.19.5*) ;; *\ \(GNU\ Binutils\)\ 2.[2-9]*) ;; *\ \(GNU\ Binutils\)\ [3-9]*) ;; *) lt_use_gnu_ld_interface=yes ;; esac ;; *) lt_use_gnu_ld_interface=yes ;; esac fi if test yes = "$lt_use_gnu_ld_interface"; then # If archive_cmds runs LD, not CC, wlarc should be empty wlarc='$wl' # Set some defaults for GNU ld with shared library support. These # are reset later if shared libraries are not supported. Putting them # here allows them to be overridden if necessary. runpath_var=LD_RUN_PATH hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' export_dynamic_flag_spec='$wl--export-dynamic' # ancient GNU ld didn't support --whole-archive et. al. if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then whole_archive_flag_spec=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' else whole_archive_flag_spec= fi supports_anon_versioning=no case `$LD -v | $SED -e 's/(^)\+)\s\+//' 2>&1` in *GNU\ gold*) supports_anon_versioning=yes ;; *\ [01].* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11 *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... *\ 2.11.*) ;; # other 2.11 versions *) supports_anon_versioning=yes ;; esac # See if GNU ld supports shared libraries. case $host_os in aix[3-9]*) # On AIX/PPC, the GNU linker is very broken if test ia64 != "$host_cpu"; then ld_shlibs=no cat <<_LT_EOF 1>&2 *** Warning: the GNU linker, at least up to release 2.19, is reported *** to be unable to reliably create shared libraries on AIX. *** Therefore, libtool is disabling shared libraries support. If you *** really care for shared libraries, you may want to install binutils *** 2.20 or above, or modify your PATH so that a non-GNU linker is found. *** You will then need to restart the configuration process. _LT_EOF fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds='' ;; m68k) archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes ;; esac ;; beos*) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then allow_undefined_flag=unsupported # Joseph Beckenbach says some releases of gcc # support --undefined. This deserves some investigation. FIXME archive_cmds='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' else ld_shlibs=no fi ;; cygwin* | mingw* | pw32* | cegcc*) # _LT_TAGVAR(hardcode_libdir_flag_spec, ) is actually meaningless, # as there is no search path for DLLs. hardcode_libdir_flag_spec='-L$libdir' export_dynamic_flag_spec='$wl--export-all-symbols' allow_undefined_flag=unsupported always_export_symbols=no enable_shared_with_static_runtimes=yes export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols' exclude_expsyms='[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname' if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' # If the export-symbols file already is a .def file, use it as # is; otherwise, prepend EXPORTS... archive_expsym_cmds='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then cp $export_symbols $output_objdir/$soname.def; else echo EXPORTS > $output_objdir/$soname.def; cat $export_symbols >> $output_objdir/$soname.def; fi~ $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' else ld_shlibs=no fi ;; haiku*) archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' link_all_deplibs=yes ;; os2*) hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes allow_undefined_flag=unsupported shrext_cmds=.dll archive_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' archive_expsym_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ prefix_cmds="$SED"~ if test EXPORTS = "`$SED 1q $export_symbols`"; then prefix_cmds="$prefix_cmds -e 1d"; fi~ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' old_archive_From_new_cmds='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' enable_shared_with_static_runtimes=yes ;; interix[3-9]*) hardcode_direct=no hardcode_shlibpath_var=no hardcode_libdir_flag_spec='$wl-rpath,$libdir' export_dynamic_flag_spec='$wl-E' # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. # Instead, shared libraries are loaded at an image base (0x10000000 by # default) and relocated if they conflict, which is a slow very memory # consuming and fragmenting process. To avoid this, we pick a random, # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link # time. Moving up from 0x10000000 also allows more sbrk(2) space. archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' archive_expsym_cmds='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ;; gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) tmp_diet=no if test linux-dietlibc = "$host_os"; then case $cc_basename in diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn) esac fi if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \ && test no = "$tmp_diet" then tmp_addflag=' $pic_flag' tmp_sharedflag='-shared' case $cc_basename,$host_cpu in pgcc*) # Portland Group C compiler whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' tmp_addflag=' $pic_flag' ;; pgf77* | pgf90* | pgf95* | pgfortran*) # Portland Group f77 and f90 compilers whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' tmp_addflag=' $pic_flag -Mnomain' ;; ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 tmp_addflag=' -i_dynamic' ;; efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 tmp_addflag=' -i_dynamic -nofor_main' ;; ifc* | ifort*) # Intel Fortran compiler tmp_addflag=' -nofor_main' ;; lf95*) # Lahey Fortran 8.1 whole_archive_flag_spec= tmp_sharedflag='--shared' ;; nagfor*) # NAGFOR 5.3 tmp_sharedflag='-Wl,-shared' ;; xl[cC]* | bgxl[cC]* | mpixl[cC]*) # IBM XL C 8.0 on PPC (deal with xlf below) tmp_sharedflag='-qmkshrobj' tmp_addflag= ;; nvcc*) # Cuda Compiler Driver 2.2 whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' compiler_needs_object=yes ;; esac case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C 5.9 whole_archive_flag_spec='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' compiler_needs_object=yes tmp_sharedflag='-G' ;; *Sun\ F*) # Sun Fortran 8.3 tmp_sharedflag='-G' ;; esac archive_cmds='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' if test yes = "$supports_anon_versioning"; then archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib' fi case $cc_basename in tcc*) export_dynamic_flag_spec='-rdynamic' ;; xlf* | bgf* | bgxlf* | mpixlf*) # IBM XL Fortran 10.1 on PPC cannot create shared libs itself whole_archive_flag_spec='--whole-archive$convenience --no-whole-archive' hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' archive_cmds='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib' if test yes = "$supports_anon_versioning"; then archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' fi ;; esac else ld_shlibs=no fi ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' wlarc= else archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' fi ;; solaris*) if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then ld_shlibs=no cat <<_LT_EOF 1>&2 *** Warning: The releases 2.8.* of the GNU linker cannot reliably *** create shared libraries on Solaris systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.9.1 or newer. Another option is to modify *** your PATH or compiler configuration so that the native linker is *** used, and then restart. _LT_EOF elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' else ld_shlibs=no fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) case `$LD -v 2>&1` in *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*) ld_shlibs=no cat <<_LT_EOF 1>&2 *** Warning: Releases of the GNU linker prior to 2.16.91.0.3 cannot *** reliably create shared libraries on SCO systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.16.91.0.3 or newer. Another option is to modify *** your PATH or compiler configuration so that the native linker is *** used, and then restart. _LT_EOF ;; *) # For security reasons, it is highly recommended that you always # use absolute paths for naming shared libraries, and exclude the # DT_RUNPATH tag from executables and libraries. But doing so # requires that you compile everything twice, which is a pain. if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' else ld_shlibs=no fi ;; esac ;; sunos4*) archive_cmds='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' wlarc= hardcode_direct=yes hardcode_shlibpath_var=no ;; *) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' else ld_shlibs=no fi ;; esac if test no = "$ld_shlibs"; then runpath_var= hardcode_libdir_flag_spec= export_dynamic_flag_spec= whole_archive_flag_spec= fi else # PORTME fill in a description of your system's linker (not GNU ld) case $host_os in aix3*) allow_undefined_flag=unsupported always_export_symbols=yes archive_expsym_cmds='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' # Note: this linker hardcodes the directories in LIBPATH if there # are no directories specified by -L. hardcode_minus_L=yes if test yes = "$GCC" && test -z "$lt_prog_compiler_static"; then # Neither direct hardcoding nor static linking is supported with a # broken collect2. hardcode_direct=unsupported fi ;; aix[4-9]*) if test ia64 = "$host_cpu"; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no exp_sym_flag='-Bexport' no_entry_flag= else # If we're using GNU nm, then we don't want the "-C" option. # -C means demangle to GNU nm, but means don't demangle to AIX nm. # Without the "-l" option, or with the "-B" option, AIX nm treats # weak defined symbols like other global defined symbols, whereas # GNU nm marks them as "W". # While the 'weak' keyword is ignored in the Export File, we need # it in the Import File for the 'aix-soname' feature, so we have # to replace the "-B" option with "-P" for AIX nm. if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then export_symbols_cmds='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' else export_symbols_cmds='`func_echo_all $NM | $SED -e '\''s/B\([^B]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && (substr(\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' fi aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # have runtime linking enabled, and use it for executables. # For shared libraries, we enable/disable runtime linking # depending on the kind of the shared library created - # when "with_aix_soname,aix_use_runtimelinking" is: # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables # "aix,yes" lib.so shared, rtl:yes, for executables # lib.a static archive # "both,no" lib.so.V(shr.o) shared, rtl:yes # lib.a(lib.so.V) shared, rtl:no, for executables # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a(lib.so.V) shared, rtl:no # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a static archive case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) for ld_flag in $LDFLAGS; do if (test x-brtl = "x$ld_flag" || test x-Wl,-brtl = "x$ld_flag"); then aix_use_runtimelinking=yes break fi done if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then # With aix-soname=svr4, we create the lib.so.V shared archives only, # so we don't have lib.a shared libs to link our executables. # We have to force runtime linking in this case. aix_use_runtimelinking=yes LDFLAGS="$LDFLAGS -Wl,-brtl" fi ;; esac exp_sym_flag='-bexport' no_entry_flag='-bnoentry' fi # When large executables or shared objects are built, AIX ld can # have problems creating the table of contents. If linking a library # or program results in "error TOC overflow" add -mminimal-toc to # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. archive_cmds='' hardcode_direct=yes hardcode_direct_absolute=yes hardcode_libdir_separator=':' link_all_deplibs=yes file_list_spec='$wl-f,' case $with_aix_soname,$aix_use_runtimelinking in aix,*) ;; # traditional, no import file svr4,* | *,yes) # use import file # The Import File defines what to hardcode. hardcode_direct=no hardcode_direct_absolute=no ;; esac if test yes = "$GCC"; then case $host_os in aix4.[012]|aix4.[012].*) # We only want to do this on AIX 4.2 and lower, the check # below for broken collect2 doesn't work under 4.3+ collect2name=`$CC -print-prog-name=collect2` if test -f "$collect2name" && strings "$collect2name" | $GREP resolve_lib_name >/dev/null then # We have reworked collect2 : else # We have old collect2 hardcode_direct=unsupported # It fails to find uninstalled libraries when the uninstalled # path is not listed in the libpath. Setting hardcode_minus_L # to unsupported forces relinking hardcode_minus_L=yes hardcode_libdir_flag_spec='-L$libdir' hardcode_libdir_separator= fi ;; esac shared_flag='-shared' if test yes = "$aix_use_runtimelinking"; then shared_flag="$shared_flag "'$wl-G' fi # Need to ensure runtime linking is disabled for the traditional # shared library, or the linker may eventually find shared libraries # /with/ Import File - we do not want to mix them. shared_flag_aix='-shared' shared_flag_svr4='-shared $wl-G' else # not using gcc if test ia64 = "$host_cpu"; then # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release # chokes on -Wl,-G. The following line is correct: shared_flag='-G' else if test yes = "$aix_use_runtimelinking"; then shared_flag='$wl-G' else shared_flag='$wl-bM:SRE' fi shared_flag_aix='$wl-bM:SRE' shared_flag_svr4='$wl-G' fi fi export_dynamic_flag_spec='$wl-bexpall' # It seems that -bexpall does not export symbols beginning with # underscore (_), so it is better to generate a list of symbols to export. always_export_symbols=yes if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then # Warning - without using the other runtime loading flags (-brtl), # -berok will link without error, but may produce a broken library. allow_undefined_flag='-berok' # Determine the default libpath from the value encoded in an # empty executable. if test set = "${lt_cv_aix_libpath+set}"; then aix_libpath=$lt_cv_aix_libpath else if ${lt_cv_aix_libpath_+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : lt_aix_libpath_sed=' /Import File Strings/,/^$/ { /^0/ { s/^0 *\([^ ]*\) *$/\1/ p } }' lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` # Check for a 64-bit object if we didn't find anything. if test -z "$lt_cv_aix_libpath_"; then lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test -z "$lt_cv_aix_libpath_"; then lt_cv_aix_libpath_=/usr/lib:/lib fi fi aix_libpath=$lt_cv_aix_libpath_ fi hardcode_libdir_flag_spec='$wl-blibpath:$libdir:'"$aix_libpath" archive_expsym_cmds='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag else if test ia64 = "$host_cpu"; then hardcode_libdir_flag_spec='$wl-R $libdir:/usr/lib:/lib' allow_undefined_flag="-z nodefs" archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols" else # Determine the default libpath from the value encoded in an # empty executable. if test set = "${lt_cv_aix_libpath+set}"; then aix_libpath=$lt_cv_aix_libpath else if ${lt_cv_aix_libpath_+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : lt_aix_libpath_sed=' /Import File Strings/,/^$/ { /^0/ { s/^0 *\([^ ]*\) *$/\1/ p } }' lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` # Check for a 64-bit object if we didn't find anything. if test -z "$lt_cv_aix_libpath_"; then lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test -z "$lt_cv_aix_libpath_"; then lt_cv_aix_libpath_=/usr/lib:/lib fi fi aix_libpath=$lt_cv_aix_libpath_ fi hardcode_libdir_flag_spec='$wl-blibpath:$libdir:'"$aix_libpath" # Warning - without using the other run time loading flags, # -berok will link without error, but may produce a broken library. no_undefined_flag=' $wl-bernotok' allow_undefined_flag=' $wl-berok' if test yes = "$with_gnu_ld"; then # We only use this code for GNU lds that support --whole-archive. whole_archive_flag_spec='$wl--whole-archive$convenience $wl--no-whole-archive' else # Exported symbols can be pulled into shared objects from archives whole_archive_flag_spec='$convenience' fi archive_cmds_need_lc=yes archive_expsym_cmds='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d' # -brtl affects multiple linker settings, -berok does not and is overridden later compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([, ]\\)%-berok\\1%g"`' if test svr4 != "$with_aix_soname"; then # This is similar to how AIX traditionally builds its shared libraries. archive_expsym_cmds="$archive_expsym_cmds"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname' fi if test aix != "$with_aix_soname"; then archive_expsym_cmds="$archive_expsym_cmds"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp' else # used by -dlpreopen to get the symbols archive_expsym_cmds="$archive_expsym_cmds"'~$MV $output_objdir/$realname.d/$soname $output_objdir' fi archive_expsym_cmds="$archive_expsym_cmds"'~$RM -r $output_objdir/$realname.d' fi fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds='' ;; m68k) archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes ;; esac ;; bsdi[45]*) export_dynamic_flag_spec=-rdynamic ;; cygwin* | mingw* | pw32* | cegcc*) # When not using gcc, we currently assume that we are using # Microsoft Visual C++. # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. case $cc_basename in cl*) # Native MSVC hardcode_libdir_flag_spec=' ' allow_undefined_flag=unsupported always_export_symbols=yes file_list_spec='@' # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=.dll # FIXME: Setting linknames here is a bad hack. archive_cmds='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames=' archive_expsym_cmds='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then cp "$export_symbols" "$output_objdir/$soname.def"; echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp"; else $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp; fi~ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ linknames=' # The linker will not automatically build a static lib if we build a DLL. # _LT_TAGVAR(old_archive_from_new_cmds, )='true' enable_shared_with_static_runtimes=yes exclude_expsyms='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1,DATA/'\'' | $SED -e '\''/^[AITW][ ]/s/.*[ ]//'\'' | sort | uniq > $export_symbols' # Don't use ranlib old_postinstall_cmds='chmod 644 $oldlib' postlink_cmds='lt_outputfile="@OUTPUT@"~ lt_tool_outputfile="@TOOL_OUTPUT@"~ case $lt_outputfile in *.exe|*.EXE) ;; *) lt_outputfile=$lt_outputfile.exe lt_tool_outputfile=$lt_tool_outputfile.exe ;; esac~ if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; $RM "$lt_outputfile.manifest"; fi' ;; *) # Assume MSVC wrapper hardcode_libdir_flag_spec=' ' allow_undefined_flag=unsupported # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=.dll # FIXME: Setting linknames here is a bad hack. archive_cmds='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames=' # The linker will automatically build a .lib file if we build a DLL. old_archive_from_new_cmds='true' # FIXME: Should let the user specify the lib program. old_archive_cmds='lib -OUT:$oldlib$oldobjs$old_deplibs' enable_shared_with_static_runtimes=yes ;; esac ;; darwin* | rhapsody*) archive_cmds_need_lc=no hardcode_direct=no hardcode_automatic=yes hardcode_shlibpath_var=unsupported if test yes = "$lt_cv_ld_force_load"; then whole_archive_flag_spec='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience $wl-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' else whole_archive_flag_spec='' fi link_all_deplibs=yes allow_undefined_flag=$_lt_dar_allow_undefined case $cc_basename in ifort*|nagfor*) _lt_dar_can_shared=yes ;; *) _lt_dar_can_shared=$GCC ;; esac if test yes = "$_lt_dar_can_shared"; then output_verbose_link_cmd=func_echo_all archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dsymutil" module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dsymutil" archive_expsym_cmds="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil" module_expsym_cmds="sed -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil" else ld_shlibs=no fi ;; dgux*) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_libdir_flag_spec='-L$libdir' hardcode_shlibpath_var=no ;; # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor # support. Future versions do this automatically, but an explicit c++rt0.o # does not break anything, and helps significantly (at the cost of a little # extra space). freebsd2.2*) archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes hardcode_shlibpath_var=no ;; # Unfortunately, older versions of FreeBSD 2 do not have this feature. freebsd2.*) archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' hardcode_direct=yes hardcode_minus_L=yes hardcode_shlibpath_var=no ;; # FreeBSD 3 and greater uses gcc -shared to do shared libraries. freebsd* | dragonfly*) archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes hardcode_shlibpath_var=no ;; hpux9*) if test yes = "$GCC"; then archive_cmds='$RM $output_objdir/$soname~$CC -shared $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' else archive_cmds='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' fi hardcode_libdir_flag_spec='$wl+b $wl$libdir' hardcode_libdir_separator=: hardcode_direct=yes # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes export_dynamic_flag_spec='$wl-E' ;; hpux10*) if test yes,no = "$GCC,$with_gnu_ld"; then archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' fi if test no = "$with_gnu_ld"; then hardcode_libdir_flag_spec='$wl+b $wl$libdir' hardcode_libdir_separator=: hardcode_direct=yes hardcode_direct_absolute=yes export_dynamic_flag_spec='$wl-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes fi ;; hpux11*) if test yes,no = "$GCC,$with_gnu_ld"; then case $host_cpu in hppa*64*) archive_cmds='$CC -shared $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' ;; esac else case $host_cpu in hppa*64*) archive_cmds='$CC -b $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) archive_cmds='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) # Older versions of the 11.00 compiler do not understand -b yet # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does) { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC understands -b" >&5 $as_echo_n "checking if $CC understands -b... " >&6; } if ${lt_cv_prog_compiler__b+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler__b=no save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS -b" echo "$lt_simple_link_test_code" > conftest.$ac_ext if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then # The linker can only warn and ignore the option if not recognized # So say no if there are warnings if test -s conftest.err; then # Append any errors to the config.log. cat conftest.err 1>&5 $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler__b=yes fi else lt_cv_prog_compiler__b=yes fi fi $RM -r conftest* LDFLAGS=$save_LDFLAGS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler__b" >&5 $as_echo "$lt_cv_prog_compiler__b" >&6; } if test yes = "$lt_cv_prog_compiler__b"; then archive_cmds='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' fi ;; esac fi if test no = "$with_gnu_ld"; then hardcode_libdir_flag_spec='$wl+b $wl$libdir' hardcode_libdir_separator=: case $host_cpu in hppa*64*|ia64*) hardcode_direct=no hardcode_shlibpath_var=no ;; *) hardcode_direct=yes hardcode_direct_absolute=yes export_dynamic_flag_spec='$wl-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes ;; esac fi ;; irix5* | irix6* | nonstopux*) if test yes = "$GCC"; then archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' # Try to use the -exported_symbol ld option, if it does not # work, assume that -exports_file does not work either and # implicitly export all symbols. # This should be the same for all languages, so no per-tag cache variable. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $host_os linker accepts -exported_symbol" >&5 $as_echo_n "checking whether the $host_os linker accepts -exported_symbol... " >&6; } if ${lt_cv_irix_exported_symbol+:} false; then : $as_echo_n "(cached) " >&6 else save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS -shared $wl-exported_symbol ${wl}foo $wl-update_registry $wl/dev/null" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int foo (void) { return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : lt_cv_irix_exported_symbol=yes else lt_cv_irix_exported_symbol=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LDFLAGS=$save_LDFLAGS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_irix_exported_symbol" >&5 $as_echo "$lt_cv_irix_exported_symbol" >&6; } if test yes = "$lt_cv_irix_exported_symbol"; then archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations $wl-exports_file $wl$export_symbols -o $lib' fi link_all_deplibs=no else archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -exports_file $export_symbols -o $lib' fi archive_cmds_need_lc='no' hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' hardcode_libdir_separator=: inherit_rpath=yes link_all_deplibs=yes ;; linux*) case $cc_basename in tcc*) # Fabrice Bellard et al's Tiny C Compiler ld_shlibs=yes archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out else archive_cmds='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF fi hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes hardcode_shlibpath_var=no ;; newsos6) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_direct=yes hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' hardcode_libdir_separator=: hardcode_shlibpath_var=no ;; *nto* | *qnx*) ;; openbsd* | bitrig*) if test -f /usr/libexec/ld.so; then hardcode_direct=yes hardcode_shlibpath_var=no hardcode_direct_absolute=yes if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags $wl-retain-symbols-file,$export_symbols' hardcode_libdir_flag_spec='$wl-rpath,$libdir' export_dynamic_flag_spec='$wl-E' else archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' hardcode_libdir_flag_spec='$wl-rpath,$libdir' fi else ld_shlibs=no fi ;; os2*) hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes allow_undefined_flag=unsupported shrext_cmds=.dll archive_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' archive_expsym_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ prefix_cmds="$SED"~ if test EXPORTS = "`$SED 1q $export_symbols`"; then prefix_cmds="$prefix_cmds -e 1d"; fi~ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' old_archive_From_new_cmds='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' enable_shared_with_static_runtimes=yes ;; osf3*) if test yes = "$GCC"; then allow_undefined_flag=' $wl-expect_unresolved $wl\*' archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' else allow_undefined_flag=' -expect_unresolved \*' archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' fi archive_cmds_need_lc='no' hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' hardcode_libdir_separator=: ;; osf4* | osf5*) # as osf3* with the addition of -msym flag if test yes = "$GCC"; then allow_undefined_flag=' $wl-expect_unresolved $wl\*' archive_cmds='$CC -shared$allow_undefined_flag $pic_flag $libobjs $deplibs $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' else allow_undefined_flag=' -expect_unresolved \*' archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' archive_expsym_cmds='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~ $CC -shared$allow_undefined_flag $wl-input $wl$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~$RM $lib.exp' # Both c and cxx compiler support -rpath directly hardcode_libdir_flag_spec='-rpath $libdir' fi archive_cmds_need_lc='no' hardcode_libdir_separator=: ;; solaris*) no_undefined_flag=' -z defs' if test yes = "$GCC"; then wlarc='$wl' archive_cmds='$CC -shared $pic_flag $wl-z ${wl}text $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -shared $pic_flag $wl-z ${wl}text $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' else case `$CC -V 2>&1` in *"Compilers 5.0"*) wlarc='' archive_cmds='$LD -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $linker_flags' archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $LD -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' ;; *) wlarc='$wl' archive_cmds='$CC -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' ;; esac fi hardcode_libdir_flag_spec='-R$libdir' hardcode_shlibpath_var=no case $host_os in solaris2.[0-5] | solaris2.[0-5].*) ;; *) # The compiler driver will combine and reorder linker options, # but understands '-z linker_flag'. GCC discards it without '$wl', # but is careful enough not to reorder. # Supported since Solaris 2.6 (maybe 2.5.1?) if test yes = "$GCC"; then whole_archive_flag_spec='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract' else whole_archive_flag_spec='-z allextract$convenience -z defaultextract' fi ;; esac link_all_deplibs=yes ;; sunos4*) if test sequent = "$host_vendor"; then # Use $CC to link under sequent, because it throws in some extra .o # files that make .init and .fini sections work. archive_cmds='$CC -G $wl-h $soname -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' fi hardcode_libdir_flag_spec='-L$libdir' hardcode_direct=yes hardcode_minus_L=yes hardcode_shlibpath_var=no ;; sysv4) case $host_vendor in sni) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_direct=yes # is this really true??? ;; siemens) ## LD is ld it makes a PLAMLIB ## CC just makes a GrossModule. archive_cmds='$LD -G -o $lib $libobjs $deplibs $linker_flags' reload_cmds='$CC -r -o $output$reload_objs' hardcode_direct=no ;; motorola) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_direct=no #Motorola manual says yes, but my tests say they lie ;; esac runpath_var='LD_RUN_PATH' hardcode_shlibpath_var=no ;; sysv4.3*) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_shlibpath_var=no export_dynamic_flag_spec='-Bexport' ;; sysv4*MP*) if test -d /usr/nec; then archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_shlibpath_var=no runpath_var=LD_RUN_PATH hardcode_runpath_var=yes ld_shlibs=yes fi ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) no_undefined_flag='$wl-z,text' archive_cmds_need_lc=no hardcode_shlibpath_var=no runpath_var='LD_RUN_PATH' if test yes = "$GCC"; then archive_cmds='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; sysv5* | sco3.2v5* | sco5v6*) # Note: We CANNOT use -z defs as we might desire, because we do not # link with -lc, and that would cause any symbols used from libc to # always be unresolved, which means just about no library would # ever link correctly. If we're not using GNU ld we use -z text # though, which does catch some bad symbols but isn't as heavy-handed # as -z defs. no_undefined_flag='$wl-z,text' allow_undefined_flag='$wl-z,nodefs' archive_cmds_need_lc=no hardcode_shlibpath_var=no hardcode_libdir_flag_spec='$wl-R,$libdir' hardcode_libdir_separator=':' link_all_deplibs=yes export_dynamic_flag_spec='$wl-Bexport' runpath_var='LD_RUN_PATH' if test yes = "$GCC"; then archive_cmds='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; uts4*) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_libdir_flag_spec='-L$libdir' hardcode_shlibpath_var=no ;; *) ld_shlibs=no ;; esac if test sni = "$host_vendor"; then case $host in sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) export_dynamic_flag_spec='$wl-Blargedynsym' ;; esac fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs" >&5 $as_echo "$ld_shlibs" >&6; } test no = "$ld_shlibs" && can_build_shared=no with_gnu_ld=$with_gnu_ld # # Do we need to explicitly link libc? # case "x$archive_cmds_need_lc" in x|xyes) # Assume -lc should be added archive_cmds_need_lc=yes if test yes,yes = "$GCC,$enable_shared"; then case $archive_cmds in *'~'*) # FIXME: we may have to deal with multi-command sequences. ;; '$CC '*) # Test whether the compiler implicitly links with -lc since on some # systems, -lgcc has to come before -lc. If gcc already passes -lc # to ld, don't add -lc before -lgcc. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5 $as_echo_n "checking whether -lc should be explicitly linked in... " >&6; } if ${lt_cv_archive_cmds_need_lc+:} false; then : $as_echo_n "(cached) " >&6 else $RM conftest* echo "$lt_simple_compile_test_code" > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } 2>conftest.err; then soname=conftest lib=conftest libobjs=conftest.$ac_objext deplibs= wl=$lt_prog_compiler_wl pic_flag=$lt_prog_compiler_pic compiler_flags=-v linker_flags=-v verstring= output_objdir=. libname=conftest lt_save_allow_undefined_flag=$allow_undefined_flag allow_undefined_flag= if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5 (eval $archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then lt_cv_archive_cmds_need_lc=no else lt_cv_archive_cmds_need_lc=yes fi allow_undefined_flag=$lt_save_allow_undefined_flag else cat conftest.err 1>&5 fi $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc" >&5 $as_echo "$lt_cv_archive_cmds_need_lc" >&6; } archive_cmds_need_lc=$lt_cv_archive_cmds_need_lc ;; esac fi ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5 $as_echo_n "checking dynamic linker characteristics... " >&6; } if test yes = "$GCC"; then case $host_os in darwin*) lt_awk_arg='/^libraries:/,/LR/' ;; *) lt_awk_arg='/^libraries:/' ;; esac case $host_os in mingw* | cegcc*) lt_sed_strip_eq='s|=\([A-Za-z]:\)|\1|g' ;; *) lt_sed_strip_eq='s|=/|/|g' ;; esac lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq` case $lt_search_path_spec in *\;*) # if the path contains ";" then we assume it to be the separator # otherwise default to the standard path separator (i.e. ":") - it is # assumed that no part of a normal pathname contains ";" but that should # okay in the real world where ";" in dirpaths is itself problematic. lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'` ;; *) lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"` ;; esac # Ok, now we have the path, separated by spaces, we can step through it # and add multilib dir if necessary... lt_tmp_lt_search_path_spec= lt_multi_os_dir=/`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` # ...but if some path component already ends with the multilib dir we assume # that all is fine and trust -print-search-dirs as is (GCC 4.2? or newer). case "$lt_multi_os_dir; $lt_search_path_spec " in "/; "* | "/.; "* | "/./; "* | *"$lt_multi_os_dir "* | *"$lt_multi_os_dir/ "*) lt_multi_os_dir= ;; esac for lt_sys_path in $lt_search_path_spec; do if test -d "$lt_sys_path$lt_multi_os_dir"; then lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path$lt_multi_os_dir" elif test -n "$lt_multi_os_dir"; then test -d "$lt_sys_path" && \ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path" fi done lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk ' BEGIN {RS = " "; FS = "/|\n";} { lt_foo = ""; lt_count = 0; for (lt_i = NF; lt_i > 0; lt_i--) { if ($lt_i != "" && $lt_i != ".") { if ($lt_i == "..") { lt_count++; } else { if (lt_count == 0) { lt_foo = "/" $lt_i lt_foo; } else { lt_count--; } } } } if (lt_foo != "") { lt_freq[lt_foo]++; } if (lt_freq[lt_foo] == 1) { print lt_foo; } }'` # AWK program above erroneously prepends '/' to C:/dos/paths # for these hosts. case $host_os in mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\ $SED 's|/\([A-Za-z]:\)|\1|g'` ;; esac sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP` else sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" fi library_names_spec= libname_spec='lib$name' soname_spec= shrext_cmds=.so postinstall_cmds= postuninstall_cmds= finish_cmds= finish_eval= shlibpath_var= shlibpath_overrides_runpath=unknown version_type=none dynamic_linker="$host_os ld.so" sys_lib_dlsearch_path_spec="/lib /usr/lib" need_lib_prefix=unknown hardcode_into_libs=no # when you set need_version to no, make sure it does not cause -set_version # flags to be left without arguments need_version=unknown case $host_os in aix3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname.a' shlibpath_var=LIBPATH # AIX 3 has no versioning support, so we append a major version to the name. soname_spec='$libname$release$shared_ext$major' ;; aix[4-9]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no hardcode_into_libs=yes if test ia64 = "$host_cpu"; then # AIX 5 supports IA64 library_names_spec='$libname$release$shared_ext$major $libname$release$shared_ext$versuffix $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH else # With GCC up to 2.95.x, collect2 would create an import file # for dependence libraries. The import file would start with # the line '#! .'. This would cause the generated library to # depend on '.', always an invalid library. This was fixed in # development snapshots of GCC prior to 3.0. case $host_os in aix4 | aix4.[01] | aix4.[01].*) if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' echo ' yes ' echo '#endif'; } | $CC -E - | $GREP yes > /dev/null; then : else can_build_shared=no fi ;; esac # Using Import Files as archive members, it is possible to support # filename-based versioning of shared library archives on AIX. While # this would work for both with and without runtime linking, it will # prevent static linking of such archives. So we do filename-based # shared library versioning with .so extension only, which is used # when both runtime linking and shared linking is enabled. # Unfortunately, runtime linking may impact performance, so we do # not want this to be the default eventually. Also, we use the # versioned .so libs for executables only if there is the -brtl # linker flag in LDFLAGS as well, or --with-aix-soname=svr4 only. # To allow for filename-based versioning support, we need to create # libNAME.so.V as an archive file, containing: # *) an Import File, referring to the versioned filename of the # archive as well as the shared archive member, telling the # bitwidth (32 or 64) of that shared object, and providing the # list of exported symbols of that shared object, eventually # decorated with the 'weak' keyword # *) the shared object with the F_LOADONLY flag set, to really avoid # it being seen by the linker. # At run time we better use the real file rather than another symlink, # but for link time we create the symlink libNAME.so -> libNAME.so.V case $with_aix_soname,$aix_use_runtimelinking in # AIX (on Power*) has no versioning support, so currently we cannot hardcode correct # soname into executable. Probably we can add versioning support to # collect2, so additional links can be useful in future. aix,yes) # traditional libtool dynamic_linker='AIX unversionable lib.so' # If using run time linking (on AIX 4.2 or later) use lib.so # instead of lib.a to let people know that these are not # typical AIX shared libraries. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' ;; aix,no) # traditional AIX only dynamic_linker='AIX lib.a(lib.so.V)' # We preserve .a as extension for shared libraries through AIX4.2 # and later when we are not doing run time linking. library_names_spec='$libname$release.a $libname.a' soname_spec='$libname$release$shared_ext$major' ;; svr4,*) # full svr4 only dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o)" library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' # We do not specify a path in Import Files, so LIBPATH fires. shlibpath_overrides_runpath=yes ;; *,yes) # both, prefer svr4 dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o), lib.a(lib.so.V)" library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' # unpreferred sharedlib libNAME.a needs extra handling postinstall_cmds='test -n "$linkname" || linkname="$realname"~func_stripname "" ".so" "$linkname"~$install_shared_prog "$dir/$func_stripname_result.$libext" "$destdir/$func_stripname_result.$libext"~test -z "$tstripme" || test -z "$striplib" || $striplib "$destdir/$func_stripname_result.$libext"' postuninstall_cmds='for n in $library_names $old_library; do :; done~func_stripname "" ".so" "$n"~test "$func_stripname_result" = "$n" || func_append rmfiles " $odir/$func_stripname_result.$libext"' # We do not specify a path in Import Files, so LIBPATH fires. shlibpath_overrides_runpath=yes ;; *,no) # both, prefer aix dynamic_linker="AIX lib.a(lib.so.V), lib.so.V($shared_archive_member_spec.o)" library_names_spec='$libname$release.a $libname.a' soname_spec='$libname$release$shared_ext$major' # unpreferred sharedlib libNAME.so.V and symlink libNAME.so need extra handling postinstall_cmds='test -z "$dlname" || $install_shared_prog $dir/$dlname $destdir/$dlname~test -z "$tstripme" || test -z "$striplib" || $striplib $destdir/$dlname~test -n "$linkname" || linkname=$realname~func_stripname "" ".a" "$linkname"~(cd "$destdir" && $LN_S -f $dlname $func_stripname_result.so)' postuninstall_cmds='test -z "$dlname" || func_append rmfiles " $odir/$dlname"~for n in $old_library $library_names; do :; done~func_stripname "" ".a" "$n"~func_append rmfiles " $odir/$func_stripname_result.so"' ;; esac shlibpath_var=LIBPATH fi ;; amigaos*) case $host_cpu in powerpc) # Since July 2007 AmigaOS4 officially supports .so libraries. # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' ;; m68k) library_names_spec='$libname.ixlibrary $libname.a' # Create ${libname}_ixlibrary.a entries in /sys/libs. finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' ;; esac ;; beos*) library_names_spec='$libname$shared_ext' dynamic_linker="$host_os ld.so" shlibpath_var=LIBRARY_PATH ;; bsdi[45]*) version_type=linux # correct to gnu/linux during the next big refactor need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" # the default ld.so.conf also contains /usr/contrib/lib and # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow # libtool to hard-code these into programs ;; cygwin* | mingw* | pw32* | cegcc*) version_type=windows shrext_cmds=.dll need_version=no need_lib_prefix=no case $GCC,$cc_basename in yes,*) # gcc library_names_spec='$libname.dll.a' # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes case $host_os in cygwin*) # Cygwin DLLs use 'cyg' prefix rather than 'lib' soname_spec='`echo $libname | sed -e 's/^lib/cyg/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api" ;; mingw* | cegcc*) # MinGW DLLs use traditional 'lib' prefix soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' ;; pw32*) # pw32 DLLs use 'pw' prefix rather than 'lib' library_names_spec='`echo $libname | sed -e 's/^lib/pw/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' ;; esac dynamic_linker='Win32 ld.exe' ;; *,cl*) # Native MSVC libname_spec='$name' soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' library_names_spec='$libname.dll.lib' case $build_os in mingw*) sys_lib_search_path_spec= lt_save_ifs=$IFS IFS=';' for lt_path in $LIB do IFS=$lt_save_ifs # Let DOS variable expansion print the short 8.3 style file name. lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" done IFS=$lt_save_ifs # Convert to MSYS style. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([a-zA-Z]\\):| /\\1|g' -e 's|^ ||'` ;; cygwin*) # Convert to unix form, then to dos form, then back to unix form # but this time dos style (no spaces!) so that the unix form looks # like /cygdrive/c/PROGRA~1:/cygdr... sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` ;; *) sys_lib_search_path_spec=$LIB if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then # It is most probably a Windows format PATH. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` else sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` fi # FIXME: find the short name or the path components, as spaces are # common. (e.g. "Program Files" -> "PROGRA~1") ;; esac # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes dynamic_linker='Win32 link.exe' ;; *) # Assume MSVC wrapper library_names_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext $libname.lib' dynamic_linker='Win32 ld.exe' ;; esac # FIXME: first we should search . and the directory the executable is in shlibpath_var=PATH ;; darwin* | rhapsody*) dynamic_linker="$host_os dyld" version_type=darwin need_lib_prefix=no need_version=no library_names_spec='$libname$release$major$shared_ext $libname$shared_ext' soname_spec='$libname$release$major$shared_ext' shlibpath_overrides_runpath=yes shlibpath_var=DYLD_LIBRARY_PATH shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib" sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' ;; dgux*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH ;; freebsd* | dragonfly*) # DragonFly does not have aout. When/if they implement a new # versioning mechanism, adjust this. if test -x /usr/bin/objformat; then objformat=`/usr/bin/objformat` else case $host_os in freebsd[23].*) objformat=aout ;; *) objformat=elf ;; esac fi version_type=freebsd-$objformat case $version_type in freebsd-elf*) library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' need_version=no need_lib_prefix=no ;; freebsd-*) library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' need_version=yes ;; esac shlibpath_var=LD_LIBRARY_PATH case $host_os in freebsd2.*) shlibpath_overrides_runpath=yes ;; freebsd3.[01]* | freebsdelf3.[01]*) shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; freebsd3.[2-9]* | freebsdelf3.[2-9]* | \ freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1) shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; *) # from 4.6 on, and DragonFly shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; esac ;; haiku*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no dynamic_linker="$host_os runtime_loader" library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LIBRARY_PATH shlibpath_overrides_runpath=no sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' hardcode_into_libs=yes ;; hpux9* | hpux10* | hpux11*) # Give a soname corresponding to the major version so that dld.sl refuses to # link against other versions. version_type=sunos need_lib_prefix=no need_version=no case $host_cpu in ia64*) shrext_cmds='.so' hardcode_into_libs=yes dynamic_linker="$host_os dld.so" shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' if test 32 = "$HPUX_IA64_MODE"; then sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" sys_lib_dlsearch_path_spec=/usr/lib/hpux32 else sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" sys_lib_dlsearch_path_spec=/usr/lib/hpux64 fi ;; hppa*64*) shrext_cmds='.sl' hardcode_into_libs=yes dynamic_linker="$host_os dld.sl" shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; *) shrext_cmds='.sl' dynamic_linker="$host_os dld.sl" shlibpath_var=SHLIB_PATH shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' ;; esac # HP-UX runs *really* slowly unless shared libraries are mode 555, ... postinstall_cmds='chmod 555 $lib' # or fails outright, so override atomically: install_override_mode=555 ;; interix[3-9]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; irix5* | irix6* | nonstopux*) case $host_os in nonstopux*) version_type=nonstopux ;; *) if test yes = "$lt_cv_prog_gnu_ld"; then version_type=linux # correct to gnu/linux during the next big refactor else version_type=irix fi ;; esac need_lib_prefix=no need_version=no soname_spec='$libname$release$shared_ext$major' library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$release$shared_ext $libname$shared_ext' case $host_os in irix5* | nonstopux*) libsuff= shlibsuff= ;; *) case $LD in # libtool.m4 will add one of these switches to LD *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") libsuff= shlibsuff= libmagic=32-bit;; *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") libsuff=32 shlibsuff=N32 libmagic=N32;; *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") libsuff=64 shlibsuff=64 libmagic=64-bit;; *) libsuff= shlibsuff= libmagic=never-match;; esac ;; esac shlibpath_var=LD_LIBRARY${shlibsuff}_PATH shlibpath_overrides_runpath=no sys_lib_search_path_spec="/usr/lib$libsuff /lib$libsuff /usr/local/lib$libsuff" sys_lib_dlsearch_path_spec="/usr/lib$libsuff /lib$libsuff" hardcode_into_libs=yes ;; # No shared lib support for Linux oldld, aout, or coff. linux*oldld* | linux*aout* | linux*coff*) dynamic_linker=no ;; linux*android*) version_type=none # Android doesn't support versioned libraries. need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext' soname_spec='$libname$release$shared_ext' finish_cmds= shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes dynamic_linker='Android linker' # Don't embed -rpath directories since the linker doesn't support them. hardcode_libdir_flag_spec='-L$libdir' ;; # This must be glibc/ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no # Some binutils ld are patched to set DT_RUNPATH if ${lt_cv_shlibpath_overrides_runpath+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_shlibpath_overrides_runpath=no save_LDFLAGS=$LDFLAGS save_libdir=$libdir eval "libdir=/foo; wl=\"$lt_prog_compiler_wl\"; \ LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec\"" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then : lt_cv_shlibpath_overrides_runpath=yes fi fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LDFLAGS=$save_LDFLAGS libdir=$save_libdir fi shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes # Ideally, we could use ldconfig to report *all* directores which are # searched for libraries, however this is still not possible. Aside from not # being certain /sbin/ldconfig is available, command # 'ldconfig -N -X -v | grep ^/' on 64bit Fedora does not report /usr/lib64, # even though it is searched at run-time. Try to do the best guess by # appending ld.so.conf contents (and includes) to the search path. if test -f /etc/ld.so.conf; then lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" fi # We used to test for /lib/ld.so.1 and disable shared libraries on # powerpc, because MkLinux only supported shared libraries with the # GNU dynamic linker. Since this was broken with cross compilers, # most powerpc-linux boxes support dynamic linking these days and # people can always --disable-shared, the test was removed, and we # assume the GNU/Linux dynamic linker is in use. dynamic_linker='GNU/Linux ld.so' ;; netbsdelf*-gnu) version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='NetBSD ld.elf_so' ;; netbsd*) version_type=sunos need_lib_prefix=no need_version=no if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' dynamic_linker='NetBSD (a.out) ld.so' else library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' dynamic_linker='NetBSD ld.elf_so' fi shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; newsos6) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; *nto* | *qnx*) version_type=qnx need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='ldqnx.so' ;; openbsd* | bitrig*) version_type=sunos sys_lib_dlsearch_path_spec=/usr/lib need_lib_prefix=no if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then need_version=no else need_version=yes fi library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; os2*) libname_spec='$name' version_type=windows shrext_cmds=.dll need_version=no need_lib_prefix=no # OS/2 can only load a DLL with a base name of 8 characters or less. soname_spec='`test -n "$os2dllname" && libname="$os2dllname"; v=$($ECHO $release$versuffix | tr -d .-); n=$($ECHO $libname | cut -b -$((8 - ${#v})) | tr . _); $ECHO $n$v`$shared_ext' library_names_spec='${libname}_dll.$libext' dynamic_linker='OS/2 ld.exe' shlibpath_var=BEGINLIBPATH sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; $ECHO \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; $ECHO \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' ;; osf3* | osf4* | osf5*) version_type=osf need_lib_prefix=no need_version=no soname_spec='$libname$release$shared_ext$major' library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; rdos*) dynamic_linker=no ;; solaris*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes # ldd complains unless libraries are executable postinstall_cmds='chmod +x $lib' ;; sunos4*) version_type=sunos library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes if test yes = "$with_gnu_ld"; then need_lib_prefix=no fi need_version=yes ;; sysv4 | sysv4.3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH case $host_vendor in sni) shlibpath_overrides_runpath=no need_lib_prefix=no runpath_var=LD_RUN_PATH ;; siemens) need_lib_prefix=no ;; motorola) need_lib_prefix=no need_version=no shlibpath_overrides_runpath=no sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' ;; esac ;; sysv4*MP*) if test -d /usr/nec; then version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$shared_ext.$versuffix $libname$shared_ext.$major $libname$shared_ext' soname_spec='$libname$shared_ext.$major' shlibpath_var=LD_LIBRARY_PATH fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) version_type=sco need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes if test yes = "$with_gnu_ld"; then sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' else sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' case $host_os in sco3.2v5*) sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" ;; esac fi sys_lib_dlsearch_path_spec='/usr/lib' ;; tpf*) # TPF is a cross-target only. Preferred cross-host = GNU/Linux. version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; uts4*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH ;; *) dynamic_linker=no ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5 $as_echo "$dynamic_linker" >&6; } test no = "$dynamic_linker" && can_build_shared=no variables_saved_for_relink="PATH $shlibpath_var $runpath_var" if test yes = "$GCC"; then variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" fi if test set = "${lt_cv_sys_lib_search_path_spec+set}"; then sys_lib_search_path_spec=$lt_cv_sys_lib_search_path_spec fi if test set = "${lt_cv_sys_lib_dlsearch_path_spec+set}"; then sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec fi # remember unaugmented sys_lib_dlsearch_path content for libtool script decls... configure_time_dlsearch_path=$sys_lib_dlsearch_path_spec # ... but it needs LT_SYS_LIBRARY_PATH munging for other configure-time code func_munge_path_list sys_lib_dlsearch_path_spec "$LT_SYS_LIBRARY_PATH" # to be used as default LT_SYS_LIBRARY_PATH value in generated libtool configure_time_lt_sys_library_path=$LT_SYS_LIBRARY_PATH { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5 $as_echo_n "checking how to hardcode library paths into programs... " >&6; } hardcode_action= if test -n "$hardcode_libdir_flag_spec" || test -n "$runpath_var" || test yes = "$hardcode_automatic"; then # We can hardcode non-existent directories. if test no != "$hardcode_direct" && # If the only mechanism to avoid hardcoding is shlibpath_var, we # have to relink, otherwise we might link with an installed library # when we should be linking with a yet-to-be-installed one ## test no != "$_LT_TAGVAR(hardcode_shlibpath_var, )" && test no != "$hardcode_minus_L"; then # Linking always hardcodes the temporary library directory. hardcode_action=relink else # We can link without hardcoding, and we can hardcode nonexisting dirs. hardcode_action=immediate fi else # We cannot hardcode anything, or else we can only hardcode existing # directories. hardcode_action=unsupported fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action" >&5 $as_echo "$hardcode_action" >&6; } if test relink = "$hardcode_action" || test yes = "$inherit_rpath"; then # Fast installation is not supported enable_fast_install=no elif test yes = "$shlibpath_overrides_runpath" || test no = "$enable_shared"; then # Fast installation is not necessary enable_fast_install=needless fi if test yes != "$enable_dlopen"; then enable_dlopen=unknown enable_dlopen_self=unknown enable_dlopen_self_static=unknown else lt_cv_dlopen=no lt_cv_dlopen_libs= case $host_os in beos*) lt_cv_dlopen=load_add_on lt_cv_dlopen_libs= lt_cv_dlopen_self=yes ;; mingw* | pw32* | cegcc*) lt_cv_dlopen=LoadLibrary lt_cv_dlopen_libs= ;; cygwin*) lt_cv_dlopen=dlopen lt_cv_dlopen_libs= ;; darwin*) # if libdl is installed we need to link against it { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 $as_echo_n "checking for dlopen in -ldl... " >&6; } if ${ac_cv_lib_dl_dlopen+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ldl $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char dlopen (); int main () { return dlopen (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_dl_dlopen=yes else ac_cv_lib_dl_dlopen=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 $as_echo "$ac_cv_lib_dl_dlopen" >&6; } if test "x$ac_cv_lib_dl_dlopen" = xyes; then : lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl else lt_cv_dlopen=dyld lt_cv_dlopen_libs= lt_cv_dlopen_self=yes fi ;; tpf*) # Don't try to run any link tests for TPF. We know it's impossible # because TPF is a cross-compiler, and we know how we open DSOs. lt_cv_dlopen=dlopen lt_cv_dlopen_libs= lt_cv_dlopen_self=no ;; *) ac_fn_c_check_func "$LINENO" "shl_load" "ac_cv_func_shl_load" if test "x$ac_cv_func_shl_load" = xyes; then : lt_cv_dlopen=shl_load else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for shl_load in -ldld" >&5 $as_echo_n "checking for shl_load in -ldld... " >&6; } if ${ac_cv_lib_dld_shl_load+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ldld $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char shl_load (); int main () { return shl_load (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_dld_shl_load=yes else ac_cv_lib_dld_shl_load=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_shl_load" >&5 $as_echo "$ac_cv_lib_dld_shl_load" >&6; } if test "x$ac_cv_lib_dld_shl_load" = xyes; then : lt_cv_dlopen=shl_load lt_cv_dlopen_libs=-ldld else ac_fn_c_check_func "$LINENO" "dlopen" "ac_cv_func_dlopen" if test "x$ac_cv_func_dlopen" = xyes; then : lt_cv_dlopen=dlopen else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 $as_echo_n "checking for dlopen in -ldl... " >&6; } if ${ac_cv_lib_dl_dlopen+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ldl $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char dlopen (); int main () { return dlopen (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_dl_dlopen=yes else ac_cv_lib_dl_dlopen=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 $as_echo "$ac_cv_lib_dl_dlopen" >&6; } if test "x$ac_cv_lib_dl_dlopen" = xyes; then : lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -lsvld" >&5 $as_echo_n "checking for dlopen in -lsvld... " >&6; } if ${ac_cv_lib_svld_dlopen+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lsvld $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char dlopen (); int main () { return dlopen (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_svld_dlopen=yes else ac_cv_lib_svld_dlopen=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_svld_dlopen" >&5 $as_echo "$ac_cv_lib_svld_dlopen" >&6; } if test "x$ac_cv_lib_svld_dlopen" = xyes; then : lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-lsvld else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dld_link in -ldld" >&5 $as_echo_n "checking for dld_link in -ldld... " >&6; } if ${ac_cv_lib_dld_dld_link+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ldld $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char dld_link (); int main () { return dld_link (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_dld_dld_link=yes else ac_cv_lib_dld_dld_link=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_dld_link" >&5 $as_echo "$ac_cv_lib_dld_dld_link" >&6; } if test "x$ac_cv_lib_dld_dld_link" = xyes; then : lt_cv_dlopen=dld_link lt_cv_dlopen_libs=-ldld fi fi fi fi fi fi ;; esac if test no = "$lt_cv_dlopen"; then enable_dlopen=no else enable_dlopen=yes fi case $lt_cv_dlopen in dlopen) save_CPPFLAGS=$CPPFLAGS test yes = "$ac_cv_header_dlfcn_h" && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" save_LDFLAGS=$LDFLAGS wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" save_LIBS=$LIBS LIBS="$lt_cv_dlopen_libs $LIBS" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a program can dlopen itself" >&5 $as_echo_n "checking whether a program can dlopen itself... " >&6; } if ${lt_cv_dlopen_self+:} false; then : $as_echo_n "(cached) " >&6 else if test yes = "$cross_compiling"; then : lt_cv_dlopen_self=cross else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF #line $LINENO "configure" #include "confdefs.h" #if HAVE_DLFCN_H #include #endif #include #ifdef RTLD_GLOBAL # define LT_DLGLOBAL RTLD_GLOBAL #else # ifdef DL_GLOBAL # define LT_DLGLOBAL DL_GLOBAL # else # define LT_DLGLOBAL 0 # endif #endif /* We may have to define LT_DLLAZY_OR_NOW in the command line if we find out it does not work in some platform. */ #ifndef LT_DLLAZY_OR_NOW # ifdef RTLD_LAZY # define LT_DLLAZY_OR_NOW RTLD_LAZY # else # ifdef DL_LAZY # define LT_DLLAZY_OR_NOW DL_LAZY # else # ifdef RTLD_NOW # define LT_DLLAZY_OR_NOW RTLD_NOW # else # ifdef DL_NOW # define LT_DLLAZY_OR_NOW DL_NOW # else # define LT_DLLAZY_OR_NOW 0 # endif # endif # endif # endif #endif /* When -fvisibility=hidden is used, assume the code has been annotated correspondingly for the symbols needed. */ #if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) int fnord () __attribute__((visibility("default"))); #endif int fnord () { return 42; } int main () { void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); int status = $lt_dlunknown; if (self) { if (dlsym (self,"fnord")) status = $lt_dlno_uscore; else { if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; else puts (dlerror ()); } /* dlclose (self); */ } else puts (dlerror ()); return status; } _LT_EOF if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 (eval $ac_link) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && test -s "conftest$ac_exeext" 2>/dev/null; then (./conftest; exit; ) >&5 2>/dev/null lt_status=$? case x$lt_status in x$lt_dlno_uscore) lt_cv_dlopen_self=yes ;; x$lt_dlneed_uscore) lt_cv_dlopen_self=yes ;; x$lt_dlunknown|x*) lt_cv_dlopen_self=no ;; esac else : # compilation failed lt_cv_dlopen_self=no fi fi rm -fr conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self" >&5 $as_echo "$lt_cv_dlopen_self" >&6; } if test yes = "$lt_cv_dlopen_self"; then wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a statically linked program can dlopen itself" >&5 $as_echo_n "checking whether a statically linked program can dlopen itself... " >&6; } if ${lt_cv_dlopen_self_static+:} false; then : $as_echo_n "(cached) " >&6 else if test yes = "$cross_compiling"; then : lt_cv_dlopen_self_static=cross else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF #line $LINENO "configure" #include "confdefs.h" #if HAVE_DLFCN_H #include #endif #include #ifdef RTLD_GLOBAL # define LT_DLGLOBAL RTLD_GLOBAL #else # ifdef DL_GLOBAL # define LT_DLGLOBAL DL_GLOBAL # else # define LT_DLGLOBAL 0 # endif #endif /* We may have to define LT_DLLAZY_OR_NOW in the command line if we find out it does not work in some platform. */ #ifndef LT_DLLAZY_OR_NOW # ifdef RTLD_LAZY # define LT_DLLAZY_OR_NOW RTLD_LAZY # else # ifdef DL_LAZY # define LT_DLLAZY_OR_NOW DL_LAZY # else # ifdef RTLD_NOW # define LT_DLLAZY_OR_NOW RTLD_NOW # else # ifdef DL_NOW # define LT_DLLAZY_OR_NOW DL_NOW # else # define LT_DLLAZY_OR_NOW 0 # endif # endif # endif # endif #endif /* When -fvisibility=hidden is used, assume the code has been annotated correspondingly for the symbols needed. */ #if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) int fnord () __attribute__((visibility("default"))); #endif int fnord () { return 42; } int main () { void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); int status = $lt_dlunknown; if (self) { if (dlsym (self,"fnord")) status = $lt_dlno_uscore; else { if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; else puts (dlerror ()); } /* dlclose (self); */ } else puts (dlerror ()); return status; } _LT_EOF if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 (eval $ac_link) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && test -s "conftest$ac_exeext" 2>/dev/null; then (./conftest; exit; ) >&5 2>/dev/null lt_status=$? case x$lt_status in x$lt_dlno_uscore) lt_cv_dlopen_self_static=yes ;; x$lt_dlneed_uscore) lt_cv_dlopen_self_static=yes ;; x$lt_dlunknown|x*) lt_cv_dlopen_self_static=no ;; esac else : # compilation failed lt_cv_dlopen_self_static=no fi fi rm -fr conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self_static" >&5 $as_echo "$lt_cv_dlopen_self_static" >&6; } fi CPPFLAGS=$save_CPPFLAGS LDFLAGS=$save_LDFLAGS LIBS=$save_LIBS ;; esac case $lt_cv_dlopen_self in yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; *) enable_dlopen_self=unknown ;; esac case $lt_cv_dlopen_self_static in yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; *) enable_dlopen_self_static=unknown ;; esac fi striplib= old_striplib= { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stripping libraries is possible" >&5 $as_echo_n "checking whether stripping libraries is possible... " >&6; } if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" test -z "$striplib" && striplib="$STRIP --strip-unneeded" { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else # FIXME - insert some real tests, host_os isn't really good enough case $host_os in darwin*) if test -n "$STRIP"; then striplib="$STRIP -x" old_striplib="$STRIP -S" { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi ;; *) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; esac fi # Report what library types will actually be built { $as_echo "$as_me:${as_lineno-$LINENO}: checking if libtool supports shared libraries" >&5 $as_echo_n "checking if libtool supports shared libraries... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: $can_build_shared" >&5 $as_echo "$can_build_shared" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build shared libraries" >&5 $as_echo_n "checking whether to build shared libraries... " >&6; } test no = "$can_build_shared" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test yes = "$enable_shared" && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[4-9]*) if test ia64 != "$host_cpu"; then case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in yes,aix,yes) ;; # shared object as lib.so file only yes,svr4,*) ;; # shared object as lib.so archive member only yes,*) enable_static=no ;; # shared object in lib.a archive as well esac fi ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_shared" >&5 $as_echo "$enable_shared" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build static libraries" >&5 $as_echo_n "checking whether to build static libraries... " >&6; } # Make sure either enable_shared or enable_static is yes. test yes = "$enable_shared" || enable_static=yes { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_static" >&5 $as_echo "$enable_static" >&6; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu CC=$lt_save_CC ac_config_commands="$ac_config_commands libtool" # Only expand once: # Check whether --enable-largefile was given. if test "${enable_largefile+set}" = set; then : enableval=$enable_largefile; fi if test "$enable_largefile" != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for special C compiler options needed for large files" >&5 $as_echo_n "checking for special C compiler options needed for large files... " >&6; } if ${ac_cv_sys_largefile_CC+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_sys_largefile_CC=no if test "$GCC" != yes; then ac_save_CC=$CC while :; do # IRIX 6.2 and later do not support large files by default, # so use the C compiler's -n32 option if that helps. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : break fi rm -f core conftest.err conftest.$ac_objext CC="$CC -n32" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_sys_largefile_CC=' -n32'; break fi rm -f core conftest.err conftest.$ac_objext break done CC=$ac_save_CC rm -f conftest.$ac_ext fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_largefile_CC" >&5 $as_echo "$ac_cv_sys_largefile_CC" >&6; } if test "$ac_cv_sys_largefile_CC" != no; then CC=$CC$ac_cv_sys_largefile_CC fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _FILE_OFFSET_BITS value needed for large files" >&5 $as_echo_n "checking for _FILE_OFFSET_BITS value needed for large files... " >&6; } if ${ac_cv_sys_file_offset_bits+:} false; then : $as_echo_n "(cached) " >&6 else while :; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_sys_file_offset_bits=no; break fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define _FILE_OFFSET_BITS 64 #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_sys_file_offset_bits=64; break fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_sys_file_offset_bits=unknown break done fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_file_offset_bits" >&5 $as_echo "$ac_cv_sys_file_offset_bits" >&6; } case $ac_cv_sys_file_offset_bits in #( no | unknown) ;; *) cat >>confdefs.h <<_ACEOF #define _FILE_OFFSET_BITS $ac_cv_sys_file_offset_bits _ACEOF ;; esac rm -rf conftest* if test $ac_cv_sys_file_offset_bits = unknown; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _LARGE_FILES value needed for large files" >&5 $as_echo_n "checking for _LARGE_FILES value needed for large files... " >&6; } if ${ac_cv_sys_large_files+:} false; then : $as_echo_n "(cached) " >&6 else while :; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_sys_large_files=no; break fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define _LARGE_FILES 1 #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_sys_large_files=1; break fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_sys_large_files=unknown break done fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_large_files" >&5 $as_echo "$ac_cv_sys_large_files" >&6; } case $ac_cv_sys_large_files in #( no | unknown) ;; *) cat >>confdefs.h <<_ACEOF #define _LARGE_FILES $ac_cv_sys_large_files _ACEOF ;; esac rm -rf conftest* fi fi if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args. set dummy ${ac_tool_prefix}pkg-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_PKG_CONFIG+:} false; then : $as_echo_n "(cached) " >&6 else case $PKG_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi PKG_CONFIG=$ac_cv_path_PKG_CONFIG if test -n "$PKG_CONFIG"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5 $as_echo "$PKG_CONFIG" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_path_PKG_CONFIG"; then ac_pt_PKG_CONFIG=$PKG_CONFIG # Extract the first word of "pkg-config", so it can be a program name with args. set dummy pkg-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_ac_pt_PKG_CONFIG+:} false; then : $as_echo_n "(cached) " >&6 else case $ac_pt_PKG_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG if test -n "$ac_pt_PKG_CONFIG"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5 $as_echo "$ac_pt_PKG_CONFIG" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_pt_PKG_CONFIG" = x; then PKG_CONFIG="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac PKG_CONFIG=$ac_pt_PKG_CONFIG fi else PKG_CONFIG="$ac_cv_path_PKG_CONFIG" fi fi if test -n "$PKG_CONFIG"; then _pkg_min_version=0.24 { $as_echo "$as_me:${as_lineno-$LINENO}: checking pkg-config is at least version $_pkg_min_version" >&5 $as_echo_n "checking pkg-config is at least version $_pkg_min_version... " >&6; } if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } PKG_CONFIG="" fi fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See \`config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 $as_echo_n "checking whether we are using the GNU C compiler... " >&6; } if ${ac_cv_c_compiler_gnu+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 $as_echo "$ac_cv_c_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+set} ac_save_CFLAGS=$CFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 $as_echo_n "checking whether $CC accepts -g... " >&6; } if ${ac_cv_prog_cc_g+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes else CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : else ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 $as_echo "$ac_cv_prog_cc_g" >&6; } if test "$ac_test_CFLAGS" = set; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 $as_echo_n "checking for $CC option to accept ISO C89... " >&6; } if ${ac_cv_prog_cc_c89+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include struct stat; /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not '\xHH' hex character constants. These don't provoke an error unfortunately, instead are silently treated as 'x'. The following induces an error, until -std is added to get proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an array size at least. It's necessary to write '\x00'==0 to get something that's true only with -std. */ int osf4_cc_array ['\x00' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) 'x' int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); int argc; char **argv; int main () { return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; ; return 0; } _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c89" in x) { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } ;; xno) { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c89" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 $as_echo "$ac_cv_prog_cc_c89" >&6; } ;; esac if test "x$ac_cv_prog_cc_c89" != xno; then : fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5 $as_echo_n "checking whether $CC understands -c and -o together... " >&6; } if ${am_cv_prog_cc_c_o+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF # Make sure it works both with $CC and with simple cc. # Following AC_PROG_CC_C_O, we do the test twice because some # compilers refuse to overwrite an existing .o file with -o, # though they will create one. am_cv_prog_cc_c_o=yes for am_i in 1 2; do if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5 ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } \ && test -f conftest2.$ac_objext; then : OK else am_cv_prog_cc_c_o=no break fi done rm -f core conftest* unset am_i fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5 $as_echo "$am_cv_prog_cc_c_o" >&6; } if test "$am_cv_prog_cc_c_o" != yes; then # Losing compiler, so override with the script. # FIXME: It is wrong to rewrite CC. # But if we don't then we get into trouble of one sort or another. # A longer-term fix would be to have automake use am__CC in this case, # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" CC="$am_aux_dir/compile $CC" fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu depcc="$CC" am_compiler_list= { $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 $as_echo_n "checking dependency style of $depcc... " >&6; } if ${am_cv_CC_dependencies_compiler_type+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For # instance it was reported that on HP-UX the gcc test will end up # making a dummy file named 'D' -- because '-MD' means "put the output # in D". rm -rf conftest.dir mkdir conftest.dir # Copy depcomp to subdir because otherwise we won't find it if we're # using a relative directory. cp "$am_depcomp" conftest.dir cd conftest.dir # We will build objects and dependencies in a subdirectory because # it helps to detect inapplicable dependency modes. For instance # both Tru64's cc and ICC support -MD to output dependencies as a # side effect of compilation, but ICC will put the dependencies in # the current directory while Tru64 will put them in the object # directory. mkdir sub am_cv_CC_dependencies_compiler_type=none if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` fi am__universal=false case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac for depmode in $am_compiler_list; do # Setup a source with many dependencies, because some compilers # like to wrap large dependency lists on column 80 (with \), and # we should not choose a depcomp mode which is confused by this. # # We need to recreate these files for each test, as the compiler may # overwrite some of them when testing with obscure command lines. # This happens at least with the AIX C compiler. : > sub/conftest.c for i in 1 2 3 4 5 6; do echo '#include "conftst'$i'.h"' >> sub/conftest.c # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with # Solaris 10 /bin/sh. echo '/* dummy */' > sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf # We check with '-c' and '-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly # handle '-M -o', and we need to detect this. Also, some Intel # versions had trouble with output in subdirs. am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in gcc) # This depmode causes a compiler race in universal mode. test "$am__universal" = false || continue ;; nosideeffect) # After this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested. if test "x$enable_dependency_tracking" = xyes; then continue else break fi ;; msvc7 | msvc7msys | msvisualcpp | msvcmsys) # This compiler won't grok '-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} am__minus_obj= ;; none) break ;; esac if depmode=$depmode \ source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && grep $am__obj sub/conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then # icc doesn't choke on unknown options, it will just issue warnings # or remarks (even with -Werror). So we grep stderr for any message # that says an option was ignored or not supported. # When given -MP, icc 7.0 and 7.1 complain thusly: # icc: Command line warning: ignoring option '-M'; no argument required # The diagnosis changed in icc 8.0: # icc: Command line remark: option '-MP' not supported if (grep 'ignoring option' conftest.err || grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else am_cv_CC_dependencies_compiler_type=$depmode break fi fi done cd .. rm -rf conftest.dir else am_cv_CC_dependencies_compiler_type=none fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 $as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type if test "x$enable_dependency_tracking" != xno \ && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then am__fastdepCC_TRUE= am__fastdepCC_FALSE='#' else am__fastdepCC_TRUE='#' am__fastdepCC_FALSE= fi ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu if test -z "$CXX"; then if test -n "$CCC"; then CXX=$CCC else if test -n "$ac_tool_prefix"; then for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CXX+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CXX"; then ac_cv_prog_CXX="$CXX" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CXX=$ac_cv_prog_CXX if test -n "$CXX"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5 $as_echo "$CXX" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CXX" && break done fi if test -z "$CXX"; then ac_ct_CXX=$CXX for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CXX+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CXX"; then ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CXX="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CXX=$ac_cv_prog_ac_ct_CXX if test -n "$ac_ct_CXX"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5 $as_echo "$ac_ct_CXX" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CXX" && break done if test "x$ac_ct_CXX" = x; then CXX="g++" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CXX=$ac_ct_CXX fi fi fi fi # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5 $as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; } if ${ac_cv_cxx_compiler_gnu+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_cxx_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5 $as_echo "$ac_cv_cxx_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GXX=yes else GXX= fi ac_test_CXXFLAGS=${CXXFLAGS+set} ac_save_CXXFLAGS=$CXXFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5 $as_echo_n "checking whether $CXX accepts -g... " >&6; } if ${ac_cv_prog_cxx_g+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_cxx_werror_flag=$ac_cxx_werror_flag ac_cxx_werror_flag=yes ac_cv_prog_cxx_g=no CXXFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_prog_cxx_g=yes else CXXFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : else ac_cxx_werror_flag=$ac_save_cxx_werror_flag CXXFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_prog_cxx_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cxx_werror_flag=$ac_save_cxx_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5 $as_echo "$ac_cv_prog_cxx_g" >&6; } if test "$ac_test_CXXFLAGS" = set; then CXXFLAGS=$ac_save_CXXFLAGS elif test $ac_cv_prog_cxx_g = yes; then if test "$GXX" = yes; then CXXFLAGS="-g -O2" else CXXFLAGS="-g" fi else if test "$GXX" = yes; then CXXFLAGS="-O2" else CXXFLAGS= fi fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu depcc="$CXX" am_compiler_list= { $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 $as_echo_n "checking dependency style of $depcc... " >&6; } if ${am_cv_CXX_dependencies_compiler_type+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For # instance it was reported that on HP-UX the gcc test will end up # making a dummy file named 'D' -- because '-MD' means "put the output # in D". rm -rf conftest.dir mkdir conftest.dir # Copy depcomp to subdir because otherwise we won't find it if we're # using a relative directory. cp "$am_depcomp" conftest.dir cd conftest.dir # We will build objects and dependencies in a subdirectory because # it helps to detect inapplicable dependency modes. For instance # both Tru64's cc and ICC support -MD to output dependencies as a # side effect of compilation, but ICC will put the dependencies in # the current directory while Tru64 will put them in the object # directory. mkdir sub am_cv_CXX_dependencies_compiler_type=none if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` fi am__universal=false case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac for depmode in $am_compiler_list; do # Setup a source with many dependencies, because some compilers # like to wrap large dependency lists on column 80 (with \), and # we should not choose a depcomp mode which is confused by this. # # We need to recreate these files for each test, as the compiler may # overwrite some of them when testing with obscure command lines. # This happens at least with the AIX C compiler. : > sub/conftest.c for i in 1 2 3 4 5 6; do echo '#include "conftst'$i'.h"' >> sub/conftest.c # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with # Solaris 10 /bin/sh. echo '/* dummy */' > sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf # We check with '-c' and '-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly # handle '-M -o', and we need to detect this. Also, some Intel # versions had trouble with output in subdirs. am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in gcc) # This depmode causes a compiler race in universal mode. test "$am__universal" = false || continue ;; nosideeffect) # After this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested. if test "x$enable_dependency_tracking" = xyes; then continue else break fi ;; msvc7 | msvc7msys | msvisualcpp | msvcmsys) # This compiler won't grok '-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} am__minus_obj= ;; none) break ;; esac if depmode=$depmode \ source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && grep $am__obj sub/conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then # icc doesn't choke on unknown options, it will just issue warnings # or remarks (even with -Werror). So we grep stderr for any message # that says an option was ignored or not supported. # When given -MP, icc 7.0 and 7.1 complain thusly: # icc: Command line warning: ignoring option '-M'; no argument required # The diagnosis changed in icc 8.0: # icc: Command line remark: option '-MP' not supported if (grep 'ignoring option' conftest.err || grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else am_cv_CXX_dependencies_compiler_type=$depmode break fi fi done cd .. rm -rf conftest.dir else am_cv_CXX_dependencies_compiler_type=none fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CXX_dependencies_compiler_type" >&5 $as_echo "$am_cv_CXX_dependencies_compiler_type" >&6; } CXXDEPMODE=depmode=$am_cv_CXX_dependencies_compiler_type if test "x$enable_dependency_tracking" != xno \ && test "$am_cv_CXX_dependencies_compiler_type" = gcc3; then am__fastdepCXX_TRUE= am__fastdepCXX_FALSE='#' else am__fastdepCXX_TRUE='#' am__fastdepCXX_FALSE= fi func_stripname_cnf () { case $2 in .*) func_stripname_result=`$ECHO "$3" | $SED "s%^$1%%; s%\\\\$2\$%%"`;; *) func_stripname_result=`$ECHO "$3" | $SED "s%^$1%%; s%$2\$%%"`;; esac } # func_stripname_cnf if test -n "$CXX" && ( test no != "$CXX" && ( (test g++ = "$CXX" && `g++ -v >/dev/null 2>&1` ) || (test g++ != "$CXX"))); then ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C++ preprocessor" >&5 $as_echo_n "checking how to run the C++ preprocessor... " >&6; } if test -z "$CXXCPP"; then if ${ac_cv_prog_CXXCPP+:} false; then : $as_echo_n "(cached) " >&6 else # Double quotes because CXXCPP needs to be expanded for CXXCPP in "$CXX -E" "/lib/cpp" do ac_preproc_ok=false for ac_cxx_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_cxx_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_cxx_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : break fi done ac_cv_prog_CXXCPP=$CXXCPP fi CXXCPP=$ac_cv_prog_CXXCPP else ac_cv_prog_CXXCPP=$CXXCPP fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXXCPP" >&5 $as_echo "$CXXCPP" >&6; } ac_preproc_ok=false for ac_cxx_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_cxx_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_cxx_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "C++ preprocessor \"$CXXCPP\" fails sanity check See \`config.log' for more details" "$LINENO" 5; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu else _lt_caught_CXX_error=yes fi ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu archive_cmds_need_lc_CXX=no allow_undefined_flag_CXX= always_export_symbols_CXX=no archive_expsym_cmds_CXX= compiler_needs_object_CXX=no export_dynamic_flag_spec_CXX= hardcode_direct_CXX=no hardcode_direct_absolute_CXX=no hardcode_libdir_flag_spec_CXX= hardcode_libdir_separator_CXX= hardcode_minus_L_CXX=no hardcode_shlibpath_var_CXX=unsupported hardcode_automatic_CXX=no inherit_rpath_CXX=no module_cmds_CXX= module_expsym_cmds_CXX= link_all_deplibs_CXX=unknown old_archive_cmds_CXX=$old_archive_cmds reload_flag_CXX=$reload_flag reload_cmds_CXX=$reload_cmds no_undefined_flag_CXX= whole_archive_flag_spec_CXX= enable_shared_with_static_runtimes_CXX=no # Source file extension for C++ test sources. ac_ext=cpp # Object file extension for compiled C++ test sources. objext=o objext_CXX=$objext # No sense in running all these tests if we already determined that # the CXX compiler isn't working. Some variables (like enable_shared) # are currently assumed to apply to all compilers on this platform, # and will be corrupted by setting them based on a non-working compiler. if test yes != "$_lt_caught_CXX_error"; then # Code to be used in simple compile tests lt_simple_compile_test_code="int some_variable = 0;" # Code to be used in simple link tests lt_simple_link_test_code='int main(int, char *[]) { return(0); }' # ltmain only uses $CC for tagged configurations so make sure $CC is set. # If no C compiler was specified, use CC. LTCC=${LTCC-"$CC"} # If no C compiler flags were specified, use CFLAGS. LTCFLAGS=${LTCFLAGS-"$CFLAGS"} # Allow CC to be a program name with arguments. compiler=$CC # save warnings/boilerplate of simple test code ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" >conftest.$ac_ext eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_compiler_boilerplate=`cat conftest.err` $RM conftest* ac_outfile=conftest.$ac_objext echo "$lt_simple_link_test_code" >conftest.$ac_ext eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_linker_boilerplate=`cat conftest.err` $RM -r conftest* # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_CFLAGS=$CFLAGS lt_save_LD=$LD lt_save_GCC=$GCC GCC=$GXX lt_save_with_gnu_ld=$with_gnu_ld lt_save_path_LD=$lt_cv_path_LD if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx else $as_unset lt_cv_prog_gnu_ld fi if test -n "${lt_cv_path_LDCXX+set}"; then lt_cv_path_LD=$lt_cv_path_LDCXX else $as_unset lt_cv_path_LD fi test -z "${LDCXX+set}" || LD=$LDCXX CC=${CXX-"c++"} CFLAGS=$CXXFLAGS compiler=$CC compiler_CXX=$CC func_cc_basename $compiler cc_basename=$func_cc_basename_result if test -n "$compiler"; then # We don't want -fno-exception when compiling C++ code, so set the # no_builtin_flag separately if test yes = "$GXX"; then lt_prog_compiler_no_builtin_flag_CXX=' -fno-builtin' else lt_prog_compiler_no_builtin_flag_CXX= fi if test yes = "$GXX"; then # Set up default GNU C++ configuration # Check whether --with-gnu-ld was given. if test "${with_gnu_ld+set}" = set; then : withval=$with_gnu_ld; test no = "$withval" || with_gnu_ld=yes else with_gnu_ld=no fi ac_prog=ld if test yes = "$GCC"; then # Check if gcc -print-prog-name=ld gives a path. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5 $as_echo_n "checking for ld used by $CC... " >&6; } case $host in *-*-mingw*) # gcc leaves a trailing carriage return, which upsets mingw ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; *) ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; esac case $ac_prog in # Accept absolute paths. [\\/]* | ?:[\\/]*) re_direlt='/[^/][^/]*/\.\./' # Canonicalize the pathname of ld ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` done test -z "$LD" && LD=$ac_prog ;; "") # If it fails, then pretend we aren't using GCC. ac_prog=ld ;; *) # If it is relative, then search for the first ld in PATH. with_gnu_ld=unknown ;; esac elif test yes = "$with_gnu_ld"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5 $as_echo_n "checking for GNU ld... " >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5 $as_echo_n "checking for non-GNU ld... " >&6; } fi if ${lt_cv_path_LD+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$LD"; then lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR for ac_dir in $PATH; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then lt_cv_path_LD=$ac_dir/$ac_prog # Check to see if the program is GNU ld. I'd rather use --version, # but apparently some variants of GNU ld only accept -v. # Break only if it was the GNU/non-GNU ld that we prefer. case `"$lt_cv_path_LD" -v 2>&1 &5 $as_echo "$LD" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5 $as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; } if ${lt_cv_prog_gnu_ld+:} false; then : $as_echo_n "(cached) " >&6 else # I'd rather use --version here, but apparently some GNU lds only accept -v. case `$LD -v 2>&1 &5 $as_echo "$lt_cv_prog_gnu_ld" >&6; } with_gnu_ld=$lt_cv_prog_gnu_ld # Check if GNU C++ uses GNU ld as the underlying linker, since the # archiving commands below assume that GNU ld is being used. if test yes = "$with_gnu_ld"; then archive_cmds_CXX='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds_CXX='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir' export_dynamic_flag_spec_CXX='$wl--export-dynamic' # If archive_cmds runs LD, not CC, wlarc should be empty # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to # investigate it a little bit more. (MM) wlarc='$wl' # ancient GNU ld didn't support --whole-archive et. al. if eval "`$CC -print-prog-name=ld` --help 2>&1" | $GREP 'no-whole-archive' > /dev/null; then whole_archive_flag_spec_CXX=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' else whole_archive_flag_spec_CXX= fi else with_gnu_ld=no wlarc= # A generic and very simple default shared library creation # command for GNU C++ for the case where it uses the native # linker, instead of GNU ld. If possible, this setting should # overridden to take advantage of the native linker features on # the platform it is being used on. archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' fi # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"' else GXX=no with_gnu_ld=no wlarc= fi # PORTME: fill in a description of your system's C++ link characteristics { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 $as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } ld_shlibs_CXX=yes case $host_os in aix3*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; aix[4-9]*) if test ia64 = "$host_cpu"; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no exp_sym_flag='-Bexport' no_entry_flag= else aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # have runtime linking enabled, and use it for executables. # For shared libraries, we enable/disable runtime linking # depending on the kind of the shared library created - # when "with_aix_soname,aix_use_runtimelinking" is: # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables # "aix,yes" lib.so shared, rtl:yes, for executables # lib.a static archive # "both,no" lib.so.V(shr.o) shared, rtl:yes # lib.a(lib.so.V) shared, rtl:no, for executables # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a(lib.so.V) shared, rtl:no # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a static archive case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) for ld_flag in $LDFLAGS; do case $ld_flag in *-brtl*) aix_use_runtimelinking=yes break ;; esac done if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then # With aix-soname=svr4, we create the lib.so.V shared archives only, # so we don't have lib.a shared libs to link our executables. # We have to force runtime linking in this case. aix_use_runtimelinking=yes LDFLAGS="$LDFLAGS -Wl,-brtl" fi ;; esac exp_sym_flag='-bexport' no_entry_flag='-bnoentry' fi # When large executables or shared objects are built, AIX ld can # have problems creating the table of contents. If linking a library # or program results in "error TOC overflow" add -mminimal-toc to # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. archive_cmds_CXX='' hardcode_direct_CXX=yes hardcode_direct_absolute_CXX=yes hardcode_libdir_separator_CXX=':' link_all_deplibs_CXX=yes file_list_spec_CXX='$wl-f,' case $with_aix_soname,$aix_use_runtimelinking in aix,*) ;; # no import file svr4,* | *,yes) # use import file # The Import File defines what to hardcode. hardcode_direct_CXX=no hardcode_direct_absolute_CXX=no ;; esac if test yes = "$GXX"; then case $host_os in aix4.[012]|aix4.[012].*) # We only want to do this on AIX 4.2 and lower, the check # below for broken collect2 doesn't work under 4.3+ collect2name=`$CC -print-prog-name=collect2` if test -f "$collect2name" && strings "$collect2name" | $GREP resolve_lib_name >/dev/null then # We have reworked collect2 : else # We have old collect2 hardcode_direct_CXX=unsupported # It fails to find uninstalled libraries when the uninstalled # path is not listed in the libpath. Setting hardcode_minus_L # to unsupported forces relinking hardcode_minus_L_CXX=yes hardcode_libdir_flag_spec_CXX='-L$libdir' hardcode_libdir_separator_CXX= fi esac shared_flag='-shared' if test yes = "$aix_use_runtimelinking"; then shared_flag=$shared_flag' $wl-G' fi # Need to ensure runtime linking is disabled for the traditional # shared library, or the linker may eventually find shared libraries # /with/ Import File - we do not want to mix them. shared_flag_aix='-shared' shared_flag_svr4='-shared $wl-G' else # not using gcc if test ia64 = "$host_cpu"; then # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release # chokes on -Wl,-G. The following line is correct: shared_flag='-G' else if test yes = "$aix_use_runtimelinking"; then shared_flag='$wl-G' else shared_flag='$wl-bM:SRE' fi shared_flag_aix='$wl-bM:SRE' shared_flag_svr4='$wl-G' fi fi export_dynamic_flag_spec_CXX='$wl-bexpall' # It seems that -bexpall does not export symbols beginning with # underscore (_), so it is better to generate a list of symbols to # export. always_export_symbols_CXX=yes if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then # Warning - without using the other runtime loading flags (-brtl), # -berok will link without error, but may produce a broken library. # The "-G" linker flag allows undefined symbols. no_undefined_flag_CXX='-bernotok' # Determine the default libpath from the value encoded in an empty # executable. if test set = "${lt_cv_aix_libpath+set}"; then aix_libpath=$lt_cv_aix_libpath else if ${lt_cv_aix_libpath__CXX+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : lt_aix_libpath_sed=' /Import File Strings/,/^$/ { /^0/ { s/^0 *\([^ ]*\) *$/\1/ p } }' lt_cv_aix_libpath__CXX=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` # Check for a 64-bit object if we didn't find anything. if test -z "$lt_cv_aix_libpath__CXX"; then lt_cv_aix_libpath__CXX=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test -z "$lt_cv_aix_libpath__CXX"; then lt_cv_aix_libpath__CXX=/usr/lib:/lib fi fi aix_libpath=$lt_cv_aix_libpath__CXX fi hardcode_libdir_flag_spec_CXX='$wl-blibpath:$libdir:'"$aix_libpath" archive_expsym_cmds_CXX='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag else if test ia64 = "$host_cpu"; then hardcode_libdir_flag_spec_CXX='$wl-R $libdir:/usr/lib:/lib' allow_undefined_flag_CXX="-z nodefs" archive_expsym_cmds_CXX="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols" else # Determine the default libpath from the value encoded in an # empty executable. if test set = "${lt_cv_aix_libpath+set}"; then aix_libpath=$lt_cv_aix_libpath else if ${lt_cv_aix_libpath__CXX+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : lt_aix_libpath_sed=' /Import File Strings/,/^$/ { /^0/ { s/^0 *\([^ ]*\) *$/\1/ p } }' lt_cv_aix_libpath__CXX=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` # Check for a 64-bit object if we didn't find anything. if test -z "$lt_cv_aix_libpath__CXX"; then lt_cv_aix_libpath__CXX=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test -z "$lt_cv_aix_libpath__CXX"; then lt_cv_aix_libpath__CXX=/usr/lib:/lib fi fi aix_libpath=$lt_cv_aix_libpath__CXX fi hardcode_libdir_flag_spec_CXX='$wl-blibpath:$libdir:'"$aix_libpath" # Warning - without using the other run time loading flags, # -berok will link without error, but may produce a broken library. no_undefined_flag_CXX=' $wl-bernotok' allow_undefined_flag_CXX=' $wl-berok' if test yes = "$with_gnu_ld"; then # We only use this code for GNU lds that support --whole-archive. whole_archive_flag_spec_CXX='$wl--whole-archive$convenience $wl--no-whole-archive' else # Exported symbols can be pulled into shared objects from archives whole_archive_flag_spec_CXX='$convenience' fi archive_cmds_need_lc_CXX=yes archive_expsym_cmds_CXX='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d' # -brtl affects multiple linker settings, -berok does not and is overridden later compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([, ]\\)%-berok\\1%g"`' if test svr4 != "$with_aix_soname"; then # This is similar to how AIX traditionally builds its shared # libraries. Need -bnortl late, we may have -brtl in LDFLAGS. archive_expsym_cmds_CXX="$archive_expsym_cmds_CXX"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname' fi if test aix != "$with_aix_soname"; then archive_expsym_cmds_CXX="$archive_expsym_cmds_CXX"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp' else # used by -dlpreopen to get the symbols archive_expsym_cmds_CXX="$archive_expsym_cmds_CXX"'~$MV $output_objdir/$realname.d/$soname $output_objdir' fi archive_expsym_cmds_CXX="$archive_expsym_cmds_CXX"'~$RM -r $output_objdir/$realname.d' fi fi ;; beos*) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then allow_undefined_flag_CXX=unsupported # Joseph Beckenbach says some releases of gcc # support --undefined. This deserves some investigation. FIXME archive_cmds_CXX='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' else ld_shlibs_CXX=no fi ;; chorus*) case $cc_basename in *) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; esac ;; cygwin* | mingw* | pw32* | cegcc*) case $GXX,$cc_basename in ,cl* | no,cl*) # Native MSVC # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. hardcode_libdir_flag_spec_CXX=' ' allow_undefined_flag_CXX=unsupported always_export_symbols_CXX=yes file_list_spec_CXX='@' # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=.dll # FIXME: Setting linknames here is a bad hack. archive_cmds_CXX='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames=' archive_expsym_cmds_CXX='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then cp "$export_symbols" "$output_objdir/$soname.def"; echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp"; else $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp; fi~ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ linknames=' # The linker will not automatically build a static lib if we build a DLL. # _LT_TAGVAR(old_archive_from_new_cmds, CXX)='true' enable_shared_with_static_runtimes_CXX=yes # Don't use ranlib old_postinstall_cmds_CXX='chmod 644 $oldlib' postlink_cmds_CXX='lt_outputfile="@OUTPUT@"~ lt_tool_outputfile="@TOOL_OUTPUT@"~ case $lt_outputfile in *.exe|*.EXE) ;; *) lt_outputfile=$lt_outputfile.exe lt_tool_outputfile=$lt_tool_outputfile.exe ;; esac~ func_to_tool_file "$lt_outputfile"~ if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; $RM "$lt_outputfile.manifest"; fi' ;; *) # g++ # _LT_TAGVAR(hardcode_libdir_flag_spec, CXX) is actually meaningless, # as there is no search path for DLLs. hardcode_libdir_flag_spec_CXX='-L$libdir' export_dynamic_flag_spec_CXX='$wl--export-all-symbols' allow_undefined_flag_CXX=unsupported always_export_symbols_CXX=no enable_shared_with_static_runtimes_CXX=yes if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' # If the export-symbols file already is a .def file, use it as # is; otherwise, prepend EXPORTS... archive_expsym_cmds_CXX='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then cp $export_symbols $output_objdir/$soname.def; else echo EXPORTS > $output_objdir/$soname.def; cat $export_symbols >> $output_objdir/$soname.def; fi~ $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' else ld_shlibs_CXX=no fi ;; esac ;; darwin* | rhapsody*) archive_cmds_need_lc_CXX=no hardcode_direct_CXX=no hardcode_automatic_CXX=yes hardcode_shlibpath_var_CXX=unsupported if test yes = "$lt_cv_ld_force_load"; then whole_archive_flag_spec_CXX='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience $wl-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' else whole_archive_flag_spec_CXX='' fi link_all_deplibs_CXX=yes allow_undefined_flag_CXX=$_lt_dar_allow_undefined case $cc_basename in ifort*|nagfor*) _lt_dar_can_shared=yes ;; *) _lt_dar_can_shared=$GCC ;; esac if test yes = "$_lt_dar_can_shared"; then output_verbose_link_cmd=func_echo_all archive_cmds_CXX="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dsymutil" module_cmds_CXX="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dsymutil" archive_expsym_cmds_CXX="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil" module_expsym_cmds_CXX="sed -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil" if test yes != "$lt_cv_apple_cc_single_mod"; then archive_cmds_CXX="\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dsymutil" archive_expsym_cmds_CXX="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dar_export_syms$_lt_dsymutil" fi else ld_shlibs_CXX=no fi ;; os2*) hardcode_libdir_flag_spec_CXX='-L$libdir' hardcode_minus_L_CXX=yes allow_undefined_flag_CXX=unsupported shrext_cmds=.dll archive_cmds_CXX='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' archive_expsym_cmds_CXX='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ prefix_cmds="$SED"~ if test EXPORTS = "`$SED 1q $export_symbols`"; then prefix_cmds="$prefix_cmds -e 1d"; fi~ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' old_archive_From_new_cmds_CXX='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' enable_shared_with_static_runtimes_CXX=yes ;; dgux*) case $cc_basename in ec++*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; ghcx*) # Green Hills C++ Compiler # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; *) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; esac ;; freebsd2.*) # C++ shared libraries reported to be fairly broken before # switch to ELF ld_shlibs_CXX=no ;; freebsd-elf*) archive_cmds_need_lc_CXX=no ;; freebsd* | dragonfly*) # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF # conventions ld_shlibs_CXX=yes ;; haiku*) archive_cmds_CXX='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' link_all_deplibs_CXX=yes ;; hpux9*) hardcode_libdir_flag_spec_CXX='$wl+b $wl$libdir' hardcode_libdir_separator_CXX=: export_dynamic_flag_spec_CXX='$wl-E' hardcode_direct_CXX=yes hardcode_minus_L_CXX=yes # Not in the search PATH, # but as the default # location of the library. case $cc_basename in CC*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; aCC*) archive_cmds_CXX='$RM $output_objdir/$soname~$CC -b $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP " \-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test yes = "$GXX"; then archive_cmds_CXX='$RM $output_objdir/$soname~$CC -shared -nostdlib $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' else # FIXME: insert proper C++ library support ld_shlibs_CXX=no fi ;; esac ;; hpux10*|hpux11*) if test no = "$with_gnu_ld"; then hardcode_libdir_flag_spec_CXX='$wl+b $wl$libdir' hardcode_libdir_separator_CXX=: case $host_cpu in hppa*64*|ia64*) ;; *) export_dynamic_flag_spec_CXX='$wl-E' ;; esac fi case $host_cpu in hppa*64*|ia64*) hardcode_direct_CXX=no hardcode_shlibpath_var_CXX=no ;; *) hardcode_direct_CXX=yes hardcode_direct_absolute_CXX=yes hardcode_minus_L_CXX=yes # Not in the search PATH, # but as the default # location of the library. ;; esac case $cc_basename in CC*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; aCC*) case $host_cpu in hppa*64*) archive_cmds_CXX='$CC -b $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; ia64*) archive_cmds_CXX='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; *) archive_cmds_CXX='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; esac # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP " \-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test yes = "$GXX"; then if test no = "$with_gnu_ld"; then case $host_cpu in hppa*64*) archive_cmds_CXX='$CC -shared -nostdlib -fPIC $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; ia64*) archive_cmds_CXX='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; *) archive_cmds_CXX='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; esac fi else # FIXME: insert proper C++ library support ld_shlibs_CXX=no fi ;; esac ;; interix[3-9]*) hardcode_direct_CXX=no hardcode_shlibpath_var_CXX=no hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir' export_dynamic_flag_spec_CXX='$wl-E' # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. # Instead, shared libraries are loaded at an image base (0x10000000 by # default) and relocated if they conflict, which is a slow very memory # consuming and fragmenting process. To avoid this, we pick a random, # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link # time. Moving up from 0x10000000 also allows more sbrk(2) space. archive_cmds_CXX='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' archive_expsym_cmds_CXX='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ;; irix5* | irix6*) case $cc_basename in CC*) # SGI C++ archive_cmds_CXX='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' # Archives containing C++ object files must be created using # "CC -ar", where "CC" is the IRIX C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. old_archive_cmds_CXX='$CC -ar -WR,-u -o $oldlib $oldobjs' ;; *) if test yes = "$GXX"; then if test no = "$with_gnu_ld"; then archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' else archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` -o $lib' fi fi link_all_deplibs_CXX=yes ;; esac hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir' hardcode_libdir_separator_CXX=: inherit_rpath_CXX=yes ;; linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in KCC*) # Kuck and Associates, Inc. (KAI) C++ Compiler # KCC will only create a shared library if the output file # ends with ".so" (or ".sl" for HP-UX), so rename the library # to its proper name (with version) after linking. archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' archive_expsym_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib $wl-retain-symbols-file,$export_symbols; mv \$templib $lib' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir' export_dynamic_flag_spec_CXX='$wl--export-dynamic' # Archives containing C++ object files must be created using # "CC -Bstatic", where "CC" is the KAI C++ compiler. old_archive_cmds_CXX='$CC -Bstatic -o $oldlib $oldobjs' ;; icpc* | ecpc* ) # Intel C++ with_gnu_ld=yes # version 8.0 and above of icpc choke on multiply defined symbols # if we add $predep_objects and $postdep_objects, however 7.1 and # earlier do not add the objects themselves. case `$CC -V 2>&1` in *"Version 7."*) archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; *) # Version 8.0 or newer tmp_idyn= case $host_cpu in ia64*) tmp_idyn=' -i_dynamic';; esac archive_cmds_CXX='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds_CXX='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; esac archive_cmds_need_lc_CXX=no hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir' export_dynamic_flag_spec_CXX='$wl--export-dynamic' whole_archive_flag_spec_CXX='$wl--whole-archive$convenience $wl--no-whole-archive' ;; pgCC* | pgcpp*) # Portland Group C++ compiler case `$CC -V` in *pgCC\ [1-5].* | *pgcpp\ [1-5].*) prelink_cmds_CXX='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~ compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"' old_archive_cmds_CXX='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~ $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~ $RANLIB $oldlib' archive_cmds_CXX='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds_CXX='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; *) # Version 6 and above use weak symbols archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; esac hardcode_libdir_flag_spec_CXX='$wl--rpath $wl$libdir' export_dynamic_flag_spec_CXX='$wl--export-dynamic' whole_archive_flag_spec_CXX='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' ;; cxx*) # Compaq C++ archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib $wl-retain-symbols-file $wl$export_symbols' runpath_var=LD_RUN_PATH hardcode_libdir_flag_spec_CXX='-rpath $libdir' hardcode_libdir_separator_CXX=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "X$list" | $Xsed' ;; xl* | mpixl* | bgxl*) # IBM XL 8.0 on PPC, with GNU ld hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir' export_dynamic_flag_spec_CXX='$wl--export-dynamic' archive_cmds_CXX='$CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' if test yes = "$supports_anon_versioning"; then archive_expsym_cmds_CXX='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib' fi ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 no_undefined_flag_CXX=' -zdefs' archive_cmds_CXX='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' archive_expsym_cmds_CXX='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file $wl$export_symbols' hardcode_libdir_flag_spec_CXX='-R$libdir' whole_archive_flag_spec_CXX='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' compiler_needs_object_CXX=yes # Not sure whether something based on # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 # would be better. output_verbose_link_cmd='func_echo_all' # Archives containing C++ object files must be created using # "CC -xar", where "CC" is the Sun C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. old_archive_cmds_CXX='$CC -xar -o $oldlib $oldobjs' ;; esac ;; esac ;; lynxos*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; m88k*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; mvs*) case $cc_basename in cxx*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; *) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; esac ;; netbsd*) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then archive_cmds_CXX='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' wlarc= hardcode_libdir_flag_spec_CXX='-R$libdir' hardcode_direct_CXX=yes hardcode_shlibpath_var_CXX=no fi # Workaround some broken pre-1.5 toolchains output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"' ;; *nto* | *qnx*) ld_shlibs_CXX=yes ;; openbsd* | bitrig*) if test -f /usr/libexec/ld.so; then hardcode_direct_CXX=yes hardcode_shlibpath_var_CXX=no hardcode_direct_absolute_CXX=yes archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir' if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`"; then archive_expsym_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file,$export_symbols -o $lib' export_dynamic_flag_spec_CXX='$wl-E' whole_archive_flag_spec_CXX=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' fi output_verbose_link_cmd=func_echo_all else ld_shlibs_CXX=no fi ;; osf3* | osf4* | osf5*) case $cc_basename in KCC*) # Kuck and Associates, Inc. (KAI) C++ Compiler # KCC will only create a shared library if the output file # ends with ".so" (or ".sl" for HP-UX), so rename the library # to its proper name (with version) after linking. archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir' hardcode_libdir_separator_CXX=: # Archives containing C++ object files must be created using # the KAI C++ compiler. case $host in osf3*) old_archive_cmds_CXX='$CC -Bstatic -o $oldlib $oldobjs' ;; *) old_archive_cmds_CXX='$CC -o $oldlib $oldobjs' ;; esac ;; RCC*) # Rational C++ 2.4.1 # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; cxx*) case $host in osf3*) allow_undefined_flag_CXX=' $wl-expect_unresolved $wl\*' archive_cmds_CXX='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $soname `test -n "$verstring" && func_echo_all "$wl-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir' ;; *) allow_undefined_flag_CXX=' -expect_unresolved \*' archive_cmds_CXX='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' archive_expsym_cmds_CXX='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ echo "-hidden">> $lib.exp~ $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname $wl-input $wl$lib.exp `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~ $RM $lib.exp' hardcode_libdir_flag_spec_CXX='-rpath $libdir' ;; esac hardcode_libdir_separator_CXX=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test yes,no = "$GXX,$with_gnu_ld"; then allow_undefined_flag_CXX=' $wl-expect_unresolved $wl\*' case $host in osf3*) archive_cmds_CXX='$CC -shared -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' ;; *) archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' ;; esac hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir' hardcode_libdir_separator_CXX=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"' else # FIXME: insert proper C++ library support ld_shlibs_CXX=no fi ;; esac ;; psos*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; sunos4*) case $cc_basename in CC*) # Sun C++ 4.x # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; lcc*) # Lucid # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; *) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; esac ;; solaris*) case $cc_basename in CC* | sunCC*) # Sun C++ 4.2, 5.x and Centerline C++ archive_cmds_need_lc_CXX=yes no_undefined_flag_CXX=' -zdefs' archive_cmds_CXX='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G$allow_undefined_flag $wl-M $wl$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' hardcode_libdir_flag_spec_CXX='-R$libdir' hardcode_shlibpath_var_CXX=no case $host_os in solaris2.[0-5] | solaris2.[0-5].*) ;; *) # The compiler driver will combine and reorder linker options, # but understands '-z linker_flag'. # Supported since Solaris 2.6 (maybe 2.5.1?) whole_archive_flag_spec_CXX='-z allextract$convenience -z defaultextract' ;; esac link_all_deplibs_CXX=yes output_verbose_link_cmd='func_echo_all' # Archives containing C++ object files must be created using # "CC -xar", where "CC" is the Sun C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. old_archive_cmds_CXX='$CC -xar -o $oldlib $oldobjs' ;; gcx*) # Green Hills C++ Compiler archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' # The C++ compiler must be used to create the archive. old_archive_cmds_CXX='$CC $LDFLAGS -archive -o $oldlib $oldobjs' ;; *) # GNU C++ compiler with Solaris linker if test yes,no = "$GXX,$with_gnu_ld"; then no_undefined_flag_CXX=' $wl-z ${wl}defs' if $CC --version | $GREP -v '^2\.7' > /dev/null; then archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -shared $pic_flag -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"' else # g++ 2.7 appears to require '-G' NOT '-shared' on this # platform. archive_cmds_CXX='$CC -G -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"' fi hardcode_libdir_flag_spec_CXX='$wl-R $wl$libdir' case $host_os in solaris2.[0-5] | solaris2.[0-5].*) ;; *) whole_archive_flag_spec_CXX='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract' ;; esac fi ;; esac ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) no_undefined_flag_CXX='$wl-z,text' archive_cmds_need_lc_CXX=no hardcode_shlibpath_var_CXX=no runpath_var='LD_RUN_PATH' case $cc_basename in CC*) archive_cmds_CXX='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds_CXX='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; *) archive_cmds_CXX='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds_CXX='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; sysv5* | sco3.2v5* | sco5v6*) # Note: We CANNOT use -z defs as we might desire, because we do not # link with -lc, and that would cause any symbols used from libc to # always be unresolved, which means just about no library would # ever link correctly. If we're not using GNU ld we use -z text # though, which does catch some bad symbols but isn't as heavy-handed # as -z defs. no_undefined_flag_CXX='$wl-z,text' allow_undefined_flag_CXX='$wl-z,nodefs' archive_cmds_need_lc_CXX=no hardcode_shlibpath_var_CXX=no hardcode_libdir_flag_spec_CXX='$wl-R,$libdir' hardcode_libdir_separator_CXX=':' link_all_deplibs_CXX=yes export_dynamic_flag_spec_CXX='$wl-Bexport' runpath_var='LD_RUN_PATH' case $cc_basename in CC*) archive_cmds_CXX='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds_CXX='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' old_archive_cmds_CXX='$CC -Tprelink_objects $oldobjs~ '"$old_archive_cmds_CXX" reload_cmds_CXX='$CC -Tprelink_objects $reload_objs~ '"$reload_cmds_CXX" ;; *) archive_cmds_CXX='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds_CXX='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; tandem*) case $cc_basename in NCC*) # NonStop-UX NCC 3.20 # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; *) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; esac ;; vxworks*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; *) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs_CXX" >&5 $as_echo "$ld_shlibs_CXX" >&6; } test no = "$ld_shlibs_CXX" && can_build_shared=no GCC_CXX=$GXX LD_CXX=$LD ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... # Dependencies to place before and after the object being linked: predep_objects_CXX= postdep_objects_CXX= predeps_CXX= postdeps_CXX= compiler_lib_search_path_CXX= cat > conftest.$ac_ext <<_LT_EOF class Foo { public: Foo (void) { a = 0; } private: int a; }; _LT_EOF _lt_libdeps_save_CFLAGS=$CFLAGS case "$CC $CFLAGS " in #( *\ -flto*\ *) CFLAGS="$CFLAGS -fno-lto" ;; *\ -fwhopr*\ *) CFLAGS="$CFLAGS -fno-whopr" ;; *\ -fuse-linker-plugin*\ *) CFLAGS="$CFLAGS -fno-use-linker-plugin" ;; esac if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then # Parse the compiler output and extract the necessary # objects, libraries and library flags. # Sentinel used to keep track of whether or not we are before # the conftest object file. pre_test_object_deps_done=no for p in `eval "$output_verbose_link_cmd"`; do case $prev$p in -L* | -R* | -l*) # Some compilers place space between "-{L,R}" and the path. # Remove the space. if test x-L = "$p" || test x-R = "$p"; then prev=$p continue fi # Expand the sysroot to ease extracting the directories later. if test -z "$prev"; then case $p in -L*) func_stripname_cnf '-L' '' "$p"; prev=-L; p=$func_stripname_result ;; -R*) func_stripname_cnf '-R' '' "$p"; prev=-R; p=$func_stripname_result ;; -l*) func_stripname_cnf '-l' '' "$p"; prev=-l; p=$func_stripname_result ;; esac fi case $p in =*) func_stripname_cnf '=' '' "$p"; p=$lt_sysroot$func_stripname_result ;; esac if test no = "$pre_test_object_deps_done"; then case $prev in -L | -R) # Internal compiler library paths should come after those # provided the user. The postdeps already come after the # user supplied libs so there is no need to process them. if test -z "$compiler_lib_search_path_CXX"; then compiler_lib_search_path_CXX=$prev$p else compiler_lib_search_path_CXX="${compiler_lib_search_path_CXX} $prev$p" fi ;; # The "-l" case would never come before the object being # linked, so don't bother handling this case. esac else if test -z "$postdeps_CXX"; then postdeps_CXX=$prev$p else postdeps_CXX="${postdeps_CXX} $prev$p" fi fi prev= ;; *.lto.$objext) ;; # Ignore GCC LTO objects *.$objext) # This assumes that the test object file only shows up # once in the compiler output. if test "$p" = "conftest.$objext"; then pre_test_object_deps_done=yes continue fi if test no = "$pre_test_object_deps_done"; then if test -z "$predep_objects_CXX"; then predep_objects_CXX=$p else predep_objects_CXX="$predep_objects_CXX $p" fi else if test -z "$postdep_objects_CXX"; then postdep_objects_CXX=$p else postdep_objects_CXX="$postdep_objects_CXX $p" fi fi ;; *) ;; # Ignore the rest. esac done # Clean up. rm -f a.out a.exe else echo "libtool.m4: error: problem compiling CXX test program" fi $RM -f confest.$objext CFLAGS=$_lt_libdeps_save_CFLAGS # PORTME: override above test on systems where it is broken case $host_os in interix[3-9]*) # Interix 3.5 installs completely hosed .la files for C++, so rather than # hack all around it, let's just trust "g++" to DTRT. predep_objects_CXX= postdep_objects_CXX= postdeps_CXX= ;; esac case " $postdeps_CXX " in *" -lc "*) archive_cmds_need_lc_CXX=no ;; esac compiler_lib_search_dirs_CXX= if test -n "${compiler_lib_search_path_CXX}"; then compiler_lib_search_dirs_CXX=`echo " ${compiler_lib_search_path_CXX}" | $SED -e 's! -L! !g' -e 's!^ !!'` fi lt_prog_compiler_wl_CXX= lt_prog_compiler_pic_CXX= lt_prog_compiler_static_CXX= # C++ specific cases for pic, static, wl, etc. if test yes = "$GXX"; then lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_static_CXX='-static' case $host_os in aix*) # All AIX code is PIC. if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor lt_prog_compiler_static_CXX='-Bstatic' fi lt_prog_compiler_pic_CXX='-fPIC' ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support lt_prog_compiler_pic_CXX='-fPIC' ;; m68k) # FIXME: we need at least 68020 code to build shared libraries, but # adding the '-m68020' flag to GCC prevents building anything better, # like '-m68040'. lt_prog_compiler_pic_CXX='-m68020 -resident32 -malways-restore-a4' ;; esac ;; beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) # PIC is the default for these OSes. ;; mingw* | cygwin* | os2* | pw32* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). # Although the cygwin gcc ignores -fPIC, still need this for old-style # (--disable-auto-import) libraries lt_prog_compiler_pic_CXX='-DDLL_EXPORT' case $host_os in os2*) lt_prog_compiler_static_CXX='$wl-static' ;; esac ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files lt_prog_compiler_pic_CXX='-fno-common' ;; *djgpp*) # DJGPP does not support shared libraries at all lt_prog_compiler_pic_CXX= ;; haiku*) # PIC is the default for Haiku. # The "-static" flag exists, but is broken. lt_prog_compiler_static_CXX= ;; interix[3-9]*) # Interix 3.x gcc -fpic/-fPIC options generate broken code. # Instead, we relocate shared libraries at runtime. ;; sysv4*MP*) if test -d /usr/nec; then lt_prog_compiler_pic_CXX=-Kconform_pic fi ;; hpux*) # PIC is the default for 64-bit PA HP-UX, but not for 32-bit # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag # sets the default TLS model and affects inlining. case $host_cpu in hppa*64*) ;; *) lt_prog_compiler_pic_CXX='-fPIC' ;; esac ;; *qnx* | *nto*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. lt_prog_compiler_pic_CXX='-fPIC -shared' ;; *) lt_prog_compiler_pic_CXX='-fPIC' ;; esac else case $host_os in aix[4-9]*) # All AIX code is PIC. if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor lt_prog_compiler_static_CXX='-Bstatic' else lt_prog_compiler_static_CXX='-bnso -bI:/lib/syscalls.exp' fi ;; chorus*) case $cc_basename in cxch68*) # Green Hills C++ Compiler # _LT_TAGVAR(lt_prog_compiler_static, CXX)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a" ;; esac ;; mingw* | cygwin* | os2* | pw32* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). lt_prog_compiler_pic_CXX='-DDLL_EXPORT' ;; dgux*) case $cc_basename in ec++*) lt_prog_compiler_pic_CXX='-KPIC' ;; ghcx*) # Green Hills C++ Compiler lt_prog_compiler_pic_CXX='-pic' ;; *) ;; esac ;; freebsd* | dragonfly*) # FreeBSD uses GNU C++ ;; hpux9* | hpux10* | hpux11*) case $cc_basename in CC*) lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_static_CXX='$wl-a ${wl}archive' if test ia64 != "$host_cpu"; then lt_prog_compiler_pic_CXX='+Z' fi ;; aCC*) lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_static_CXX='$wl-a ${wl}archive' case $host_cpu in hppa*64*|ia64*) # +Z the default ;; *) lt_prog_compiler_pic_CXX='+Z' ;; esac ;; *) ;; esac ;; interix*) # This is c89, which is MS Visual C++ (no shared libs) # Anyone wants to do a port? ;; irix5* | irix6* | nonstopux*) case $cc_basename in CC*) lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_static_CXX='-non_shared' # CC pic flag -KPIC is the default. ;; *) ;; esac ;; linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in KCC*) # KAI C++ Compiler lt_prog_compiler_wl_CXX='--backend -Wl,' lt_prog_compiler_pic_CXX='-fPIC' ;; ecpc* ) # old Intel C++ for x86_64, which still supported -KPIC. lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_pic_CXX='-KPIC' lt_prog_compiler_static_CXX='-static' ;; icpc* ) # Intel C++, used to be incompatible with GCC. # ICC 10 doesn't accept -KPIC any more. lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_pic_CXX='-fPIC' lt_prog_compiler_static_CXX='-static' ;; pgCC* | pgcpp*) # Portland Group C++ compiler lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_pic_CXX='-fpic' lt_prog_compiler_static_CXX='-Bstatic' ;; cxx*) # Compaq C++ # Make sure the PIC flag is empty. It appears that all Alpha # Linux and Compaq Tru64 Unix objects are PIC. lt_prog_compiler_pic_CXX= lt_prog_compiler_static_CXX='-non_shared' ;; xlc* | xlC* | bgxl[cC]* | mpixl[cC]*) # IBM XL 8.0, 9.0 on PPC and BlueGene lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_pic_CXX='-qpic' lt_prog_compiler_static_CXX='-qstaticlink' ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 lt_prog_compiler_pic_CXX='-KPIC' lt_prog_compiler_static_CXX='-Bstatic' lt_prog_compiler_wl_CXX='-Qoption ld ' ;; esac ;; esac ;; lynxos*) ;; m88k*) ;; mvs*) case $cc_basename in cxx*) lt_prog_compiler_pic_CXX='-W c,exportall' ;; *) ;; esac ;; netbsd* | netbsdelf*-gnu) ;; *qnx* | *nto*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. lt_prog_compiler_pic_CXX='-fPIC -shared' ;; osf3* | osf4* | osf5*) case $cc_basename in KCC*) lt_prog_compiler_wl_CXX='--backend -Wl,' ;; RCC*) # Rational C++ 2.4.1 lt_prog_compiler_pic_CXX='-pic' ;; cxx*) # Digital/Compaq C++ lt_prog_compiler_wl_CXX='-Wl,' # Make sure the PIC flag is empty. It appears that all Alpha # Linux and Compaq Tru64 Unix objects are PIC. lt_prog_compiler_pic_CXX= lt_prog_compiler_static_CXX='-non_shared' ;; *) ;; esac ;; psos*) ;; solaris*) case $cc_basename in CC* | sunCC*) # Sun C++ 4.2, 5.x and Centerline C++ lt_prog_compiler_pic_CXX='-KPIC' lt_prog_compiler_static_CXX='-Bstatic' lt_prog_compiler_wl_CXX='-Qoption ld ' ;; gcx*) # Green Hills C++ Compiler lt_prog_compiler_pic_CXX='-PIC' ;; *) ;; esac ;; sunos4*) case $cc_basename in CC*) # Sun C++ 4.x lt_prog_compiler_pic_CXX='-pic' lt_prog_compiler_static_CXX='-Bstatic' ;; lcc*) # Lucid lt_prog_compiler_pic_CXX='-pic' ;; *) ;; esac ;; sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) case $cc_basename in CC*) lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_pic_CXX='-KPIC' lt_prog_compiler_static_CXX='-Bstatic' ;; esac ;; tandem*) case $cc_basename in NCC*) # NonStop-UX NCC 3.20 lt_prog_compiler_pic_CXX='-KPIC' ;; *) ;; esac ;; vxworks*) ;; *) lt_prog_compiler_can_build_shared_CXX=no ;; esac fi case $host_os in # For platforms that do not support PIC, -DPIC is meaningless: *djgpp*) lt_prog_compiler_pic_CXX= ;; *) lt_prog_compiler_pic_CXX="$lt_prog_compiler_pic_CXX -DPIC" ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5 $as_echo_n "checking for $compiler option to produce PIC... " >&6; } if ${lt_cv_prog_compiler_pic_CXX+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_pic_CXX=$lt_prog_compiler_pic_CXX fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_CXX" >&5 $as_echo "$lt_cv_prog_compiler_pic_CXX" >&6; } lt_prog_compiler_pic_CXX=$lt_cv_prog_compiler_pic_CXX # # Check to make sure the PIC flag actually works. # if test -n "$lt_prog_compiler_pic_CXX"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works" >&5 $as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works... " >&6; } if ${lt_cv_prog_compiler_pic_works_CXX+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_pic_works_CXX=no ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="$lt_prog_compiler_pic_CXX -DPIC" ## exclude from sc_useless_quotes_in_assignment # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. # The option is referenced via a variable to avoid confusing sed. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler_pic_works_CXX=yes fi fi $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works_CXX" >&5 $as_echo "$lt_cv_prog_compiler_pic_works_CXX" >&6; } if test yes = "$lt_cv_prog_compiler_pic_works_CXX"; then case $lt_prog_compiler_pic_CXX in "" | " "*) ;; *) lt_prog_compiler_pic_CXX=" $lt_prog_compiler_pic_CXX" ;; esac else lt_prog_compiler_pic_CXX= lt_prog_compiler_can_build_shared_CXX=no fi fi # # Check to make sure the static flag actually works. # wl=$lt_prog_compiler_wl_CXX eval lt_tmp_static_flag=\"$lt_prog_compiler_static_CXX\" { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5 $as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; } if ${lt_cv_prog_compiler_static_works_CXX+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_static_works_CXX=no save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS $lt_tmp_static_flag" echo "$lt_simple_link_test_code" > conftest.$ac_ext if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then # The linker can only warn and ignore the option if not recognized # So say no if there are warnings if test -s conftest.err; then # Append any errors to the config.log. cat conftest.err 1>&5 $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler_static_works_CXX=yes fi else lt_cv_prog_compiler_static_works_CXX=yes fi fi $RM -r conftest* LDFLAGS=$save_LDFLAGS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works_CXX" >&5 $as_echo "$lt_cv_prog_compiler_static_works_CXX" >&6; } if test yes = "$lt_cv_prog_compiler_static_works_CXX"; then : else lt_prog_compiler_static_CXX= fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 $as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } if ${lt_cv_prog_compiler_c_o_CXX+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_c_o_CXX=no $RM -r conftest 2>/dev/null mkdir conftest cd conftest mkdir out echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-o out/conftest2.$ac_objext" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then lt_cv_prog_compiler_c_o_CXX=yes fi fi chmod u+w . 2>&5 $RM conftest* # SGI C++ compiler will create directory out/ii_files/ for # template instantiation test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files $RM out/* && rmdir out cd .. $RM -r conftest $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o_CXX" >&5 $as_echo "$lt_cv_prog_compiler_c_o_CXX" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 $as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } if ${lt_cv_prog_compiler_c_o_CXX+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_c_o_CXX=no $RM -r conftest 2>/dev/null mkdir conftest cd conftest mkdir out echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-o out/conftest2.$ac_objext" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then lt_cv_prog_compiler_c_o_CXX=yes fi fi chmod u+w . 2>&5 $RM conftest* # SGI C++ compiler will create directory out/ii_files/ for # template instantiation test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files $RM out/* && rmdir out cd .. $RM -r conftest $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o_CXX" >&5 $as_echo "$lt_cv_prog_compiler_c_o_CXX" >&6; } hard_links=nottested if test no = "$lt_cv_prog_compiler_c_o_CXX" && test no != "$need_locks"; then # do not overwrite the value of need_locks provided by the user { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5 $as_echo_n "checking if we can lock with hard links... " >&6; } hard_links=yes $RM conftest* ln conftest.a conftest.b 2>/dev/null && hard_links=no touch conftest.a ln conftest.a conftest.b 2>&5 || hard_links=no ln conftest.a conftest.b 2>/dev/null && hard_links=no { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5 $as_echo "$hard_links" >&6; } if test no = "$hard_links"; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&5 $as_echo "$as_me: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&2;} need_locks=warn fi else need_locks=no fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 $as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' exclude_expsyms_CXX='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*' case $host_os in aix[4-9]*) # If we're using GNU nm, then we don't want the "-C" option. # -C means demangle to GNU nm, but means don't demangle to AIX nm. # Without the "-l" option, or with the "-B" option, AIX nm treats # weak defined symbols like other global defined symbols, whereas # GNU nm marks them as "W". # While the 'weak' keyword is ignored in the Export File, we need # it in the Import File for the 'aix-soname' feature, so we have # to replace the "-B" option with "-P" for AIX nm. if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then export_symbols_cmds_CXX='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' else export_symbols_cmds_CXX='`func_echo_all $NM | $SED -e '\''s/B\([^B]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && (substr(\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' fi ;; pw32*) export_symbols_cmds_CXX=$ltdll_cmds ;; cygwin* | mingw* | cegcc*) case $cc_basename in cl*) exclude_expsyms_CXX='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' ;; *) export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols' exclude_expsyms_CXX='[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname' ;; esac ;; linux* | k*bsd*-gnu | gnu*) link_all_deplibs_CXX=no ;; *) export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs_CXX" >&5 $as_echo "$ld_shlibs_CXX" >&6; } test no = "$ld_shlibs_CXX" && can_build_shared=no with_gnu_ld_CXX=$with_gnu_ld # # Do we need to explicitly link libc? # case "x$archive_cmds_need_lc_CXX" in x|xyes) # Assume -lc should be added archive_cmds_need_lc_CXX=yes if test yes,yes = "$GCC,$enable_shared"; then case $archive_cmds_CXX in *'~'*) # FIXME: we may have to deal with multi-command sequences. ;; '$CC '*) # Test whether the compiler implicitly links with -lc since on some # systems, -lgcc has to come before -lc. If gcc already passes -lc # to ld, don't add -lc before -lgcc. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5 $as_echo_n "checking whether -lc should be explicitly linked in... " >&6; } if ${lt_cv_archive_cmds_need_lc_CXX+:} false; then : $as_echo_n "(cached) " >&6 else $RM conftest* echo "$lt_simple_compile_test_code" > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } 2>conftest.err; then soname=conftest lib=conftest libobjs=conftest.$ac_objext deplibs= wl=$lt_prog_compiler_wl_CXX pic_flag=$lt_prog_compiler_pic_CXX compiler_flags=-v linker_flags=-v verstring= output_objdir=. libname=conftest lt_save_allow_undefined_flag=$allow_undefined_flag_CXX allow_undefined_flag_CXX= if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds_CXX 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5 (eval $archive_cmds_CXX 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then lt_cv_archive_cmds_need_lc_CXX=no else lt_cv_archive_cmds_need_lc_CXX=yes fi allow_undefined_flag_CXX=$lt_save_allow_undefined_flag else cat conftest.err 1>&5 fi $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc_CXX" >&5 $as_echo "$lt_cv_archive_cmds_need_lc_CXX" >&6; } archive_cmds_need_lc_CXX=$lt_cv_archive_cmds_need_lc_CXX ;; esac fi ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5 $as_echo_n "checking dynamic linker characteristics... " >&6; } library_names_spec= libname_spec='lib$name' soname_spec= shrext_cmds=.so postinstall_cmds= postuninstall_cmds= finish_cmds= finish_eval= shlibpath_var= shlibpath_overrides_runpath=unknown version_type=none dynamic_linker="$host_os ld.so" sys_lib_dlsearch_path_spec="/lib /usr/lib" need_lib_prefix=unknown hardcode_into_libs=no # when you set need_version to no, make sure it does not cause -set_version # flags to be left without arguments need_version=unknown case $host_os in aix3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname.a' shlibpath_var=LIBPATH # AIX 3 has no versioning support, so we append a major version to the name. soname_spec='$libname$release$shared_ext$major' ;; aix[4-9]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no hardcode_into_libs=yes if test ia64 = "$host_cpu"; then # AIX 5 supports IA64 library_names_spec='$libname$release$shared_ext$major $libname$release$shared_ext$versuffix $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH else # With GCC up to 2.95.x, collect2 would create an import file # for dependence libraries. The import file would start with # the line '#! .'. This would cause the generated library to # depend on '.', always an invalid library. This was fixed in # development snapshots of GCC prior to 3.0. case $host_os in aix4 | aix4.[01] | aix4.[01].*) if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' echo ' yes ' echo '#endif'; } | $CC -E - | $GREP yes > /dev/null; then : else can_build_shared=no fi ;; esac # Using Import Files as archive members, it is possible to support # filename-based versioning of shared library archives on AIX. While # this would work for both with and without runtime linking, it will # prevent static linking of such archives. So we do filename-based # shared library versioning with .so extension only, which is used # when both runtime linking and shared linking is enabled. # Unfortunately, runtime linking may impact performance, so we do # not want this to be the default eventually. Also, we use the # versioned .so libs for executables only if there is the -brtl # linker flag in LDFLAGS as well, or --with-aix-soname=svr4 only. # To allow for filename-based versioning support, we need to create # libNAME.so.V as an archive file, containing: # *) an Import File, referring to the versioned filename of the # archive as well as the shared archive member, telling the # bitwidth (32 or 64) of that shared object, and providing the # list of exported symbols of that shared object, eventually # decorated with the 'weak' keyword # *) the shared object with the F_LOADONLY flag set, to really avoid # it being seen by the linker. # At run time we better use the real file rather than another symlink, # but for link time we create the symlink libNAME.so -> libNAME.so.V case $with_aix_soname,$aix_use_runtimelinking in # AIX (on Power*) has no versioning support, so currently we cannot hardcode correct # soname into executable. Probably we can add versioning support to # collect2, so additional links can be useful in future. aix,yes) # traditional libtool dynamic_linker='AIX unversionable lib.so' # If using run time linking (on AIX 4.2 or later) use lib.so # instead of lib.a to let people know that these are not # typical AIX shared libraries. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' ;; aix,no) # traditional AIX only dynamic_linker='AIX lib.a(lib.so.V)' # We preserve .a as extension for shared libraries through AIX4.2 # and later when we are not doing run time linking. library_names_spec='$libname$release.a $libname.a' soname_spec='$libname$release$shared_ext$major' ;; svr4,*) # full svr4 only dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o)" library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' # We do not specify a path in Import Files, so LIBPATH fires. shlibpath_overrides_runpath=yes ;; *,yes) # both, prefer svr4 dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o), lib.a(lib.so.V)" library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' # unpreferred sharedlib libNAME.a needs extra handling postinstall_cmds='test -n "$linkname" || linkname="$realname"~func_stripname "" ".so" "$linkname"~$install_shared_prog "$dir/$func_stripname_result.$libext" "$destdir/$func_stripname_result.$libext"~test -z "$tstripme" || test -z "$striplib" || $striplib "$destdir/$func_stripname_result.$libext"' postuninstall_cmds='for n in $library_names $old_library; do :; done~func_stripname "" ".so" "$n"~test "$func_stripname_result" = "$n" || func_append rmfiles " $odir/$func_stripname_result.$libext"' # We do not specify a path in Import Files, so LIBPATH fires. shlibpath_overrides_runpath=yes ;; *,no) # both, prefer aix dynamic_linker="AIX lib.a(lib.so.V), lib.so.V($shared_archive_member_spec.o)" library_names_spec='$libname$release.a $libname.a' soname_spec='$libname$release$shared_ext$major' # unpreferred sharedlib libNAME.so.V and symlink libNAME.so need extra handling postinstall_cmds='test -z "$dlname" || $install_shared_prog $dir/$dlname $destdir/$dlname~test -z "$tstripme" || test -z "$striplib" || $striplib $destdir/$dlname~test -n "$linkname" || linkname=$realname~func_stripname "" ".a" "$linkname"~(cd "$destdir" && $LN_S -f $dlname $func_stripname_result.so)' postuninstall_cmds='test -z "$dlname" || func_append rmfiles " $odir/$dlname"~for n in $old_library $library_names; do :; done~func_stripname "" ".a" "$n"~func_append rmfiles " $odir/$func_stripname_result.so"' ;; esac shlibpath_var=LIBPATH fi ;; amigaos*) case $host_cpu in powerpc) # Since July 2007 AmigaOS4 officially supports .so libraries. # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' ;; m68k) library_names_spec='$libname.ixlibrary $libname.a' # Create ${libname}_ixlibrary.a entries in /sys/libs. finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' ;; esac ;; beos*) library_names_spec='$libname$shared_ext' dynamic_linker="$host_os ld.so" shlibpath_var=LIBRARY_PATH ;; bsdi[45]*) version_type=linux # correct to gnu/linux during the next big refactor need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" # the default ld.so.conf also contains /usr/contrib/lib and # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow # libtool to hard-code these into programs ;; cygwin* | mingw* | pw32* | cegcc*) version_type=windows shrext_cmds=.dll need_version=no need_lib_prefix=no case $GCC,$cc_basename in yes,*) # gcc library_names_spec='$libname.dll.a' # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes case $host_os in cygwin*) # Cygwin DLLs use 'cyg' prefix rather than 'lib' soname_spec='`echo $libname | sed -e 's/^lib/cyg/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' ;; mingw* | cegcc*) # MinGW DLLs use traditional 'lib' prefix soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' ;; pw32*) # pw32 DLLs use 'pw' prefix rather than 'lib' library_names_spec='`echo $libname | sed -e 's/^lib/pw/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' ;; esac dynamic_linker='Win32 ld.exe' ;; *,cl*) # Native MSVC libname_spec='$name' soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' library_names_spec='$libname.dll.lib' case $build_os in mingw*) sys_lib_search_path_spec= lt_save_ifs=$IFS IFS=';' for lt_path in $LIB do IFS=$lt_save_ifs # Let DOS variable expansion print the short 8.3 style file name. lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" done IFS=$lt_save_ifs # Convert to MSYS style. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([a-zA-Z]\\):| /\\1|g' -e 's|^ ||'` ;; cygwin*) # Convert to unix form, then to dos form, then back to unix form # but this time dos style (no spaces!) so that the unix form looks # like /cygdrive/c/PROGRA~1:/cygdr... sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` ;; *) sys_lib_search_path_spec=$LIB if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then # It is most probably a Windows format PATH. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` else sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` fi # FIXME: find the short name or the path components, as spaces are # common. (e.g. "Program Files" -> "PROGRA~1") ;; esac # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes dynamic_linker='Win32 link.exe' ;; *) # Assume MSVC wrapper library_names_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext $libname.lib' dynamic_linker='Win32 ld.exe' ;; esac # FIXME: first we should search . and the directory the executable is in shlibpath_var=PATH ;; darwin* | rhapsody*) dynamic_linker="$host_os dyld" version_type=darwin need_lib_prefix=no need_version=no library_names_spec='$libname$release$major$shared_ext $libname$shared_ext' soname_spec='$libname$release$major$shared_ext' shlibpath_overrides_runpath=yes shlibpath_var=DYLD_LIBRARY_PATH shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' ;; dgux*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH ;; freebsd* | dragonfly*) # DragonFly does not have aout. When/if they implement a new # versioning mechanism, adjust this. if test -x /usr/bin/objformat; then objformat=`/usr/bin/objformat` else case $host_os in freebsd[23].*) objformat=aout ;; *) objformat=elf ;; esac fi version_type=freebsd-$objformat case $version_type in freebsd-elf*) library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' need_version=no need_lib_prefix=no ;; freebsd-*) library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' need_version=yes ;; esac shlibpath_var=LD_LIBRARY_PATH case $host_os in freebsd2.*) shlibpath_overrides_runpath=yes ;; freebsd3.[01]* | freebsdelf3.[01]*) shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; freebsd3.[2-9]* | freebsdelf3.[2-9]* | \ freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1) shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; *) # from 4.6 on, and DragonFly shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; esac ;; haiku*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no dynamic_linker="$host_os runtime_loader" library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LIBRARY_PATH shlibpath_overrides_runpath=no sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' hardcode_into_libs=yes ;; hpux9* | hpux10* | hpux11*) # Give a soname corresponding to the major version so that dld.sl refuses to # link against other versions. version_type=sunos need_lib_prefix=no need_version=no case $host_cpu in ia64*) shrext_cmds='.so' hardcode_into_libs=yes dynamic_linker="$host_os dld.so" shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' if test 32 = "$HPUX_IA64_MODE"; then sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" sys_lib_dlsearch_path_spec=/usr/lib/hpux32 else sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" sys_lib_dlsearch_path_spec=/usr/lib/hpux64 fi ;; hppa*64*) shrext_cmds='.sl' hardcode_into_libs=yes dynamic_linker="$host_os dld.sl" shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; *) shrext_cmds='.sl' dynamic_linker="$host_os dld.sl" shlibpath_var=SHLIB_PATH shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' ;; esac # HP-UX runs *really* slowly unless shared libraries are mode 555, ... postinstall_cmds='chmod 555 $lib' # or fails outright, so override atomically: install_override_mode=555 ;; interix[3-9]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; irix5* | irix6* | nonstopux*) case $host_os in nonstopux*) version_type=nonstopux ;; *) if test yes = "$lt_cv_prog_gnu_ld"; then version_type=linux # correct to gnu/linux during the next big refactor else version_type=irix fi ;; esac need_lib_prefix=no need_version=no soname_spec='$libname$release$shared_ext$major' library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$release$shared_ext $libname$shared_ext' case $host_os in irix5* | nonstopux*) libsuff= shlibsuff= ;; *) case $LD in # libtool.m4 will add one of these switches to LD *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") libsuff= shlibsuff= libmagic=32-bit;; *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") libsuff=32 shlibsuff=N32 libmagic=N32;; *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") libsuff=64 shlibsuff=64 libmagic=64-bit;; *) libsuff= shlibsuff= libmagic=never-match;; esac ;; esac shlibpath_var=LD_LIBRARY${shlibsuff}_PATH shlibpath_overrides_runpath=no sys_lib_search_path_spec="/usr/lib$libsuff /lib$libsuff /usr/local/lib$libsuff" sys_lib_dlsearch_path_spec="/usr/lib$libsuff /lib$libsuff" hardcode_into_libs=yes ;; # No shared lib support for Linux oldld, aout, or coff. linux*oldld* | linux*aout* | linux*coff*) dynamic_linker=no ;; linux*android*) version_type=none # Android doesn't support versioned libraries. need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext' soname_spec='$libname$release$shared_ext' finish_cmds= shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes dynamic_linker='Android linker' # Don't embed -rpath directories since the linker doesn't support them. hardcode_libdir_flag_spec_CXX='-L$libdir' ;; # This must be glibc/ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no # Some binutils ld are patched to set DT_RUNPATH if ${lt_cv_shlibpath_overrides_runpath+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_shlibpath_overrides_runpath=no save_LDFLAGS=$LDFLAGS save_libdir=$libdir eval "libdir=/foo; wl=\"$lt_prog_compiler_wl_CXX\"; \ LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec_CXX\"" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then : lt_cv_shlibpath_overrides_runpath=yes fi fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LDFLAGS=$save_LDFLAGS libdir=$save_libdir fi shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes # Ideally, we could use ldconfig to report *all* directores which are # searched for libraries, however this is still not possible. Aside from not # being certain /sbin/ldconfig is available, command # 'ldconfig -N -X -v | grep ^/' on 64bit Fedora does not report /usr/lib64, # even though it is searched at run-time. Try to do the best guess by # appending ld.so.conf contents (and includes) to the search path. if test -f /etc/ld.so.conf; then lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" fi # We used to test for /lib/ld.so.1 and disable shared libraries on # powerpc, because MkLinux only supported shared libraries with the # GNU dynamic linker. Since this was broken with cross compilers, # most powerpc-linux boxes support dynamic linking these days and # people can always --disable-shared, the test was removed, and we # assume the GNU/Linux dynamic linker is in use. dynamic_linker='GNU/Linux ld.so' ;; netbsdelf*-gnu) version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='NetBSD ld.elf_so' ;; netbsd*) version_type=sunos need_lib_prefix=no need_version=no if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' dynamic_linker='NetBSD (a.out) ld.so' else library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' dynamic_linker='NetBSD ld.elf_so' fi shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; newsos6) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; *nto* | *qnx*) version_type=qnx need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='ldqnx.so' ;; openbsd* | bitrig*) version_type=sunos sys_lib_dlsearch_path_spec=/usr/lib need_lib_prefix=no if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then need_version=no else need_version=yes fi library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; os2*) libname_spec='$name' version_type=windows shrext_cmds=.dll need_version=no need_lib_prefix=no # OS/2 can only load a DLL with a base name of 8 characters or less. soname_spec='`test -n "$os2dllname" && libname="$os2dllname"; v=$($ECHO $release$versuffix | tr -d .-); n=$($ECHO $libname | cut -b -$((8 - ${#v})) | tr . _); $ECHO $n$v`$shared_ext' library_names_spec='${libname}_dll.$libext' dynamic_linker='OS/2 ld.exe' shlibpath_var=BEGINLIBPATH sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; $ECHO \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; $ECHO \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' ;; osf3* | osf4* | osf5*) version_type=osf need_lib_prefix=no need_version=no soname_spec='$libname$release$shared_ext$major' library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; rdos*) dynamic_linker=no ;; solaris*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes # ldd complains unless libraries are executable postinstall_cmds='chmod +x $lib' ;; sunos4*) version_type=sunos library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes if test yes = "$with_gnu_ld"; then need_lib_prefix=no fi need_version=yes ;; sysv4 | sysv4.3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH case $host_vendor in sni) shlibpath_overrides_runpath=no need_lib_prefix=no runpath_var=LD_RUN_PATH ;; siemens) need_lib_prefix=no ;; motorola) need_lib_prefix=no need_version=no shlibpath_overrides_runpath=no sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' ;; esac ;; sysv4*MP*) if test -d /usr/nec; then version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$shared_ext.$versuffix $libname$shared_ext.$major $libname$shared_ext' soname_spec='$libname$shared_ext.$major' shlibpath_var=LD_LIBRARY_PATH fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) version_type=sco need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes if test yes = "$with_gnu_ld"; then sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' else sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' case $host_os in sco3.2v5*) sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" ;; esac fi sys_lib_dlsearch_path_spec='/usr/lib' ;; tpf*) # TPF is a cross-target only. Preferred cross-host = GNU/Linux. version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; uts4*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH ;; *) dynamic_linker=no ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5 $as_echo "$dynamic_linker" >&6; } test no = "$dynamic_linker" && can_build_shared=no variables_saved_for_relink="PATH $shlibpath_var $runpath_var" if test yes = "$GCC"; then variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" fi if test set = "${lt_cv_sys_lib_search_path_spec+set}"; then sys_lib_search_path_spec=$lt_cv_sys_lib_search_path_spec fi if test set = "${lt_cv_sys_lib_dlsearch_path_spec+set}"; then sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec fi # remember unaugmented sys_lib_dlsearch_path content for libtool script decls... configure_time_dlsearch_path=$sys_lib_dlsearch_path_spec # ... but it needs LT_SYS_LIBRARY_PATH munging for other configure-time code func_munge_path_list sys_lib_dlsearch_path_spec "$LT_SYS_LIBRARY_PATH" # to be used as default LT_SYS_LIBRARY_PATH value in generated libtool configure_time_lt_sys_library_path=$LT_SYS_LIBRARY_PATH { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5 $as_echo_n "checking how to hardcode library paths into programs... " >&6; } hardcode_action_CXX= if test -n "$hardcode_libdir_flag_spec_CXX" || test -n "$runpath_var_CXX" || test yes = "$hardcode_automatic_CXX"; then # We can hardcode non-existent directories. if test no != "$hardcode_direct_CXX" && # If the only mechanism to avoid hardcoding is shlibpath_var, we # have to relink, otherwise we might link with an installed library # when we should be linking with a yet-to-be-installed one ## test no != "$_LT_TAGVAR(hardcode_shlibpath_var, CXX)" && test no != "$hardcode_minus_L_CXX"; then # Linking always hardcodes the temporary library directory. hardcode_action_CXX=relink else # We can link without hardcoding, and we can hardcode nonexisting dirs. hardcode_action_CXX=immediate fi else # We cannot hardcode anything, or else we can only hardcode existing # directories. hardcode_action_CXX=unsupported fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action_CXX" >&5 $as_echo "$hardcode_action_CXX" >&6; } if test relink = "$hardcode_action_CXX" || test yes = "$inherit_rpath_CXX"; then # Fast installation is not supported enable_fast_install=no elif test yes = "$shlibpath_overrides_runpath" || test no = "$enable_shared"; then # Fast installation is not necessary enable_fast_install=needless fi fi # test -n "$compiler" CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS LDCXX=$LD LD=$lt_save_LD GCC=$lt_save_GCC with_gnu_ld=$lt_save_with_gnu_ld lt_cv_path_LDCXX=$lt_cv_path_LD lt_cv_path_LD=$lt_save_path_LD lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld fi # test yes != "$_lt_caught_CXX_error" ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu LIBOPENMPT_LTVER_CURRENT=3 LIBOPENMPT_LTVER_REVISION=3 LIBOPENMPT_LTVER_AGE=3 $as_echo "#define MPT_SVNURL \"https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.6.1\"" >>confdefs.h $as_echo "#define MPT_SVNVERSION \"16764\"" >>confdefs.h $as_echo "#define MPT_SVNDATE \"2022-01-30T16:49:19.812343Z\"" >>confdefs.h $as_echo "#define MPT_PACKAGE true" >>confdefs.h case $host_os in mingw32*) LIBOPENMPT_WIN32_LIBS="-lole32 -lrpcrt4" LIBOPENMPT_LIBS_PRIVATE_WIN32="-lole32 -lrpcrt4" OPENMPT123_WIN32_LIBS=-lwinmm CXXFLAGS="$CXXFLAGS -municode -mthreads" CFLAGS="$CFLAGS -municode -mthreads" WIN32_CONSOLE_CXXFLAGS=-mconsole WIN32_CONSOLE_CFLAGS=-mconsole ;; *) LIBOPENMPT_WIN32_LIBS= LIBOPENMPT_LIBS_PRIVATE_WIN32= OPENMPT123_WIN32_LIBS= WIN32_CONSOLE_CXXFLAGS= WIN32_CONSOLE_CFLAGS= ;; esac LIBOPENMPT_REQUIRES_PRIVATE= LIBOPENMPT_LIBS_PRIVATE= # Required libopenmpt dependency: zlib ZLIB_PKG= # Check whether --with-zlib was given. if test "${with_zlib+set}" = set; then : withval=$with_zlib; fi if test "x$with_zlib" != "xno"; then : pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for zlib" >&5 $as_echo_n "checking for zlib... " >&6; } if test -n "$ZLIB_CFLAGS"; then pkg_cv_ZLIB_CFLAGS="$ZLIB_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"zlib\""; } >&5 ($PKG_CONFIG --exists --print-errors "zlib") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_ZLIB_CFLAGS=`$PKG_CONFIG --cflags "zlib" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$ZLIB_LIBS"; then pkg_cv_ZLIB_LIBS="$ZLIB_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"zlib\""; } >&5 ($PKG_CONFIG --exists --print-errors "zlib") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_ZLIB_LIBS=`$PKG_CONFIG --libs "zlib" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then ZLIB_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "zlib" 2>&1` else ZLIB_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "zlib" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$ZLIB_PKG_ERRORS" >&5 as_fn_error $? "Unable to find zlib." "$LINENO" 5 elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } as_fn_error $? "Unable to find zlib." "$LINENO" 5 else ZLIB_CFLAGS=$pkg_cv_ZLIB_CFLAGS ZLIB_LIBS=$pkg_cv_ZLIB_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } ZLIB_PKG=zlib $as_echo "#define MPT_WITH_ZLIB /**/" >>confdefs.h fi fi # Required libopenmpt dependency: mpg123 MPG123_PKG= # Check whether --with-mpg123 was given. if test "${with_mpg123+set}" = set; then : withval=$with_mpg123; fi if test "x$with_mpg123" != "xno"; then : pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libmpg123 >= 1.14.0" >&5 $as_echo_n "checking for libmpg123 >= 1.14.0... " >&6; } if test -n "$MPG123_CFLAGS"; then pkg_cv_MPG123_CFLAGS="$MPG123_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libmpg123 >= 1.14.0\""; } >&5 ($PKG_CONFIG --exists --print-errors "libmpg123 >= 1.14.0") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_MPG123_CFLAGS=`$PKG_CONFIG --cflags "libmpg123 >= 1.14.0" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$MPG123_LIBS"; then pkg_cv_MPG123_LIBS="$MPG123_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libmpg123 >= 1.14.0\""; } >&5 ($PKG_CONFIG --exists --print-errors "libmpg123 >= 1.14.0") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_MPG123_LIBS=`$PKG_CONFIG --libs "libmpg123 >= 1.14.0" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then MPG123_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libmpg123 >= 1.14.0" 2>&1` else MPG123_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libmpg123 >= 1.14.0" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$MPG123_PKG_ERRORS" >&5 as_fn_error $? "Unable to find libmpg123." "$LINENO" 5 elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } as_fn_error $? "Unable to find libmpg123." "$LINENO" 5 else MPG123_CFLAGS=$pkg_cv_MPG123_CFLAGS MPG123_LIBS=$pkg_cv_MPG123_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } MPG123_PKG=libmpg123 $as_echo "#define MPT_WITH_MPG123 /**/" >>confdefs.h fi fi # Required libopenmpt dependency: ogg OGG_PKG= # Check whether --with-ogg was given. if test "${with_ogg+set}" = set; then : withval=$with_ogg; fi if test "x$with_ogg" != "xno"; then : pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ogg" >&5 $as_echo_n "checking for ogg... " >&6; } if test -n "$OGG_CFLAGS"; then pkg_cv_OGG_CFLAGS="$OGG_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"ogg\""; } >&5 ($PKG_CONFIG --exists --print-errors "ogg") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_OGG_CFLAGS=`$PKG_CONFIG --cflags "ogg" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$OGG_LIBS"; then pkg_cv_OGG_LIBS="$OGG_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"ogg\""; } >&5 ($PKG_CONFIG --exists --print-errors "ogg") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_OGG_LIBS=`$PKG_CONFIG --libs "ogg" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then OGG_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "ogg" 2>&1` else OGG_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "ogg" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$OGG_PKG_ERRORS" >&5 as_fn_error $? "Unable to find libogg." "$LINENO" 5 elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } as_fn_error $? "Unable to find libogg." "$LINENO" 5 else OGG_CFLAGS=$pkg_cv_OGG_CFLAGS OGG_LIBS=$pkg_cv_OGG_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } OGG_PKG=ogg $as_echo "#define MPT_WITH_OGG /**/" >>confdefs.h fi fi # Required libopenmpt dependency: vorbis VORBIS_PKG= # Check whether --with-vorbis was given. if test "${with_vorbis+set}" = set; then : withval=$with_vorbis; fi if test "x$with_vorbis" != "xno"; then : pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for vorbis" >&5 $as_echo_n "checking for vorbis... " >&6; } if test -n "$VORBIS_CFLAGS"; then pkg_cv_VORBIS_CFLAGS="$VORBIS_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"vorbis\""; } >&5 ($PKG_CONFIG --exists --print-errors "vorbis") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_VORBIS_CFLAGS=`$PKG_CONFIG --cflags "vorbis" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$VORBIS_LIBS"; then pkg_cv_VORBIS_LIBS="$VORBIS_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"vorbis\""; } >&5 ($PKG_CONFIG --exists --print-errors "vorbis") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_VORBIS_LIBS=`$PKG_CONFIG --libs "vorbis" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then VORBIS_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "vorbis" 2>&1` else VORBIS_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "vorbis" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$VORBIS_PKG_ERRORS" >&5 as_fn_error $? "Unable to find libvorbis." "$LINENO" 5 elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } as_fn_error $? "Unable to find libvorbis." "$LINENO" 5 else VORBIS_CFLAGS=$pkg_cv_VORBIS_CFLAGS VORBIS_LIBS=$pkg_cv_VORBIS_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } VORBIS_PKG=vorbis $as_echo "#define MPT_WITH_VORBIS /**/" >>confdefs.h fi fi # Required libopenmpt dependency: vorbisfile VORBISFILE_PKG= # Check whether --with-vorbisfile was given. if test "${with_vorbisfile+set}" = set; then : withval=$with_vorbisfile; fi if test "x$with_vorbisfile" != "xno"; then : pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for vorbisfile" >&5 $as_echo_n "checking for vorbisfile... " >&6; } if test -n "$VORBISFILE_CFLAGS"; then pkg_cv_VORBISFILE_CFLAGS="$VORBISFILE_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"vorbisfile\""; } >&5 ($PKG_CONFIG --exists --print-errors "vorbisfile") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_VORBISFILE_CFLAGS=`$PKG_CONFIG --cflags "vorbisfile" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$VORBISFILE_LIBS"; then pkg_cv_VORBISFILE_LIBS="$VORBISFILE_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"vorbisfile\""; } >&5 ($PKG_CONFIG --exists --print-errors "vorbisfile") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_VORBISFILE_LIBS=`$PKG_CONFIG --libs "vorbisfile" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then VORBISFILE_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "vorbisfile" 2>&1` else VORBISFILE_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "vorbisfile" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$VORBISFILE_PKG_ERRORS" >&5 as_fn_error $? "Unable to find libvorbisfile." "$LINENO" 5 elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } as_fn_error $? "Unable to find libvorbisfile." "$LINENO" 5 else VORBISFILE_CFLAGS=$pkg_cv_VORBISFILE_CFLAGS VORBISFILE_LIBS=$pkg_cv_VORBISFILE_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } VORBISFILE_PKG=vorbisfile $as_echo "#define MPT_WITH_VORBISFILE /**/" >>confdefs.h fi fi LIBOPENMPT_REQUIRES_PRIVATE="$ZLIB_PKG $MPG123_PKG $OGG_PKG $VORBIS_PKG $VORBISFILE_PKG" LIBOPENMPT_LIBS_PRIVATE="$CXXSTDLIB_PCLIBSPRIVATE $LIBOPENMPT_LIBS_PRIVATE_WIN32" # openmpt123 # Check whether --enable-openmpt123 was given. if test "${enable_openmpt123+set}" = set; then : enableval=$enable_openmpt123; fi if test "x$enable_openmpt123" != "xno"; then ENABLE_OPENMPT123_TRUE= ENABLE_OPENMPT123_FALSE='#' else ENABLE_OPENMPT123_TRUE='#' ENABLE_OPENMPT123_FALSE= fi # examples # Check whether --enable-examples was given. if test "${enable_examples+set}" = set; then : enableval=$enable_examples; fi if test "x$enable_examples" != "xno"; then ENABLE_EXAMPLES_TRUE= ENABLE_EXAMPLES_FALSE='#' else ENABLE_EXAMPLES_TRUE='#' ENABLE_EXAMPLES_FALSE= fi # tests # Check whether --enable-tests was given. if test "${enable_tests+set}" = set; then : enableval=$enable_tests; fi if test "x$enable_tests" != "xno"; then ENABLE_TESTS_TRUE= ENABLE_TESTS_FALSE='#' else ENABLE_TESTS_TRUE='#' ENABLE_TESTS_FALSE= fi # Optional openmpt123 dependency # Check whether --with-pulseaudio was given. if test "${with_pulseaudio+set}" = set; then : withval=$with_pulseaudio; fi if test "x$enable_openmpt123" != "xno"; then : case $host_os in linux*) if test "x$with_pulseaudio" != "xno"; then : pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libpulse libpulse-simple" >&5 $as_echo_n "checking for libpulse libpulse-simple... " >&6; } if test -n "$PULSEAUDIO_CFLAGS"; then pkg_cv_PULSEAUDIO_CFLAGS="$PULSEAUDIO_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libpulse libpulse-simple\""; } >&5 ($PKG_CONFIG --exists --print-errors "libpulse libpulse-simple") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_PULSEAUDIO_CFLAGS=`$PKG_CONFIG --cflags "libpulse libpulse-simple" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$PULSEAUDIO_LIBS"; then pkg_cv_PULSEAUDIO_LIBS="$PULSEAUDIO_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libpulse libpulse-simple\""; } >&5 ($PKG_CONFIG --exists --print-errors "libpulse libpulse-simple") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_PULSEAUDIO_LIBS=`$PKG_CONFIG --libs "libpulse libpulse-simple" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then PULSEAUDIO_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libpulse libpulse-simple" 2>&1` else PULSEAUDIO_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libpulse libpulse-simple" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$PULSEAUDIO_PKG_ERRORS" >&5 have_pulseaudio=0 as_fn_error $? "Unable to find libpulse and/or libpulse-simple." "$LINENO" 5 elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } have_pulseaudio=0 as_fn_error $? "Unable to find libpulse and/or libpulse-simple." "$LINENO" 5 else PULSEAUDIO_CFLAGS=$pkg_cv_PULSEAUDIO_CFLAGS PULSEAUDIO_LIBS=$pkg_cv_PULSEAUDIO_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } have_pulseaudio=1 $as_echo "#define MPT_WITH_PULSEAUDIO /**/" >>confdefs.h fi else have_pulseaudio=0 fi ;; *) if test "x$with_pulseaudio" = "xyes"; then : pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libpulse libpulse-simple" >&5 $as_echo_n "checking for libpulse libpulse-simple... " >&6; } if test -n "$PULSEAUDIO_CFLAGS"; then pkg_cv_PULSEAUDIO_CFLAGS="$PULSEAUDIO_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libpulse libpulse-simple\""; } >&5 ($PKG_CONFIG --exists --print-errors "libpulse libpulse-simple") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_PULSEAUDIO_CFLAGS=`$PKG_CONFIG --cflags "libpulse libpulse-simple" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$PULSEAUDIO_LIBS"; then pkg_cv_PULSEAUDIO_LIBS="$PULSEAUDIO_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libpulse libpulse-simple\""; } >&5 ($PKG_CONFIG --exists --print-errors "libpulse libpulse-simple") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_PULSEAUDIO_LIBS=`$PKG_CONFIG --libs "libpulse libpulse-simple" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then PULSEAUDIO_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libpulse libpulse-simple" 2>&1` else PULSEAUDIO_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libpulse libpulse-simple" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$PULSEAUDIO_PKG_ERRORS" >&5 have_pulseaudio=0 as_fn_error $? "Unable to find libpulse and/or libpulse-simple." "$LINENO" 5 elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } have_pulseaudio=0 as_fn_error $? "Unable to find libpulse and/or libpulse-simple." "$LINENO" 5 else PULSEAUDIO_CFLAGS=$pkg_cv_PULSEAUDIO_CFLAGS PULSEAUDIO_LIBS=$pkg_cv_PULSEAUDIO_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } have_pulseaudio=1 $as_echo "#define MPT_WITH_PULSEAUDIO /**/" >>confdefs.h fi else have_pulseaudio=0 fi ;; esac else have_pulseaudio=0 fi # Optional openmpt123 and examples dependency # Check whether --with-portaudio was given. if test "${with_portaudio+set}" = set; then : withval=$with_portaudio; fi if test "x$enable_openmpt123$enable_examples" != "xnono"; then : if test "x$with_portaudio" != "xno"; then : pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for portaudio-2.0" >&5 $as_echo_n "checking for portaudio-2.0... " >&6; } if test -n "$PORTAUDIO_CFLAGS"; then pkg_cv_PORTAUDIO_CFLAGS="$PORTAUDIO_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"portaudio-2.0\""; } >&5 ($PKG_CONFIG --exists --print-errors "portaudio-2.0") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_PORTAUDIO_CFLAGS=`$PKG_CONFIG --cflags "portaudio-2.0" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$PORTAUDIO_LIBS"; then pkg_cv_PORTAUDIO_LIBS="$PORTAUDIO_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"portaudio-2.0\""; } >&5 ($PKG_CONFIG --exists --print-errors "portaudio-2.0") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_PORTAUDIO_LIBS=`$PKG_CONFIG --libs "portaudio-2.0" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then PORTAUDIO_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "portaudio-2.0" 2>&1` else PORTAUDIO_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "portaudio-2.0" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$PORTAUDIO_PKG_ERRORS" >&5 have_portaudio=0 as_fn_error $? "Unable to find libportaudio." "$LINENO" 5 elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } have_portaudio=0 as_fn_error $? "Unable to find libportaudio." "$LINENO" 5 else PORTAUDIO_CFLAGS=$pkg_cv_PORTAUDIO_CFLAGS PORTAUDIO_LIBS=$pkg_cv_PORTAUDIO_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } have_portaudio=1 $as_echo "#define MPT_WITH_PORTAUDIO /**/" >>confdefs.h fi else have_portaudio=0 fi else have_portaudio=0 fi if test x$have_portaudio = x1; then HAVE_PORTAUDIO_TRUE= HAVE_PORTAUDIO_FALSE='#' else HAVE_PORTAUDIO_TRUE='#' HAVE_PORTAUDIO_FALSE= fi # Optional examples dependency: PortAudio C++ # Check whether --with-portaudiocpp was given. if test "${with_portaudiocpp+set}" = set; then : withval=$with_portaudiocpp; fi if test "x$enable_examples" != "xno"; then : if test "x$with_portaudiocpp" != "xno"; then : pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for portaudiocpp" >&5 $as_echo_n "checking for portaudiocpp... " >&6; } if test -n "$PORTAUDIOCPP_CFLAGS"; then pkg_cv_PORTAUDIOCPP_CFLAGS="$PORTAUDIOCPP_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"portaudiocpp\""; } >&5 ($PKG_CONFIG --exists --print-errors "portaudiocpp") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_PORTAUDIOCPP_CFLAGS=`$PKG_CONFIG --cflags "portaudiocpp" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$PORTAUDIOCPP_LIBS"; then pkg_cv_PORTAUDIOCPP_LIBS="$PORTAUDIOCPP_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"portaudiocpp\""; } >&5 ($PKG_CONFIG --exists --print-errors "portaudiocpp") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_PORTAUDIOCPP_LIBS=`$PKG_CONFIG --libs "portaudiocpp" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then PORTAUDIOCPP_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "portaudiocpp" 2>&1` else PORTAUDIOCPP_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "portaudiocpp" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$PORTAUDIOCPP_PKG_ERRORS" >&5 have_portaudiocpp=0 as_fn_error $? "Unable to find libportaudiocpp." "$LINENO" 5 elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } have_portaudiocpp=0 as_fn_error $? "Unable to find libportaudiocpp." "$LINENO" 5 else PORTAUDIOCPP_CFLAGS=$pkg_cv_PORTAUDIOCPP_CFLAGS PORTAUDIOCPP_LIBS=$pkg_cv_PORTAUDIOCPP_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } have_portaudiocpp=1 $as_echo "#define MPT_WITH_PORTAUDIOCPP /**/" >>confdefs.h fi else have_portaudiocpp=0 fi else have_portaudiocpp=0 fi if test x$have_portaudiocpp = x1; then HAVE_PORTAUDIOCPP_TRUE= HAVE_PORTAUDIOCPP_FALSE='#' else HAVE_PORTAUDIOCPP_TRUE='#' HAVE_PORTAUDIOCPP_FALSE= fi # Optional disabled openmpt123 dependency: libsdl2 # Check whether --with-sdl2 was given. if test "${with_sdl2+set}" = set; then : withval=$with_sdl2; fi if test "x$enable_openmpt123" != "xno"; then : if test "x$with_sdl2" = "xyes"; then : pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sdl2 >= 2.0.4" >&5 $as_echo_n "checking for sdl2 >= 2.0.4... " >&6; } if test -n "$SDL2_CFLAGS"; then pkg_cv_SDL2_CFLAGS="$SDL2_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"sdl2 >= 2.0.4\""; } >&5 ($PKG_CONFIG --exists --print-errors "sdl2 >= 2.0.4") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_SDL2_CFLAGS=`$PKG_CONFIG --cflags "sdl2 >= 2.0.4" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$SDL2_LIBS"; then pkg_cv_SDL2_LIBS="$SDL2_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"sdl2 >= 2.0.4\""; } >&5 ($PKG_CONFIG --exists --print-errors "sdl2 >= 2.0.4") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_SDL2_LIBS=`$PKG_CONFIG --libs "sdl2 >= 2.0.4" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then SDL2_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "sdl2 >= 2.0.4" 2>&1` else SDL2_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "sdl2 >= 2.0.4" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$SDL2_PKG_ERRORS" >&5 as_fn_error $? "Unable to find libsdl2." "$LINENO" 5 elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } as_fn_error $? "Unable to find libsdl2." "$LINENO" 5 else SDL2_CFLAGS=$pkg_cv_SDL2_CFLAGS SDL2_LIBS=$pkg_cv_SDL2_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } $as_echo "#define MPT_WITH_SDL2 /**/" >>confdefs.h fi fi fi # Optional openmpt123 dependency: libsndfile # Check whether --with-sndfile was given. if test "${with_sndfile+set}" = set; then : withval=$with_sndfile; fi if test "x$enable_openmpt123" != "xno"; then : if test "x$with_sndfile" != "xno"; then : pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sndfile" >&5 $as_echo_n "checking for sndfile... " >&6; } if test -n "$SNDFILE_CFLAGS"; then pkg_cv_SNDFILE_CFLAGS="$SNDFILE_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"sndfile\""; } >&5 ($PKG_CONFIG --exists --print-errors "sndfile") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_SNDFILE_CFLAGS=`$PKG_CONFIG --cflags "sndfile" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$SNDFILE_LIBS"; then pkg_cv_SNDFILE_LIBS="$SNDFILE_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"sndfile\""; } >&5 ($PKG_CONFIG --exists --print-errors "sndfile") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_SNDFILE_LIBS=`$PKG_CONFIG --libs "sndfile" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then SNDFILE_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "sndfile" 2>&1` else SNDFILE_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "sndfile" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$SNDFILE_PKG_ERRORS" >&5 as_fn_error $? "Unable to find libsndfile." "$LINENO" 5 elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } as_fn_error $? "Unable to find libsndfile." "$LINENO" 5 else SNDFILE_CFLAGS=$pkg_cv_SNDFILE_CFLAGS SNDFILE_LIBS=$pkg_cv_SNDFILE_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } $as_echo "#define MPT_WITH_SNDFILE /**/" >>confdefs.h fi fi fi # Optional openmpt123 dependency: libFLAC # Check whether --with-flac was given. if test "${with_flac+set}" = set; then : withval=$with_flac; fi if test "x$enable_openmpt123" != "xno"; then : if test "x$with_flac" != "xno"; then : pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for flac >= 1.3.0" >&5 $as_echo_n "checking for flac >= 1.3.0... " >&6; } if test -n "$FLAC_CFLAGS"; then pkg_cv_FLAC_CFLAGS="$FLAC_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"flac >= 1.3.0\""; } >&5 ($PKG_CONFIG --exists --print-errors "flac >= 1.3.0") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_FLAC_CFLAGS=`$PKG_CONFIG --cflags "flac >= 1.3.0" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$FLAC_LIBS"; then pkg_cv_FLAC_LIBS="$FLAC_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"flac >= 1.3.0\""; } >&5 ($PKG_CONFIG --exists --print-errors "flac >= 1.3.0") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_FLAC_LIBS=`$PKG_CONFIG --libs "flac >= 1.3.0" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then FLAC_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "flac >= 1.3.0" 2>&1` else FLAC_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "flac >= 1.3.0" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$FLAC_PKG_ERRORS" >&5 as_fn_error $? "Unable to find libflac >= 1.3.0." "$LINENO" 5 elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } as_fn_error $? "Unable to find libflac >= 1.3.0." "$LINENO" 5 else FLAC_CFLAGS=$pkg_cv_FLAC_CFLAGS FLAC_LIBS=$pkg_cv_FLAC_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } $as_echo "#define MPT_WITH_FLAC /**/" >>confdefs.h fi fi fi # We want a modern C compiler case $ac_cv_prog_cc_stdc in #( no) : ac_cv_prog_cc_c99=no; ac_cv_prog_cc_c89=no ;; #( *) : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C99" >&5 $as_echo_n "checking for $CC option to accept ISO C99... " >&6; } if ${ac_cv_prog_cc_c99+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c99=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include #include // Check varargs macros. These examples are taken from C99 6.10.3.5. #define debug(...) fprintf (stderr, __VA_ARGS__) #define showlist(...) puts (#__VA_ARGS__) #define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__)) static void test_varargs_macros (void) { int x = 1234; int y = 5678; debug ("Flag"); debug ("X = %d\n", x); showlist (The first, second, and third items.); report (x>y, "x is %d but y is %d", x, y); } // Check long long types. #define BIG64 18446744073709551615ull #define BIG32 4294967295ul #define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0) #if !BIG_OK your preprocessor is broken; #endif #if BIG_OK #else your preprocessor is broken; #endif static long long int bignum = -9223372036854775807LL; static unsigned long long int ubignum = BIG64; struct incomplete_array { int datasize; double data[]; }; struct named_init { int number; const wchar_t *name; double average; }; typedef const char *ccp; static inline int test_restrict (ccp restrict text) { // See if C++-style comments work. // Iterate through items via the restricted pointer. // Also check for declarations in for loops. for (unsigned int i = 0; *(text+i) != '\0'; ++i) continue; return 0; } // Check varargs and va_copy. static void test_varargs (const char *format, ...) { va_list args; va_start (args, format); va_list args_copy; va_copy (args_copy, args); const char *str; int number; float fnumber; while (*format) { switch (*format++) { case 's': // string str = va_arg (args_copy, const char *); break; case 'd': // int number = va_arg (args_copy, int); break; case 'f': // float fnumber = va_arg (args_copy, double); break; default: break; } } va_end (args_copy); va_end (args); } int main () { // Check bool. _Bool success = false; // Check restrict. if (test_restrict ("String literal") == 0) success = true; char *restrict newvar = "Another string"; // Check varargs. test_varargs ("s, d' f .", "string", 65, 34.234); test_varargs_macros (); // Check flexible array members. struct incomplete_array *ia = malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10)); ia->datasize = 10; for (int i = 0; i < ia->datasize; ++i) ia->data[i] = i * 1.234; // Check named initializers. struct named_init ni = { .number = 34, .name = L"Test wide string", .average = 543.34343, }; ni.number = 58; int dynamic_array[ni.number]; dynamic_array[ni.number - 1] = 543; // work around unused variable warnings return (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == 'x' || dynamic_array[ni.number - 1] != 543); ; return 0; } _ACEOF for ac_arg in '' -std=gnu99 -std=c99 -c99 -AC99 -D_STDC_C99= -qlanglvl=extc99 do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_c99=$ac_arg fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c99" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c99" in x) { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } ;; xno) { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c99" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5 $as_echo "$ac_cv_prog_cc_c99" >&6; } ;; esac if test "x$ac_cv_prog_cc_c99" != xno; then : ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99 else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 $as_echo_n "checking for $CC option to accept ISO C89... " >&6; } if ${ac_cv_prog_cc_c89+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include struct stat; /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not '\xHH' hex character constants. These don't provoke an error unfortunately, instead are silently treated as 'x'. The following induces an error, until -std is added to get proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an array size at least. It's necessary to write '\x00'==0 to get something that's true only with -std. */ int osf4_cc_array ['\x00' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) 'x' int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); int argc; char **argv; int main () { return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; ; return 0; } _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c89" in x) { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } ;; xno) { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c89" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 $as_echo "$ac_cv_prog_cc_c89" >&6; } ;; esac if test "x$ac_cv_prog_cc_c89" != xno; then : ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89 else ac_cv_prog_cc_stdc=no fi fi ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO Standard C" >&5 $as_echo_n "checking for $CC option to accept ISO Standard C... " >&6; } if ${ac_cv_prog_cc_stdc+:} false; then : $as_echo_n "(cached) " >&6 fi case $ac_cv_prog_cc_stdc in #( no) : { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; #( '') : { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } ;; #( *) : { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_stdc" >&5 $as_echo "$ac_cv_prog_cc_stdc" >&6; } ;; esac #AC_PROG_CC_C99 # We need C++17 support #AX_CXX_COMPILE_STDCXX(20, [noext], [optional]) #AS_IF([test "x$HAVE_CXX20" != "x1"], # [ # AX_CXX_COMPILE_STDCXX(17, [noext], [mandatory]) # ],[] #) ax_cxx_compile_alternatives="17 1z" ax_cxx_compile_cxx17_required=true ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu ac_success=no if test x$ac_success = xno; then for alternative in ${ax_cxx_compile_alternatives}; do for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do cachevar=`$as_echo "ax_cv_cxx_compile_cxx17_$switch" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports C++17 features with $switch" >&5 $as_echo_n "checking whether $CXX supports C++17 features with $switch... " >&6; } if eval \${$cachevar+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_CXX="$CXX" CXX="$CXX $switch" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ // If the compiler admits that it is not ready for C++11, why torture it? // Hopefully, this will speed up the test. #ifndef __cplusplus #error "This is not a C++ compiler" #elif __cplusplus < 201103L #error "This is not a C++11 compiler" #else namespace cxx11 { namespace test_static_assert { template struct check { static_assert(sizeof(int) <= sizeof(T), "not big enough"); }; } namespace test_final_override { struct Base { virtual void f() {} }; struct Derived : public Base { virtual void f() override {} }; } namespace test_double_right_angle_brackets { template < typename T > struct check {}; typedef check single_type; typedef check> double_type; typedef check>> triple_type; typedef check>>> quadruple_type; } namespace test_decltype { int f() { int a = 1; decltype(a) b = 2; return a + b; } } namespace test_type_deduction { template < typename T1, typename T2 > struct is_same { static const bool value = false; }; template < typename T > struct is_same { static const bool value = true; }; template < typename T1, typename T2 > auto add(T1 a1, T2 a2) -> decltype(a1 + a2) { return a1 + a2; } int test(const int c, volatile int v) { static_assert(is_same::value == true, ""); static_assert(is_same::value == false, ""); static_assert(is_same::value == false, ""); auto ac = c; auto av = v; auto sumi = ac + av + 'x'; auto sumf = ac + av + 1.0; static_assert(is_same::value == true, ""); static_assert(is_same::value == true, ""); static_assert(is_same::value == true, ""); static_assert(is_same::value == false, ""); static_assert(is_same::value == true, ""); return (sumf > 0.0) ? sumi : add(c, v); } } namespace test_noexcept { int f() { return 0; } int g() noexcept { return 0; } static_assert(noexcept(f()) == false, ""); static_assert(noexcept(g()) == true, ""); } namespace test_constexpr { template < typename CharT > unsigned long constexpr strlen_c_r(const CharT *const s, const unsigned long acc) noexcept { return *s ? strlen_c_r(s + 1, acc + 1) : acc; } template < typename CharT > unsigned long constexpr strlen_c(const CharT *const s) noexcept { return strlen_c_r(s, 0UL); } static_assert(strlen_c("") == 0UL, ""); static_assert(strlen_c("1") == 1UL, ""); static_assert(strlen_c("example") == 7UL, ""); static_assert(strlen_c("another\0example") == 7UL, ""); } namespace test_rvalue_references { template < int N > struct answer { static constexpr int value = N; }; answer<1> f(int&) { return answer<1>(); } answer<2> f(const int&) { return answer<2>(); } answer<3> f(int&&) { return answer<3>(); } void test() { int i = 0; const int c = 0; static_assert(decltype(f(i))::value == 1, ""); static_assert(decltype(f(c))::value == 2, ""); static_assert(decltype(f(0))::value == 3, ""); } } namespace test_uniform_initialization { struct test { static const int zero {}; static const int one {1}; }; static_assert(test::zero == 0, ""); static_assert(test::one == 1, ""); } namespace test_lambdas { void test1() { auto lambda1 = [](){}; auto lambda2 = lambda1; lambda1(); lambda2(); } int test2() { auto a = [](int i, int j){ return i + j; }(1, 2); auto b = []() -> int { return '0'; }(); auto c = [=](){ return a + b; }(); auto d = [&](){ return c; }(); auto e = [a, &b](int x) mutable { const auto identity = [](int y){ return y; }; for (auto i = 0; i < a; ++i) a += b--; return x + identity(a + b); }(0); return a + b + c + d + e; } int test3() { const auto nullary = [](){ return 0; }; const auto unary = [](int x){ return x; }; using nullary_t = decltype(nullary); using unary_t = decltype(unary); const auto higher1st = [](nullary_t f){ return f(); }; const auto higher2nd = [unary](nullary_t f1){ return [unary, f1](unary_t f2){ return f2(unary(f1())); }; }; return higher1st(nullary) + higher2nd(nullary)(unary); } } namespace test_variadic_templates { template struct sum; template struct sum { static constexpr auto value = N0 + sum::value; }; template <> struct sum<> { static constexpr auto value = 0; }; static_assert(sum<>::value == 0, ""); static_assert(sum<1>::value == 1, ""); static_assert(sum<23>::value == 23, ""); static_assert(sum<1, 2>::value == 3, ""); static_assert(sum<5, 5, 11>::value == 21, ""); static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, ""); } // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function // because of this. namespace test_template_alias_sfinae { struct foo {}; template using member = typename T::member_type; template void func(...) {} template void func(member*) {} void test(); void test() { func(0); } } } // namespace cxx11 #endif // __cplusplus >= 201103L // If the compiler admits that it is not ready for C++14, why torture it? // Hopefully, this will speed up the test. #ifndef __cplusplus #error "This is not a C++ compiler" #elif __cplusplus < 201402L #error "This is not a C++14 compiler" #else namespace cxx14 { namespace test_polymorphic_lambdas { int test() { const auto lambda = [](auto&&... args){ const auto istiny = [](auto x){ return (sizeof(x) == 1UL) ? 1 : 0; }; const int aretiny[] = { istiny(args)... }; return aretiny[0]; }; return lambda(1, 1L, 1.0f, '1'); } } namespace test_binary_literals { constexpr auto ivii = 0b0000000000101010; static_assert(ivii == 42, "wrong value"); } namespace test_generalized_constexpr { template < typename CharT > constexpr unsigned long strlen_c(const CharT *const s) noexcept { auto length = 0UL; for (auto p = s; *p; ++p) ++length; return length; } static_assert(strlen_c("") == 0UL, ""); static_assert(strlen_c("x") == 1UL, ""); static_assert(strlen_c("test") == 4UL, ""); static_assert(strlen_c("another\0test") == 7UL, ""); } namespace test_lambda_init_capture { int test() { auto x = 0; const auto lambda1 = [a = x](int b){ return a + b; }; const auto lambda2 = [a = lambda1(x)](){ return a; }; return lambda2(); } } namespace test_digit_separators { constexpr auto ten_million = 100'000'000; static_assert(ten_million == 100000000, ""); } namespace test_return_type_deduction { auto f(int& x) { return x; } decltype(auto) g(int& x) { return x; } template < typename T1, typename T2 > struct is_same { static constexpr auto value = false; }; template < typename T > struct is_same { static constexpr auto value = true; }; int test() { auto x = 0; static_assert(is_same::value, ""); static_assert(is_same::value, ""); return x; } } } // namespace cxx14 #endif // __cplusplus >= 201402L // If the compiler admits that it is not ready for C++17, why torture it? // Hopefully, this will speed up the test. #ifndef __cplusplus #error "This is not a C++ compiler" #elif __cplusplus < 201703L #error "This is not a C++17 compiler" #else #include #include #include namespace cxx17 { namespace test_constexpr_lambdas { constexpr int foo = [](){return 42;}(); } namespace test::nested_namespace::definitions { } namespace test_fold_expression { template int multiply(Args... args) { return (args * ... * 1); } template bool all(Args... args) { return (args && ...); } } namespace test_extended_static_assert { static_assert (true); } namespace test_auto_brace_init_list { auto foo = {5}; auto bar {5}; static_assert(std::is_same, decltype(foo)>::value); static_assert(std::is_same::value); } namespace test_typename_in_template_template_parameter { template typename X> struct D; } namespace test_fallthrough_nodiscard_maybe_unused_attributes { int f1() { return 42; } [[nodiscard]] int f2() { [[maybe_unused]] auto unused = f1(); switch (f1()) { case 17: f1(); [[fallthrough]]; case 42: f1(); } return f1(); } } namespace test_extended_aggregate_initialization { struct base1 { int b1, b2 = 42; }; struct base2 { base2() { b3 = 42; } int b3; }; struct derived : base1, base2 { int d; }; derived d1 {{1, 2}, {}, 4}; // full initialization derived d2 {{}, {}, 4}; // value-initialized bases } namespace test_general_range_based_for_loop { struct iter { int i; int& operator* () { return i; } const int& operator* () const { return i; } iter& operator++() { ++i; return *this; } }; struct sentinel { int i; }; bool operator== (const iter& i, const sentinel& s) { return i.i == s.i; } bool operator!= (const iter& i, const sentinel& s) { return !(i == s); } struct range { iter begin() const { return {0}; } sentinel end() const { return {5}; } }; void f() { range r {}; for (auto i : r) { [[maybe_unused]] auto v = i; } } } namespace test_lambda_capture_asterisk_this_by_value { struct t { int i; int foo() { return [*this]() { return i; }(); } }; } namespace test_enum_class_construction { enum class byte : unsigned char {}; byte foo {42}; } namespace test_constexpr_if { template int f () { if constexpr(cond) { return 13; } else { return 42; } } } namespace test_selection_statement_with_initializer { int f() { return 13; } int f2() { if (auto i = f(); i > 0) { return 3; } switch (auto i = f(); i + 4) { case 17: return 2; default: return 1; } } } namespace test_template_argument_deduction_for_class_templates { template struct pair { pair (T1 p1, T2 p2) : m1 {p1}, m2 {p2} {} T1 m1; T2 m2; }; void f() { [[maybe_unused]] auto p = pair{13, 42u}; } } namespace test_non_type_auto_template_parameters { template struct B {}; B<5> b1; B<'a'> b2; } namespace test_structured_bindings { int arr[2] = { 1, 2 }; std::pair pr = { 1, 2 }; auto f1() -> int(&)[2] { return arr; } auto f2() -> std::pair& { return pr; } struct S { int x1 : 2; volatile double y1; }; S f3() { return {}; } auto [ x1, y1 ] = f1(); auto& [ xr1, yr1 ] = f1(); auto [ x2, y2 ] = f2(); auto& [ xr2, yr2 ] = f2(); const auto [ x3, y3 ] = f3(); } namespace test_exception_spec_type_system { struct Good {}; struct Bad {}; void g1() noexcept; void g2(); template Bad f(T*, T*); template Good f(T1*, T2*); static_assert (std::is_same_v); } namespace test_inline_variables { template void f(T) {} template inline T g(T) { return T{}; } template<> inline void f<>(int) {} template<> int g<>(int) { return 5; } } } // namespace cxx17 #endif // __cplusplus < 201703L _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : eval $cachevar=yes else eval $cachevar=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CXX="$ac_save_CXX" fi eval ac_res=\$$cachevar { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } if eval test x\$$cachevar = xyes; then CXX="$CXX $switch" if test -n "$CXXCPP" ; then CXXCPP="$CXXCPP $switch" fi ac_success=yes break fi done if test x$ac_success = xyes; then break fi done fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test x$ax_cxx_compile_cxx17_required = xtrue; then if test x$ac_success = xno; then as_fn_error $? "*** A compiler with support for C++17 language features is required." "$LINENO" 5 fi fi if test x$ac_success = xno; then HAVE_CXX17=0 { $as_echo "$as_me:${as_lineno-$LINENO}: No compiler with C++17 support was found" >&5 $as_echo "$as_me: No compiler with C++17 support was found" >&6;} else HAVE_CXX17=1 $as_echo "#define HAVE_CXX17 1" >>confdefs.h fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -fvisibility=hidden" >&5 $as_echo_n "checking whether C compiler accepts -fvisibility=hidden... " >&6; } if ${ax_cv_check_cflags___fvisibility_hidden+:} false; then : $as_echo_n "(cached) " >&6 else ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -fvisibility=hidden" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ax_cv_check_cflags___fvisibility_hidden=yes else ax_cv_check_cflags___fvisibility_hidden=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS=$ax_check_save_flags fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___fvisibility_hidden" >&5 $as_echo "$ax_cv_check_cflags___fvisibility_hidden" >&6; } if test "x$ax_cv_check_cflags___fvisibility_hidden" = xyes; then : CFLAGS="$CFLAGS -fvisibility=hidden" else : fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking CFLAGS for maximum warnings" >&5 $as_echo_n "checking CFLAGS for maximum warnings... " >&6; } if ${ac_cv_cflags_warn_all+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_cflags_warn_all="no, unknown" ac_save_CFLAGS="$CFLAGS" for ac_arg in "-warn all % -warn all" "-pedantic % -Wall" "-xstrconst % -v" "-std1 % -verbose -w0 -warnprotos" "-qlanglvl=ansi % -qsrcmsg -qinfo=all:noppt:noppc:noobs:nocnd" "-ansi -ansiE % -fullwarn" "+ESlit % +w1" "-Xc % -pvctl,fullmsg" "-h conform % -h msglevel 2" # do CFLAGS="$ac_save_CFLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'` cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_cflags_warn_all=`echo $ac_arg | sed -e 's,.*% *,,'` ; break fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext done CFLAGS="$ac_save_CFLAGS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags_warn_all" >&5 $as_echo "$ac_cv_cflags_warn_all" >&6; } case ".$ac_cv_cflags_warn_all" in .ok|.ok,*) ;; .|.no|.no,*) ;; *) if ${CFLAGS+:} false; then : case " $CFLAGS " in #( *" $ac_cv_cflags_warn_all "*) : { { $as_echo "$as_me:${as_lineno-$LINENO}: : CFLAGS already contains \$ac_cv_cflags_warn_all"; } >&5 (: CFLAGS already contains $ac_cv_cflags_warn_all) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } ;; #( *) : as_fn_append CFLAGS " $ac_cv_cflags_warn_all" { { $as_echo "$as_me:${as_lineno-$LINENO}: : CFLAGS=\"\$CFLAGS\""; } >&5 (: CFLAGS="$CFLAGS") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } ;; esac else CFLAGS=$ac_cv_cflags_warn_all { { $as_echo "$as_me:${as_lineno-$LINENO}: : CFLAGS=\"\$CFLAGS\""; } >&5 (: CFLAGS="$CFLAGS") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } fi ;; esac ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C++ compiler accepts -fvisibility=hidden" >&5 $as_echo_n "checking whether C++ compiler accepts -fvisibility=hidden... " >&6; } if ${ax_cv_check_cxxflags___fvisibility_hidden+:} false; then : $as_echo_n "(cached) " >&6 else ax_check_save_flags=$CXXFLAGS CXXFLAGS="$CXXFLAGS -fvisibility=hidden" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ax_cv_check_cxxflags___fvisibility_hidden=yes else ax_cv_check_cxxflags___fvisibility_hidden=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CXXFLAGS=$ax_check_save_flags fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cxxflags___fvisibility_hidden" >&5 $as_echo "$ax_cv_check_cxxflags___fvisibility_hidden" >&6; } if test "x$ax_cv_check_cxxflags___fvisibility_hidden" = xyes; then : CXXFLAGS="$CXXFLAGS -fvisibility=hidden" else : fi ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking CXXFLAGS for maximum warnings" >&5 $as_echo_n "checking CXXFLAGS for maximum warnings... " >&6; } if ${ac_cv_cxxflags_warn_all+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_cxxflags_warn_all="no, unknown" ac_save_CXXFLAGS="$CXXFLAGS" for ac_arg in "-warn all % -warn all" "-pedantic % -Wall" "-xstrconst % -v" "-std1 % -verbose -w0 -warnprotos" "-qlanglvl=ansi % -qsrcmsg -qinfo=all:noppt:noppc:noobs:nocnd" "-ansi -ansiE % -fullwarn" "+ESlit % +w1" "-Xc % -pvctl,fullmsg" "-h conform % -h msglevel 2" # do CXXFLAGS="$ac_save_CXXFLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'` cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_cxxflags_warn_all=`echo $ac_arg | sed -e 's,.*% *,,'` ; break fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext done CXXFLAGS="$ac_save_CXXFLAGS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxxflags_warn_all" >&5 $as_echo "$ac_cv_cxxflags_warn_all" >&6; } case ".$ac_cv_cxxflags_warn_all" in .ok|.ok,*) ;; .|.no|.no,*) ;; *) if ${CXXFLAGS+:} false; then : case " $CXXFLAGS " in #( *" $ac_cv_cxxflags_warn_all "*) : { { $as_echo "$as_me:${as_lineno-$LINENO}: : CXXFLAGS already contains \$ac_cv_cxxflags_warn_all"; } >&5 (: CXXFLAGS already contains $ac_cv_cxxflags_warn_all) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } ;; #( *) : as_fn_append CXXFLAGS " $ac_cv_cxxflags_warn_all" { { $as_echo "$as_me:${as_lineno-$LINENO}: : CXXFLAGS=\"\$CXXFLAGS\""; } >&5 (: CXXFLAGS="$CXXFLAGS") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } ;; esac else CXXFLAGS=$ac_cv_cxxflags_warn_all { { $as_echo "$as_me:${as_lineno-$LINENO}: : CXXFLAGS=\"\$CXXFLAGS\""; } >&5 (: CXXFLAGS="$CXXFLAGS") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } fi ;; esac ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu # mingw c++ thread ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu case $host_os in mingw32*) ac_fn_cxx_check_header_mongrel "$LINENO" "mingw.thread.h" "ac_cv_header_mingw_thread_h" "$ac_includes_default" if test "x$ac_cv_header_mingw_thread_h" = xyes; then : have_mingwstdtthreads=1 else have_mingwstdtthreads=0 fi ;; *) have_mingwstdtthreads=0 ;; esac ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test "x$have_mingwstdtthreads" = "x1"; then : MINGWSTDTHREADS_CPPFLAGS=-DMPT_WITH_MINGWSTDTHREADS else MINGWSTDTHREADS_CPPFLAGS= fi # Files: DX_PROJECT=libopenmpt DX_CONFIG='Doxyfile' DX_DOCDIR='doxygen-doc' # Environment variables used inside doxygen.cfg: DX_ENV="$DX_ENV SRCDIR='$srcdir'" SRCDIR=$srcdir DX_ENV="$DX_ENV PROJECT='$DX_PROJECT'" PROJECT=$DX_PROJECT DX_ENV="$DX_ENV VERSION='$PACKAGE_VERSION'" # Doxygen itself: # Check whether --enable-doxygen-doc was given. if test "${enable_doxygen_doc+set}" = set; then : enableval=$enable_doxygen_doc; case "$enableval" in #( y|Y|yes|Yes|YES) DX_FLAG_doc=1 ;; #( n|N|no|No|NO) DX_FLAG_doc=0 ;; #( *) as_fn_error $? "invalid value '$enableval' given to doxygen-doc" "$LINENO" 5 ;; esac else DX_FLAG_doc=1 fi if test "$DX_FLAG_doc" = 1; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}doxygen", so it can be a program name with args. set dummy ${ac_tool_prefix}doxygen; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_DX_DOXYGEN+:} false; then : $as_echo_n "(cached) " >&6 else case $DX_DOXYGEN in [\\/]* | ?:[\\/]*) ac_cv_path_DX_DOXYGEN="$DX_DOXYGEN" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_DX_DOXYGEN="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi DX_DOXYGEN=$ac_cv_path_DX_DOXYGEN if test -n "$DX_DOXYGEN"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DX_DOXYGEN" >&5 $as_echo "$DX_DOXYGEN" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_path_DX_DOXYGEN"; then ac_pt_DX_DOXYGEN=$DX_DOXYGEN # Extract the first word of "doxygen", so it can be a program name with args. set dummy doxygen; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_ac_pt_DX_DOXYGEN+:} false; then : $as_echo_n "(cached) " >&6 else case $ac_pt_DX_DOXYGEN in [\\/]* | ?:[\\/]*) ac_cv_path_ac_pt_DX_DOXYGEN="$ac_pt_DX_DOXYGEN" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_ac_pt_DX_DOXYGEN="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi ac_pt_DX_DOXYGEN=$ac_cv_path_ac_pt_DX_DOXYGEN if test -n "$ac_pt_DX_DOXYGEN"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_DX_DOXYGEN" >&5 $as_echo "$ac_pt_DX_DOXYGEN" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_pt_DX_DOXYGEN" = x; then DX_DOXYGEN="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac DX_DOXYGEN=$ac_pt_DX_DOXYGEN fi else DX_DOXYGEN="$ac_cv_path_DX_DOXYGEN" fi if test "$DX_FLAG_doc$DX_DOXYGEN" = 1; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: doxygen not found - will not generate any doxygen documentation" >&5 $as_echo "$as_me: WARNING: doxygen not found - will not generate any doxygen documentation" >&2;} DX_FLAG_doc=0 fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}perl", so it can be a program name with args. set dummy ${ac_tool_prefix}perl; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_DX_PERL+:} false; then : $as_echo_n "(cached) " >&6 else case $DX_PERL in [\\/]* | ?:[\\/]*) ac_cv_path_DX_PERL="$DX_PERL" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_DX_PERL="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi DX_PERL=$ac_cv_path_DX_PERL if test -n "$DX_PERL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DX_PERL" >&5 $as_echo "$DX_PERL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_path_DX_PERL"; then ac_pt_DX_PERL=$DX_PERL # Extract the first word of "perl", so it can be a program name with args. set dummy perl; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_ac_pt_DX_PERL+:} false; then : $as_echo_n "(cached) " >&6 else case $ac_pt_DX_PERL in [\\/]* | ?:[\\/]*) ac_cv_path_ac_pt_DX_PERL="$ac_pt_DX_PERL" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_ac_pt_DX_PERL="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi ac_pt_DX_PERL=$ac_cv_path_ac_pt_DX_PERL if test -n "$ac_pt_DX_PERL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_DX_PERL" >&5 $as_echo "$ac_pt_DX_PERL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_pt_DX_PERL" = x; then DX_PERL="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac DX_PERL=$ac_pt_DX_PERL fi else DX_PERL="$ac_cv_path_DX_PERL" fi if test "$DX_FLAG_doc$DX_PERL" = 1; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: perl not found - will not generate any doxygen documentation" >&5 $as_echo "$as_me: WARNING: perl not found - will not generate any doxygen documentation" >&2;} DX_FLAG_doc=0 fi : fi if test "$DX_FLAG_doc" = 1; then DX_ENV="$DX_ENV PERL_PATH='$DX_PERL'" PERL_PATH=$DX_PERL : else : fi # Dot for graphics: # Check whether --enable-doxygen-dot was given. if test "${enable_doxygen_dot+set}" = set; then : enableval=$enable_doxygen_dot; case "$enableval" in #( y|Y|yes|Yes|YES) DX_FLAG_dot=1 test "$DX_FLAG_doc" = "1" \ || as_fn_error $? "doxygen-dot requires doxygen-doc" "$LINENO" 5 ;; #( n|N|no|No|NO) DX_FLAG_dot=0 ;; #( *) as_fn_error $? "invalid value '$enableval' given to doxygen-dot" "$LINENO" 5 ;; esac else DX_FLAG_dot=0 test "$DX_FLAG_doc" = "1" || DX_FLAG_dot=0 fi if test "$DX_FLAG_dot" = 1; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}dot", so it can be a program name with args. set dummy ${ac_tool_prefix}dot; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_DX_DOT+:} false; then : $as_echo_n "(cached) " >&6 else case $DX_DOT in [\\/]* | ?:[\\/]*) ac_cv_path_DX_DOT="$DX_DOT" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_DX_DOT="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi DX_DOT=$ac_cv_path_DX_DOT if test -n "$DX_DOT"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DX_DOT" >&5 $as_echo "$DX_DOT" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_path_DX_DOT"; then ac_pt_DX_DOT=$DX_DOT # Extract the first word of "dot", so it can be a program name with args. set dummy dot; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_ac_pt_DX_DOT+:} false; then : $as_echo_n "(cached) " >&6 else case $ac_pt_DX_DOT in [\\/]* | ?:[\\/]*) ac_cv_path_ac_pt_DX_DOT="$ac_pt_DX_DOT" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_ac_pt_DX_DOT="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi ac_pt_DX_DOT=$ac_cv_path_ac_pt_DX_DOT if test -n "$ac_pt_DX_DOT"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_DX_DOT" >&5 $as_echo "$ac_pt_DX_DOT" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_pt_DX_DOT" = x; then DX_DOT="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac DX_DOT=$ac_pt_DX_DOT fi else DX_DOT="$ac_cv_path_DX_DOT" fi if test "$DX_FLAG_dot$DX_DOT" = 1; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: dot not found - will not generate graphics for doxygen documentation" >&5 $as_echo "$as_me: WARNING: dot not found - will not generate graphics for doxygen documentation" >&2;} DX_FLAG_dot=0 fi : fi if test "$DX_FLAG_dot" = 1; then DX_ENV="$DX_ENV HAVE_DOT='YES'" HAVE_DOT=YES DX_ENV="$DX_ENV DOT_PATH='`expr ".$DX_DOT" : '\(\.\)[^/]*$' \| "x$DX_DOT" : 'x\(.*\)/[^/]*$'`'" DOT_PATH=`expr ".$DX_DOT" : '\(\.\)[^/]*$' \| "x$DX_DOT" : 'x\(.*\)/[^/]*$'` : else DX_ENV="$DX_ENV HAVE_DOT='NO'" HAVE_DOT=NO : fi # Man pages generation: # Check whether --enable-doxygen-man was given. if test "${enable_doxygen_man+set}" = set; then : enableval=$enable_doxygen_man; case "$enableval" in #( y|Y|yes|Yes|YES) DX_FLAG_man=1 test "$DX_FLAG_doc" = "1" \ || as_fn_error $? "doxygen-man requires doxygen-doc" "$LINENO" 5 ;; #( n|N|no|No|NO) DX_FLAG_man=0 ;; #( *) as_fn_error $? "invalid value '$enableval' given to doxygen-man" "$LINENO" 5 ;; esac else DX_FLAG_man=0 test "$DX_FLAG_doc" = "1" || DX_FLAG_man=0 fi if test "$DX_FLAG_man" = 1; then : fi if test "$DX_FLAG_man" = 1; then DX_ENV="$DX_ENV GENERATE_MAN='YES'" GENERATE_MAN=YES : else DX_ENV="$DX_ENV GENERATE_MAN='NO'" GENERATE_MAN=NO : fi # RTF file generation: # Check whether --enable-doxygen-rtf was given. if test "${enable_doxygen_rtf+set}" = set; then : enableval=$enable_doxygen_rtf; case "$enableval" in #( y|Y|yes|Yes|YES) DX_FLAG_rtf=1 test "$DX_FLAG_doc" = "1" \ || as_fn_error $? "doxygen-rtf requires doxygen-doc" "$LINENO" 5 ;; #( n|N|no|No|NO) DX_FLAG_rtf=0 ;; #( *) as_fn_error $? "invalid value '$enableval' given to doxygen-rtf" "$LINENO" 5 ;; esac else DX_FLAG_rtf=0 test "$DX_FLAG_doc" = "1" || DX_FLAG_rtf=0 fi if test "$DX_FLAG_rtf" = 1; then : fi if test "$DX_FLAG_rtf" = 1; then DX_ENV="$DX_ENV GENERATE_RTF='YES'" GENERATE_RTF=YES : else DX_ENV="$DX_ENV GENERATE_RTF='NO'" GENERATE_RTF=NO : fi # XML file generation: # Check whether --enable-doxygen-xml was given. if test "${enable_doxygen_xml+set}" = set; then : enableval=$enable_doxygen_xml; case "$enableval" in #( y|Y|yes|Yes|YES) DX_FLAG_xml=1 test "$DX_FLAG_doc" = "1" \ || as_fn_error $? "doxygen-xml requires doxygen-doc" "$LINENO" 5 ;; #( n|N|no|No|NO) DX_FLAG_xml=0 ;; #( *) as_fn_error $? "invalid value '$enableval' given to doxygen-xml" "$LINENO" 5 ;; esac else DX_FLAG_xml=0 test "$DX_FLAG_doc" = "1" || DX_FLAG_xml=0 fi if test "$DX_FLAG_xml" = 1; then : fi if test "$DX_FLAG_xml" = 1; then DX_ENV="$DX_ENV GENERATE_XML='YES'" GENERATE_XML=YES : else DX_ENV="$DX_ENV GENERATE_XML='NO'" GENERATE_XML=NO : fi # (Compressed) HTML help generation: # Check whether --enable-doxygen-chm was given. if test "${enable_doxygen_chm+set}" = set; then : enableval=$enable_doxygen_chm; case "$enableval" in #( y|Y|yes|Yes|YES) DX_FLAG_chm=1 test "$DX_FLAG_doc" = "1" \ || as_fn_error $? "doxygen-chm requires doxygen-doc" "$LINENO" 5 ;; #( n|N|no|No|NO) DX_FLAG_chm=0 ;; #( *) as_fn_error $? "invalid value '$enableval' given to doxygen-chm" "$LINENO" 5 ;; esac else DX_FLAG_chm=0 test "$DX_FLAG_doc" = "1" || DX_FLAG_chm=0 fi if test "$DX_FLAG_chm" = 1; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}hhc", so it can be a program name with args. set dummy ${ac_tool_prefix}hhc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_DX_HHC+:} false; then : $as_echo_n "(cached) " >&6 else case $DX_HHC in [\\/]* | ?:[\\/]*) ac_cv_path_DX_HHC="$DX_HHC" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_DX_HHC="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi DX_HHC=$ac_cv_path_DX_HHC if test -n "$DX_HHC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DX_HHC" >&5 $as_echo "$DX_HHC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_path_DX_HHC"; then ac_pt_DX_HHC=$DX_HHC # Extract the first word of "hhc", so it can be a program name with args. set dummy hhc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_ac_pt_DX_HHC+:} false; then : $as_echo_n "(cached) " >&6 else case $ac_pt_DX_HHC in [\\/]* | ?:[\\/]*) ac_cv_path_ac_pt_DX_HHC="$ac_pt_DX_HHC" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_ac_pt_DX_HHC="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi ac_pt_DX_HHC=$ac_cv_path_ac_pt_DX_HHC if test -n "$ac_pt_DX_HHC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_DX_HHC" >&5 $as_echo "$ac_pt_DX_HHC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_pt_DX_HHC" = x; then DX_HHC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac DX_HHC=$ac_pt_DX_HHC fi else DX_HHC="$ac_cv_path_DX_HHC" fi if test "$DX_FLAG_chm$DX_HHC" = 1; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: hhc not found - will not generate doxygen compressed HTML help documentation" >&5 $as_echo "$as_me: WARNING: hhc not found - will not generate doxygen compressed HTML help documentation" >&2;} DX_FLAG_chm=0 fi : fi if test "$DX_FLAG_chm" = 1; then DX_ENV="$DX_ENV HHC_PATH='$DX_HHC'" HHC_PATH=$DX_HHC DX_ENV="$DX_ENV GENERATE_HTML='YES'" GENERATE_HTML=YES DX_ENV="$DX_ENV GENERATE_HTMLHELP='YES'" GENERATE_HTMLHELP=YES : else DX_ENV="$DX_ENV GENERATE_HTMLHELP='NO'" GENERATE_HTMLHELP=NO : fi # Separate CHI file generation. # Check whether --enable-doxygen-chi was given. if test "${enable_doxygen_chi+set}" = set; then : enableval=$enable_doxygen_chi; case "$enableval" in #( y|Y|yes|Yes|YES) DX_FLAG_chi=1 test "$DX_FLAG_chm" = "1" \ || as_fn_error $? "doxygen-chi requires doxygen-chm" "$LINENO" 5 ;; #( n|N|no|No|NO) DX_FLAG_chi=0 ;; #( *) as_fn_error $? "invalid value '$enableval' given to doxygen-chi" "$LINENO" 5 ;; esac else DX_FLAG_chi=0 test "$DX_FLAG_chm" = "1" || DX_FLAG_chi=0 fi if test "$DX_FLAG_chi" = 1; then : fi if test "$DX_FLAG_chi" = 1; then DX_ENV="$DX_ENV GENERATE_CHI='YES'" GENERATE_CHI=YES : else DX_ENV="$DX_ENV GENERATE_CHI='NO'" GENERATE_CHI=NO : fi # Plain HTML pages generation: # Check whether --enable-doxygen-html was given. if test "${enable_doxygen_html+set}" = set; then : enableval=$enable_doxygen_html; case "$enableval" in #( y|Y|yes|Yes|YES) DX_FLAG_html=1 test "$DX_FLAG_doc" = "1" \ || as_fn_error $? "doxygen-html requires doxygen-doc" "$LINENO" 5 test "$DX_FLAG_chm" = "0" \ || as_fn_error $? "doxygen-html contradicts doxygen-chm" "$LINENO" 5 ;; #( n|N|no|No|NO) DX_FLAG_html=0 ;; #( *) as_fn_error $? "invalid value '$enableval' given to doxygen-html" "$LINENO" 5 ;; esac else DX_FLAG_html=1 test "$DX_FLAG_doc" = "1" || DX_FLAG_html=0 test "$DX_FLAG_chm" = "0" || DX_FLAG_html=0 fi if test "$DX_FLAG_html" = 1; then : fi if test "$DX_FLAG_html" = 1; then DX_ENV="$DX_ENV GENERATE_HTML='YES'" GENERATE_HTML=YES : else test "$DX_FLAG_chm" = 1 || DX_ENV="$DX_ENV GENERATE_HTML='NO'" GENERATE_HTML=NO : fi # PostScript file generation: # Check whether --enable-doxygen-ps was given. if test "${enable_doxygen_ps+set}" = set; then : enableval=$enable_doxygen_ps; case "$enableval" in #( y|Y|yes|Yes|YES) DX_FLAG_ps=1 test "$DX_FLAG_doc" = "1" \ || as_fn_error $? "doxygen-ps requires doxygen-doc" "$LINENO" 5 ;; #( n|N|no|No|NO) DX_FLAG_ps=0 ;; #( *) as_fn_error $? "invalid value '$enableval' given to doxygen-ps" "$LINENO" 5 ;; esac else DX_FLAG_ps=0 test "$DX_FLAG_doc" = "1" || DX_FLAG_ps=0 fi if test "$DX_FLAG_ps" = 1; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}latex", so it can be a program name with args. set dummy ${ac_tool_prefix}latex; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_DX_LATEX+:} false; then : $as_echo_n "(cached) " >&6 else case $DX_LATEX in [\\/]* | ?:[\\/]*) ac_cv_path_DX_LATEX="$DX_LATEX" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_DX_LATEX="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi DX_LATEX=$ac_cv_path_DX_LATEX if test -n "$DX_LATEX"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DX_LATEX" >&5 $as_echo "$DX_LATEX" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_path_DX_LATEX"; then ac_pt_DX_LATEX=$DX_LATEX # Extract the first word of "latex", so it can be a program name with args. set dummy latex; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_ac_pt_DX_LATEX+:} false; then : $as_echo_n "(cached) " >&6 else case $ac_pt_DX_LATEX in [\\/]* | ?:[\\/]*) ac_cv_path_ac_pt_DX_LATEX="$ac_pt_DX_LATEX" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_ac_pt_DX_LATEX="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi ac_pt_DX_LATEX=$ac_cv_path_ac_pt_DX_LATEX if test -n "$ac_pt_DX_LATEX"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_DX_LATEX" >&5 $as_echo "$ac_pt_DX_LATEX" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_pt_DX_LATEX" = x; then DX_LATEX="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac DX_LATEX=$ac_pt_DX_LATEX fi else DX_LATEX="$ac_cv_path_DX_LATEX" fi if test "$DX_FLAG_ps$DX_LATEX" = 1; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: latex not found - will not generate doxygen PostScript documentation" >&5 $as_echo "$as_me: WARNING: latex not found - will not generate doxygen PostScript documentation" >&2;} DX_FLAG_ps=0 fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}makeindex", so it can be a program name with args. set dummy ${ac_tool_prefix}makeindex; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_DX_MAKEINDEX+:} false; then : $as_echo_n "(cached) " >&6 else case $DX_MAKEINDEX in [\\/]* | ?:[\\/]*) ac_cv_path_DX_MAKEINDEX="$DX_MAKEINDEX" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_DX_MAKEINDEX="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi DX_MAKEINDEX=$ac_cv_path_DX_MAKEINDEX if test -n "$DX_MAKEINDEX"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DX_MAKEINDEX" >&5 $as_echo "$DX_MAKEINDEX" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_path_DX_MAKEINDEX"; then ac_pt_DX_MAKEINDEX=$DX_MAKEINDEX # Extract the first word of "makeindex", so it can be a program name with args. set dummy makeindex; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_ac_pt_DX_MAKEINDEX+:} false; then : $as_echo_n "(cached) " >&6 else case $ac_pt_DX_MAKEINDEX in [\\/]* | ?:[\\/]*) ac_cv_path_ac_pt_DX_MAKEINDEX="$ac_pt_DX_MAKEINDEX" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_ac_pt_DX_MAKEINDEX="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi ac_pt_DX_MAKEINDEX=$ac_cv_path_ac_pt_DX_MAKEINDEX if test -n "$ac_pt_DX_MAKEINDEX"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_DX_MAKEINDEX" >&5 $as_echo "$ac_pt_DX_MAKEINDEX" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_pt_DX_MAKEINDEX" = x; then DX_MAKEINDEX="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac DX_MAKEINDEX=$ac_pt_DX_MAKEINDEX fi else DX_MAKEINDEX="$ac_cv_path_DX_MAKEINDEX" fi if test "$DX_FLAG_ps$DX_MAKEINDEX" = 1; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: makeindex not found - will not generate doxygen PostScript documentation" >&5 $as_echo "$as_me: WARNING: makeindex not found - will not generate doxygen PostScript documentation" >&2;} DX_FLAG_ps=0 fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}dvips", so it can be a program name with args. set dummy ${ac_tool_prefix}dvips; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_DX_DVIPS+:} false; then : $as_echo_n "(cached) " >&6 else case $DX_DVIPS in [\\/]* | ?:[\\/]*) ac_cv_path_DX_DVIPS="$DX_DVIPS" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_DX_DVIPS="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi DX_DVIPS=$ac_cv_path_DX_DVIPS if test -n "$DX_DVIPS"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DX_DVIPS" >&5 $as_echo "$DX_DVIPS" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_path_DX_DVIPS"; then ac_pt_DX_DVIPS=$DX_DVIPS # Extract the first word of "dvips", so it can be a program name with args. set dummy dvips; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_ac_pt_DX_DVIPS+:} false; then : $as_echo_n "(cached) " >&6 else case $ac_pt_DX_DVIPS in [\\/]* | ?:[\\/]*) ac_cv_path_ac_pt_DX_DVIPS="$ac_pt_DX_DVIPS" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_ac_pt_DX_DVIPS="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi ac_pt_DX_DVIPS=$ac_cv_path_ac_pt_DX_DVIPS if test -n "$ac_pt_DX_DVIPS"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_DX_DVIPS" >&5 $as_echo "$ac_pt_DX_DVIPS" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_pt_DX_DVIPS" = x; then DX_DVIPS="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac DX_DVIPS=$ac_pt_DX_DVIPS fi else DX_DVIPS="$ac_cv_path_DX_DVIPS" fi if test "$DX_FLAG_ps$DX_DVIPS" = 1; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: dvips not found - will not generate doxygen PostScript documentation" >&5 $as_echo "$as_me: WARNING: dvips not found - will not generate doxygen PostScript documentation" >&2;} DX_FLAG_ps=0 fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}egrep", so it can be a program name with args. set dummy ${ac_tool_prefix}egrep; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_DX_EGREP+:} false; then : $as_echo_n "(cached) " >&6 else case $DX_EGREP in [\\/]* | ?:[\\/]*) ac_cv_path_DX_EGREP="$DX_EGREP" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_DX_EGREP="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi DX_EGREP=$ac_cv_path_DX_EGREP if test -n "$DX_EGREP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DX_EGREP" >&5 $as_echo "$DX_EGREP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_path_DX_EGREP"; then ac_pt_DX_EGREP=$DX_EGREP # Extract the first word of "egrep", so it can be a program name with args. set dummy egrep; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_ac_pt_DX_EGREP+:} false; then : $as_echo_n "(cached) " >&6 else case $ac_pt_DX_EGREP in [\\/]* | ?:[\\/]*) ac_cv_path_ac_pt_DX_EGREP="$ac_pt_DX_EGREP" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_ac_pt_DX_EGREP="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi ac_pt_DX_EGREP=$ac_cv_path_ac_pt_DX_EGREP if test -n "$ac_pt_DX_EGREP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_DX_EGREP" >&5 $as_echo "$ac_pt_DX_EGREP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_pt_DX_EGREP" = x; then DX_EGREP="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac DX_EGREP=$ac_pt_DX_EGREP fi else DX_EGREP="$ac_cv_path_DX_EGREP" fi if test "$DX_FLAG_ps$DX_EGREP" = 1; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: egrep not found - will not generate doxygen PostScript documentation" >&5 $as_echo "$as_me: WARNING: egrep not found - will not generate doxygen PostScript documentation" >&2;} DX_FLAG_ps=0 fi : fi if test "$DX_FLAG_ps" = 1; then : else : fi # PDF file generation: # Check whether --enable-doxygen-pdf was given. if test "${enable_doxygen_pdf+set}" = set; then : enableval=$enable_doxygen_pdf; case "$enableval" in #( y|Y|yes|Yes|YES) DX_FLAG_pdf=1 test "$DX_FLAG_doc" = "1" \ || as_fn_error $? "doxygen-pdf requires doxygen-doc" "$LINENO" 5 ;; #( n|N|no|No|NO) DX_FLAG_pdf=0 ;; #( *) as_fn_error $? "invalid value '$enableval' given to doxygen-pdf" "$LINENO" 5 ;; esac else DX_FLAG_pdf=0 test "$DX_FLAG_doc" = "1" || DX_FLAG_pdf=0 fi if test "$DX_FLAG_pdf" = 1; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}pdflatex", so it can be a program name with args. set dummy ${ac_tool_prefix}pdflatex; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_DX_PDFLATEX+:} false; then : $as_echo_n "(cached) " >&6 else case $DX_PDFLATEX in [\\/]* | ?:[\\/]*) ac_cv_path_DX_PDFLATEX="$DX_PDFLATEX" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_DX_PDFLATEX="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi DX_PDFLATEX=$ac_cv_path_DX_PDFLATEX if test -n "$DX_PDFLATEX"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DX_PDFLATEX" >&5 $as_echo "$DX_PDFLATEX" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_path_DX_PDFLATEX"; then ac_pt_DX_PDFLATEX=$DX_PDFLATEX # Extract the first word of "pdflatex", so it can be a program name with args. set dummy pdflatex; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_ac_pt_DX_PDFLATEX+:} false; then : $as_echo_n "(cached) " >&6 else case $ac_pt_DX_PDFLATEX in [\\/]* | ?:[\\/]*) ac_cv_path_ac_pt_DX_PDFLATEX="$ac_pt_DX_PDFLATEX" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_ac_pt_DX_PDFLATEX="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi ac_pt_DX_PDFLATEX=$ac_cv_path_ac_pt_DX_PDFLATEX if test -n "$ac_pt_DX_PDFLATEX"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_DX_PDFLATEX" >&5 $as_echo "$ac_pt_DX_PDFLATEX" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_pt_DX_PDFLATEX" = x; then DX_PDFLATEX="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac DX_PDFLATEX=$ac_pt_DX_PDFLATEX fi else DX_PDFLATEX="$ac_cv_path_DX_PDFLATEX" fi if test "$DX_FLAG_pdf$DX_PDFLATEX" = 1; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: pdflatex not found - will not generate doxygen PDF documentation" >&5 $as_echo "$as_me: WARNING: pdflatex not found - will not generate doxygen PDF documentation" >&2;} DX_FLAG_pdf=0 fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}makeindex", so it can be a program name with args. set dummy ${ac_tool_prefix}makeindex; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_DX_MAKEINDEX+:} false; then : $as_echo_n "(cached) " >&6 else case $DX_MAKEINDEX in [\\/]* | ?:[\\/]*) ac_cv_path_DX_MAKEINDEX="$DX_MAKEINDEX" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_DX_MAKEINDEX="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi DX_MAKEINDEX=$ac_cv_path_DX_MAKEINDEX if test -n "$DX_MAKEINDEX"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DX_MAKEINDEX" >&5 $as_echo "$DX_MAKEINDEX" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_path_DX_MAKEINDEX"; then ac_pt_DX_MAKEINDEX=$DX_MAKEINDEX # Extract the first word of "makeindex", so it can be a program name with args. set dummy makeindex; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_ac_pt_DX_MAKEINDEX+:} false; then : $as_echo_n "(cached) " >&6 else case $ac_pt_DX_MAKEINDEX in [\\/]* | ?:[\\/]*) ac_cv_path_ac_pt_DX_MAKEINDEX="$ac_pt_DX_MAKEINDEX" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_ac_pt_DX_MAKEINDEX="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi ac_pt_DX_MAKEINDEX=$ac_cv_path_ac_pt_DX_MAKEINDEX if test -n "$ac_pt_DX_MAKEINDEX"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_DX_MAKEINDEX" >&5 $as_echo "$ac_pt_DX_MAKEINDEX" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_pt_DX_MAKEINDEX" = x; then DX_MAKEINDEX="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac DX_MAKEINDEX=$ac_pt_DX_MAKEINDEX fi else DX_MAKEINDEX="$ac_cv_path_DX_MAKEINDEX" fi if test "$DX_FLAG_pdf$DX_MAKEINDEX" = 1; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: makeindex not found - will not generate doxygen PDF documentation" >&5 $as_echo "$as_me: WARNING: makeindex not found - will not generate doxygen PDF documentation" >&2;} DX_FLAG_pdf=0 fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}egrep", so it can be a program name with args. set dummy ${ac_tool_prefix}egrep; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_DX_EGREP+:} false; then : $as_echo_n "(cached) " >&6 else case $DX_EGREP in [\\/]* | ?:[\\/]*) ac_cv_path_DX_EGREP="$DX_EGREP" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_DX_EGREP="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi DX_EGREP=$ac_cv_path_DX_EGREP if test -n "$DX_EGREP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DX_EGREP" >&5 $as_echo "$DX_EGREP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_path_DX_EGREP"; then ac_pt_DX_EGREP=$DX_EGREP # Extract the first word of "egrep", so it can be a program name with args. set dummy egrep; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_ac_pt_DX_EGREP+:} false; then : $as_echo_n "(cached) " >&6 else case $ac_pt_DX_EGREP in [\\/]* | ?:[\\/]*) ac_cv_path_ac_pt_DX_EGREP="$ac_pt_DX_EGREP" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_ac_pt_DX_EGREP="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi ac_pt_DX_EGREP=$ac_cv_path_ac_pt_DX_EGREP if test -n "$ac_pt_DX_EGREP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_DX_EGREP" >&5 $as_echo "$ac_pt_DX_EGREP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_pt_DX_EGREP" = x; then DX_EGREP="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac DX_EGREP=$ac_pt_DX_EGREP fi else DX_EGREP="$ac_cv_path_DX_EGREP" fi if test "$DX_FLAG_pdf$DX_EGREP" = 1; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: egrep not found - will not generate doxygen PDF documentation" >&5 $as_echo "$as_me: WARNING: egrep not found - will not generate doxygen PDF documentation" >&2;} DX_FLAG_pdf=0 fi : fi if test "$DX_FLAG_pdf" = 1; then : else : fi # LaTeX generation for PS and/or PDF: if test "$DX_FLAG_ps" = 1 || test "$DX_FLAG_pdf" = 1; then DX_ENV="$DX_ENV GENERATE_LATEX='YES'" GENERATE_LATEX=YES else DX_ENV="$DX_ENV GENERATE_LATEX='NO'" GENERATE_LATEX=NO fi # Paper size for PS and/or PDF: case "$DOXYGEN_PAPER_SIZE" in #( "") DOXYGEN_PAPER_SIZE="" ;; #( a4wide|a4|letter|legal|executive) DX_ENV="$DX_ENV PAPER_SIZE='$DOXYGEN_PAPER_SIZE'" PAPER_SIZE=$DOXYGEN_PAPER_SIZE ;; #( *) as_fn_error $? "unknown DOXYGEN_PAPER_SIZE='$DOXYGEN_PAPER_SIZE'" "$LINENO" 5 ;; esac # Rules: if test $DX_FLAG_html -eq 1; then : DX_SNIPPET_html="## ------------------------------- ## ## Rules specific for HTML output. ## ## ------------------------------- ## DX_CLEAN_HTML = \$(DX_DOCDIR)/html\\ \$(DX_DOCDIR)/html " else DX_SNIPPET_html="" fi if test $DX_FLAG_chi -eq 1; then : DX_SNIPPET_chi=" DX_CLEAN_CHI = \$(DX_DOCDIR)/\$(PACKAGE).chi\\ \$(DX_DOCDIR)/\$(PACKAGE).chi" else DX_SNIPPET_chi="" fi if test $DX_FLAG_chm -eq 1; then : DX_SNIPPET_chm="## ------------------------------ ## ## Rules specific for CHM output. ## ## ------------------------------ ## DX_CLEAN_CHM = \$(DX_DOCDIR)/chm\\ \$(DX_DOCDIR)/chm\ ${DX_SNIPPET_chi} " else DX_SNIPPET_chm="" fi if test $DX_FLAG_man -eq 1; then : DX_SNIPPET_man="## ------------------------------ ## ## Rules specific for MAN output. ## ## ------------------------------ ## DX_CLEAN_MAN = \$(DX_DOCDIR)/man\\ \$(DX_DOCDIR)/man " else DX_SNIPPET_man="" fi if test $DX_FLAG_rtf -eq 1; then : DX_SNIPPET_rtf="## ------------------------------ ## ## Rules specific for RTF output. ## ## ------------------------------ ## DX_CLEAN_RTF = \$(DX_DOCDIR)/rtf\\ \$(DX_DOCDIR)/rtf " else DX_SNIPPET_rtf="" fi if test $DX_FLAG_xml -eq 1; then : DX_SNIPPET_xml="## ------------------------------ ## ## Rules specific for XML output. ## ## ------------------------------ ## DX_CLEAN_XML = \$(DX_DOCDIR)/xml\\ \$(DX_DOCDIR)/xml " else DX_SNIPPET_xml="" fi if test $DX_FLAG_ps -eq 1; then : DX_SNIPPET_ps="## ----------------------------- ## ## Rules specific for PS output. ## ## ----------------------------- ## DX_CLEAN_PS = \$(DX_DOCDIR)/\$(PACKAGE).ps\\ \$(DX_DOCDIR)/\$(PACKAGE).ps DX_PS_GOAL = doxygen-ps doxygen-ps: \$(DX_CLEAN_PS) \$(DX_DOCDIR)/\$(PACKAGE).ps: \$(DX_DOCDIR)/\$(PACKAGE).tag \$(DX_V_LATEX)cd \$(DX_DOCDIR)/latex; \\ rm -f *.aux *.toc *.idx *.ind *.ilg *.log *.out; \\ \$(DX_LATEX) refman.tex; \\ \$(DX_MAKEINDEX) refman.idx; \\ \$(DX_LATEX) refman.tex; \\ countdown=5; \\ while \$(DX_EGREP) 'Rerun (LaTeX|to get cross-references right)' \\ refman.log > /dev/null 2>&1 \\ && test \$\$countdown -gt 0; do \\ \$(DX_LATEX) refman.tex; \\ countdown=\`expr \$\$countdown - 1\`; \\ done; \\ \$(DX_DVIPS) -o ../\$(PACKAGE).ps refman.dvi " else DX_SNIPPET_ps="" fi if test $DX_FLAG_pdf -eq 1; then : DX_SNIPPET_pdf="## ------------------------------ ## ## Rules specific for PDF output. ## ## ------------------------------ ## DX_CLEAN_PDF = \$(DX_DOCDIR)/\$(PACKAGE).pdf\\ \$(DX_DOCDIR)/\$(PACKAGE).pdf DX_PDF_GOAL = doxygen-pdf doxygen-pdf: \$(DX_CLEAN_PDF) \$(DX_DOCDIR)/\$(PACKAGE).pdf: \$(DX_DOCDIR)/\$(PACKAGE).tag \$(DX_V_LATEX)cd \$(DX_DOCDIR)/latex; \\ rm -f *.aux *.toc *.idx *.ind *.ilg *.log *.out; \\ \$(DX_PDFLATEX) refman.tex; \\ \$(DX_MAKEINDEX) refman.idx; \\ \$(DX_PDFLATEX) refman.tex; \\ countdown=5; \\ while \$(DX_EGREP) 'Rerun (LaTeX|to get cross-references right)' \\ refman.log > /dev/null 2>&1 \\ && test \$\$countdown -gt 0; do \\ \$(DX_PDFLATEX) refman.tex; \\ countdown=\`expr \$\$countdown - 1\`; \\ done; \\ mv refman.pdf ../\$(PACKAGE).pdf " else DX_SNIPPET_pdf="" fi if test $DX_FLAG_ps -eq 1 -o $DX_FLAG_pdf -eq 1; then : DX_SNIPPET_latex="## ------------------------------------------------- ## ## Rules specific for LaTeX (shared for PS and PDF). ## ## ------------------------------------------------- ## DX_V_LATEX = \$(_DX_v_LATEX_\$(V)) _DX_v_LATEX_ = \$(_DX_v_LATEX_\$(AM_DEFAULT_VERBOSITY)) _DX_v_LATEX_0 = @echo \" LATEX \" \$@; DX_CLEAN_LATEX = \$(DX_DOCDIR)/latex\\ \$(DX_DOCDIR)/latex " else DX_SNIPPET_latex="" fi if test $DX_FLAG_doc -eq 1; then : DX_SNIPPET_doc="## --------------------------------- ## ## Format-independent Doxygen rules. ## ## --------------------------------- ## ${DX_SNIPPET_html}\ ${DX_SNIPPET_chm}\ ${DX_SNIPPET_man}\ ${DX_SNIPPET_rtf}\ ${DX_SNIPPET_xml}\ ${DX_SNIPPET_ps}\ ${DX_SNIPPET_pdf}\ ${DX_SNIPPET_latex}\ DX_V_DXGEN = \$(_DX_v_DXGEN_\$(V)) _DX_v_DXGEN_ = \$(_DX_v_DXGEN_\$(AM_DEFAULT_VERBOSITY)) _DX_v_DXGEN_0 = @echo \" DXGEN \" \$<; .PHONY: doxygen-run doxygen-doc \$(DX_PS_GOAL) \$(DX_PDF_GOAL) .INTERMEDIATE: doxygen-run \$(DX_PS_GOAL) \$(DX_PDF_GOAL) doxygen-run: \$(DX_DOCDIR)/\$(PACKAGE).tag doxygen-doc: doxygen-run \$(DX_PS_GOAL) \$(DX_PDF_GOAL) \$(DX_DOCDIR)/\$(PACKAGE).tag: \$(DX_CONFIG) \$(pkginclude_HEADERS) \$(A""M_V_at)rm -rf \$(DX_DOCDIR) \$(DX_V_DXGEN)\$(DX_ENV) DOCDIR=\$(DX_DOCDIR) \$(DX_DOXYGEN) \$(DX_CONFIG) \$(A""M_V_at)echo Timestamp >\$@ DX_CLEANFILES = \\ \$(DX_DOCDIR)/doxygen_sqlite3.db \\ \$(DX_DOCDIR)/\$(PACKAGE).tag \\ -r \\ \$(DX_CLEAN_HTML) \\ \$(DX_CLEAN_CHM) \\ \$(DX_CLEAN_CHI) \\ \$(DX_CLEAN_MAN) \\ \$(DX_CLEAN_RTF) \\ \$(DX_CLEAN_XML) \\ \$(DX_CLEAN_PS) \\ \$(DX_CLEAN_PDF) \\ \$(DX_CLEAN_LATEX)" else DX_SNIPPET_doc="" fi DX_RULES="${DX_SNIPPET_doc}" #For debugging: #echo DX_FLAG_doc=$DX_FLAG_doc #echo DX_FLAG_dot=$DX_FLAG_dot #echo DX_FLAG_man=$DX_FLAG_man #echo DX_FLAG_html=$DX_FLAG_html #echo DX_FLAG_chm=$DX_FLAG_chm #echo DX_FLAG_chi=$DX_FLAG_chi #echo DX_FLAG_rtf=$DX_FLAG_rtf #echo DX_FLAG_xml=$DX_FLAG_xml #echo DX_FLAG_pdf=$DX_FLAG_pdf #echo DX_FLAG_ps=$DX_FLAG_ps #echo DX_ENV=$DX_ENV cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, we kill variables containing newlines. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. ( for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) # `set' does not quote correctly, so add quotes: double-quote # substitution turns \\\\ into \\, and sed turns \\ into \. sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; #( *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) | sed ' /^ac_cv_env_/b end t clear :clear s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then if test "x$cache_file" != "x/dev/null"; then { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 $as_echo "$as_me: updating cache $cache_file" >&6;} if test ! -f "$cache_file" || test -h "$cache_file"; then cat confcache >"$cache_file" else case $cache_file in #( */* | ?:*) mv -f confcache "$cache_file"$$ && mv -f "$cache_file"$$ "$cache_file" ;; #( *) mv -f confcache "$cache_file" ;; esac fi fi else { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 $as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' # Transform confdefs.h into DEFS. # Protect against shell expansion while executing Makefile rules. # Protect against Makefile macro expansion. # # If the first sed substitution is executed (which looks for macros that # take arguments), then branch to the quote section. Otherwise, # look for a macro that doesn't take arguments. ac_script=' :mline /\\$/{ N s,\\\n,, b mline } t clear :clear s/^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*([^)]*)\)[ ]*\(.*\)/-D\1=\2/g t quote s/^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)/-D\1=\2/g t quote b any :quote s/[ `~#$^&*(){}\\|;'\''"<>?]/\\&/g s/\[/\\&/g s/\]/\\&/g s/\$/$$/g H :any ${ g s/^\n// s/\n/ /g p } ' DEFS=`sed -n "$ac_script" confdefs.h` ac_libobjs= ac_ltlibobjs= U= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' ac_i=`$as_echo "$ac_i" | sed "$ac_script"` # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR # will be set to the directory where LIBOBJS objects are built. as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs { $as_echo "$as_me:${as_lineno-$LINENO}: checking that generated files are newer than configure" >&5 $as_echo_n "checking that generated files are newer than configure... " >&6; } if test -n "$am_sleep_pid"; then # Hide warnings about reused PIDs. wait $am_sleep_pid 2>/dev/null fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: done" >&5 $as_echo "done" >&6; } if test -n "$EXEEXT"; then am__EXEEXT_TRUE= am__EXEEXT_FALSE='#' else am__EXEEXT_TRUE='#' am__EXEEXT_FALSE= fi if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then as_fn_error $? "conditional \"AMDEP\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then as_fn_error $? "conditional \"am__fastdepCC\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then as_fn_error $? "conditional \"am__fastdepCC\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${am__fastdepCXX_TRUE}" && test -z "${am__fastdepCXX_FALSE}"; then as_fn_error $? "conditional \"am__fastdepCXX\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ENABLE_OPENMPT123_TRUE}" && test -z "${ENABLE_OPENMPT123_FALSE}"; then as_fn_error $? "conditional \"ENABLE_OPENMPT123\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ENABLE_EXAMPLES_TRUE}" && test -z "${ENABLE_EXAMPLES_FALSE}"; then as_fn_error $? "conditional \"ENABLE_EXAMPLES\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ENABLE_TESTS_TRUE}" && test -z "${ENABLE_TESTS_FALSE}"; then as_fn_error $? "conditional \"ENABLE_TESTS\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${HAVE_PORTAUDIO_TRUE}" && test -z "${HAVE_PORTAUDIO_FALSE}"; then as_fn_error $? "conditional \"HAVE_PORTAUDIO\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${HAVE_PORTAUDIOCPP_TRUE}" && test -z "${HAVE_PORTAUDIOCPP_FALSE}"; then as_fn_error $? "conditional \"HAVE_PORTAUDIOCPP\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 $as_echo "$as_me: creating $CONFIG_STATUS" >&6;} as_write_fail=0 cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 6>&1 ## ----------------------------------- ## ## Main body of $CONFIG_STATUS script. ## ## ----------------------------------- ## _ASEOF test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Save the log message, to keep $0 and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" This file was extended by libopenmpt $as_me 0.6.1+release.autotools, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ on `(hostname || uname -n) 2>/dev/null | sed 1q` " _ACEOF case $ac_config_files in *" "*) set x $ac_config_files; shift; ac_config_files=$*;; esac cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. config_files="$ac_config_files" config_commands="$ac_config_commands" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ac_cs_usage="\ \`$as_me' instantiates files and other configuration actions from templates according to the current configuration. Unless the files and actions are specified as TAGs, all are instantiated by default. Usage: $0 [OPTION]... [TAG]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit --config print configuration, then exit -q, --quiet, --silent do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE Configuration files: $config_files Configuration commands: $config_commands Report bugs to . libopenmpt home page: ." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ libopenmpt config.status 0.6.1+release.autotools configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" Copyright (C) 2012 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' INSTALL='$INSTALL' MKDIR_P='$MKDIR_P' AWK='$AWK' test -n "\$AWK" || AWK=awk _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # The default lists apply if the user does not specify any file. ac_need_defaults=: while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) $as_echo "$ac_cs_version"; exit ;; --config | --confi | --conf | --con | --co | --c ) $as_echo "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; '') as_fn_error $? "missing file argument" ;; esac as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; --he | --h | --help | --hel | -h ) $as_echo "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) as_fn_error $? "unrecognized option: \`$1' Try \`$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" ac_need_defaults=false ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' export CONFIG_SHELL exec "\$@" fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX $as_echo "$ac_log" } >&5 _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # # INIT-COMMANDS # AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}" # The HP-UX ksh and POSIX shell print the target directory to stdout # if CDPATH is set. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH sed_quote_subst='$sed_quote_subst' double_quote_subst='$double_quote_subst' delay_variable_subst='$delay_variable_subst' macro_version='`$ECHO "$macro_version" | $SED "$delay_single_quote_subst"`' macro_revision='`$ECHO "$macro_revision" | $SED "$delay_single_quote_subst"`' enable_shared='`$ECHO "$enable_shared" | $SED "$delay_single_quote_subst"`' enable_static='`$ECHO "$enable_static" | $SED "$delay_single_quote_subst"`' pic_mode='`$ECHO "$pic_mode" | $SED "$delay_single_quote_subst"`' enable_fast_install='`$ECHO "$enable_fast_install" | $SED "$delay_single_quote_subst"`' shared_archive_member_spec='`$ECHO "$shared_archive_member_spec" | $SED "$delay_single_quote_subst"`' SHELL='`$ECHO "$SHELL" | $SED "$delay_single_quote_subst"`' ECHO='`$ECHO "$ECHO" | $SED "$delay_single_quote_subst"`' PATH_SEPARATOR='`$ECHO "$PATH_SEPARATOR" | $SED "$delay_single_quote_subst"`' host_alias='`$ECHO "$host_alias" | $SED "$delay_single_quote_subst"`' host='`$ECHO "$host" | $SED "$delay_single_quote_subst"`' host_os='`$ECHO "$host_os" | $SED "$delay_single_quote_subst"`' build_alias='`$ECHO "$build_alias" | $SED "$delay_single_quote_subst"`' build='`$ECHO "$build" | $SED "$delay_single_quote_subst"`' build_os='`$ECHO "$build_os" | $SED "$delay_single_quote_subst"`' SED='`$ECHO "$SED" | $SED "$delay_single_quote_subst"`' Xsed='`$ECHO "$Xsed" | $SED "$delay_single_quote_subst"`' GREP='`$ECHO "$GREP" | $SED "$delay_single_quote_subst"`' EGREP='`$ECHO "$EGREP" | $SED "$delay_single_quote_subst"`' FGREP='`$ECHO "$FGREP" | $SED "$delay_single_quote_subst"`' LD='`$ECHO "$LD" | $SED "$delay_single_quote_subst"`' NM='`$ECHO "$NM" | $SED "$delay_single_quote_subst"`' LN_S='`$ECHO "$LN_S" | $SED "$delay_single_quote_subst"`' max_cmd_len='`$ECHO "$max_cmd_len" | $SED "$delay_single_quote_subst"`' ac_objext='`$ECHO "$ac_objext" | $SED "$delay_single_quote_subst"`' exeext='`$ECHO "$exeext" | $SED "$delay_single_quote_subst"`' lt_unset='`$ECHO "$lt_unset" | $SED "$delay_single_quote_subst"`' lt_SP2NL='`$ECHO "$lt_SP2NL" | $SED "$delay_single_quote_subst"`' lt_NL2SP='`$ECHO "$lt_NL2SP" | $SED "$delay_single_quote_subst"`' lt_cv_to_host_file_cmd='`$ECHO "$lt_cv_to_host_file_cmd" | $SED "$delay_single_quote_subst"`' lt_cv_to_tool_file_cmd='`$ECHO "$lt_cv_to_tool_file_cmd" | $SED "$delay_single_quote_subst"`' reload_flag='`$ECHO "$reload_flag" | $SED "$delay_single_quote_subst"`' reload_cmds='`$ECHO "$reload_cmds" | $SED "$delay_single_quote_subst"`' OBJDUMP='`$ECHO "$OBJDUMP" | $SED "$delay_single_quote_subst"`' deplibs_check_method='`$ECHO "$deplibs_check_method" | $SED "$delay_single_quote_subst"`' file_magic_cmd='`$ECHO "$file_magic_cmd" | $SED "$delay_single_quote_subst"`' file_magic_glob='`$ECHO "$file_magic_glob" | $SED "$delay_single_quote_subst"`' want_nocaseglob='`$ECHO "$want_nocaseglob" | $SED "$delay_single_quote_subst"`' DLLTOOL='`$ECHO "$DLLTOOL" | $SED "$delay_single_quote_subst"`' sharedlib_from_linklib_cmd='`$ECHO "$sharedlib_from_linklib_cmd" | $SED "$delay_single_quote_subst"`' AR='`$ECHO "$AR" | $SED "$delay_single_quote_subst"`' AR_FLAGS='`$ECHO "$AR_FLAGS" | $SED "$delay_single_quote_subst"`' archiver_list_spec='`$ECHO "$archiver_list_spec" | $SED "$delay_single_quote_subst"`' STRIP='`$ECHO "$STRIP" | $SED "$delay_single_quote_subst"`' RANLIB='`$ECHO "$RANLIB" | $SED "$delay_single_quote_subst"`' old_postinstall_cmds='`$ECHO "$old_postinstall_cmds" | $SED "$delay_single_quote_subst"`' old_postuninstall_cmds='`$ECHO "$old_postuninstall_cmds" | $SED "$delay_single_quote_subst"`' old_archive_cmds='`$ECHO "$old_archive_cmds" | $SED "$delay_single_quote_subst"`' lock_old_archive_extraction='`$ECHO "$lock_old_archive_extraction" | $SED "$delay_single_quote_subst"`' CC='`$ECHO "$CC" | $SED "$delay_single_quote_subst"`' CFLAGS='`$ECHO "$CFLAGS" | $SED "$delay_single_quote_subst"`' compiler='`$ECHO "$compiler" | $SED "$delay_single_quote_subst"`' GCC='`$ECHO "$GCC" | $SED "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_pipe='`$ECHO "$lt_cv_sys_global_symbol_pipe" | $SED "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_to_cdecl='`$ECHO "$lt_cv_sys_global_symbol_to_cdecl" | $SED "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_to_import='`$ECHO "$lt_cv_sys_global_symbol_to_import" | $SED "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_to_c_name_address='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address" | $SED "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_to_c_name_address_lib_prefix='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address_lib_prefix" | $SED "$delay_single_quote_subst"`' lt_cv_nm_interface='`$ECHO "$lt_cv_nm_interface" | $SED "$delay_single_quote_subst"`' nm_file_list_spec='`$ECHO "$nm_file_list_spec" | $SED "$delay_single_quote_subst"`' lt_sysroot='`$ECHO "$lt_sysroot" | $SED "$delay_single_quote_subst"`' lt_cv_truncate_bin='`$ECHO "$lt_cv_truncate_bin" | $SED "$delay_single_quote_subst"`' objdir='`$ECHO "$objdir" | $SED "$delay_single_quote_subst"`' MAGIC_CMD='`$ECHO "$MAGIC_CMD" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_no_builtin_flag='`$ECHO "$lt_prog_compiler_no_builtin_flag" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_pic='`$ECHO "$lt_prog_compiler_pic" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_wl='`$ECHO "$lt_prog_compiler_wl" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_static='`$ECHO "$lt_prog_compiler_static" | $SED "$delay_single_quote_subst"`' lt_cv_prog_compiler_c_o='`$ECHO "$lt_cv_prog_compiler_c_o" | $SED "$delay_single_quote_subst"`' need_locks='`$ECHO "$need_locks" | $SED "$delay_single_quote_subst"`' MANIFEST_TOOL='`$ECHO "$MANIFEST_TOOL" | $SED "$delay_single_quote_subst"`' DSYMUTIL='`$ECHO "$DSYMUTIL" | $SED "$delay_single_quote_subst"`' NMEDIT='`$ECHO "$NMEDIT" | $SED "$delay_single_quote_subst"`' LIPO='`$ECHO "$LIPO" | $SED "$delay_single_quote_subst"`' OTOOL='`$ECHO "$OTOOL" | $SED "$delay_single_quote_subst"`' OTOOL64='`$ECHO "$OTOOL64" | $SED "$delay_single_quote_subst"`' libext='`$ECHO "$libext" | $SED "$delay_single_quote_subst"`' shrext_cmds='`$ECHO "$shrext_cmds" | $SED "$delay_single_quote_subst"`' extract_expsyms_cmds='`$ECHO "$extract_expsyms_cmds" | $SED "$delay_single_quote_subst"`' archive_cmds_need_lc='`$ECHO "$archive_cmds_need_lc" | $SED "$delay_single_quote_subst"`' enable_shared_with_static_runtimes='`$ECHO "$enable_shared_with_static_runtimes" | $SED "$delay_single_quote_subst"`' export_dynamic_flag_spec='`$ECHO "$export_dynamic_flag_spec" | $SED "$delay_single_quote_subst"`' whole_archive_flag_spec='`$ECHO "$whole_archive_flag_spec" | $SED "$delay_single_quote_subst"`' compiler_needs_object='`$ECHO "$compiler_needs_object" | $SED "$delay_single_quote_subst"`' old_archive_from_new_cmds='`$ECHO "$old_archive_from_new_cmds" | $SED "$delay_single_quote_subst"`' old_archive_from_expsyms_cmds='`$ECHO "$old_archive_from_expsyms_cmds" | $SED "$delay_single_quote_subst"`' archive_cmds='`$ECHO "$archive_cmds" | $SED "$delay_single_quote_subst"`' archive_expsym_cmds='`$ECHO "$archive_expsym_cmds" | $SED "$delay_single_quote_subst"`' module_cmds='`$ECHO "$module_cmds" | $SED "$delay_single_quote_subst"`' module_expsym_cmds='`$ECHO "$module_expsym_cmds" | $SED "$delay_single_quote_subst"`' with_gnu_ld='`$ECHO "$with_gnu_ld" | $SED "$delay_single_quote_subst"`' allow_undefined_flag='`$ECHO "$allow_undefined_flag" | $SED "$delay_single_quote_subst"`' no_undefined_flag='`$ECHO "$no_undefined_flag" | $SED "$delay_single_quote_subst"`' hardcode_libdir_flag_spec='`$ECHO "$hardcode_libdir_flag_spec" | $SED "$delay_single_quote_subst"`' hardcode_libdir_separator='`$ECHO "$hardcode_libdir_separator" | $SED "$delay_single_quote_subst"`' hardcode_direct='`$ECHO "$hardcode_direct" | $SED "$delay_single_quote_subst"`' hardcode_direct_absolute='`$ECHO "$hardcode_direct_absolute" | $SED "$delay_single_quote_subst"`' hardcode_minus_L='`$ECHO "$hardcode_minus_L" | $SED "$delay_single_quote_subst"`' hardcode_shlibpath_var='`$ECHO "$hardcode_shlibpath_var" | $SED "$delay_single_quote_subst"`' hardcode_automatic='`$ECHO "$hardcode_automatic" | $SED "$delay_single_quote_subst"`' inherit_rpath='`$ECHO "$inherit_rpath" | $SED "$delay_single_quote_subst"`' link_all_deplibs='`$ECHO "$link_all_deplibs" | $SED "$delay_single_quote_subst"`' always_export_symbols='`$ECHO "$always_export_symbols" | $SED "$delay_single_quote_subst"`' export_symbols_cmds='`$ECHO "$export_symbols_cmds" | $SED "$delay_single_quote_subst"`' exclude_expsyms='`$ECHO "$exclude_expsyms" | $SED "$delay_single_quote_subst"`' include_expsyms='`$ECHO "$include_expsyms" | $SED "$delay_single_quote_subst"`' prelink_cmds='`$ECHO "$prelink_cmds" | $SED "$delay_single_quote_subst"`' postlink_cmds='`$ECHO "$postlink_cmds" | $SED "$delay_single_quote_subst"`' file_list_spec='`$ECHO "$file_list_spec" | $SED "$delay_single_quote_subst"`' variables_saved_for_relink='`$ECHO "$variables_saved_for_relink" | $SED "$delay_single_quote_subst"`' need_lib_prefix='`$ECHO "$need_lib_prefix" | $SED "$delay_single_quote_subst"`' need_version='`$ECHO "$need_version" | $SED "$delay_single_quote_subst"`' version_type='`$ECHO "$version_type" | $SED "$delay_single_quote_subst"`' runpath_var='`$ECHO "$runpath_var" | $SED "$delay_single_quote_subst"`' shlibpath_var='`$ECHO "$shlibpath_var" | $SED "$delay_single_quote_subst"`' shlibpath_overrides_runpath='`$ECHO "$shlibpath_overrides_runpath" | $SED "$delay_single_quote_subst"`' libname_spec='`$ECHO "$libname_spec" | $SED "$delay_single_quote_subst"`' library_names_spec='`$ECHO "$library_names_spec" | $SED "$delay_single_quote_subst"`' soname_spec='`$ECHO "$soname_spec" | $SED "$delay_single_quote_subst"`' install_override_mode='`$ECHO "$install_override_mode" | $SED "$delay_single_quote_subst"`' postinstall_cmds='`$ECHO "$postinstall_cmds" | $SED "$delay_single_quote_subst"`' postuninstall_cmds='`$ECHO "$postuninstall_cmds" | $SED "$delay_single_quote_subst"`' finish_cmds='`$ECHO "$finish_cmds" | $SED "$delay_single_quote_subst"`' finish_eval='`$ECHO "$finish_eval" | $SED "$delay_single_quote_subst"`' hardcode_into_libs='`$ECHO "$hardcode_into_libs" | $SED "$delay_single_quote_subst"`' sys_lib_search_path_spec='`$ECHO "$sys_lib_search_path_spec" | $SED "$delay_single_quote_subst"`' configure_time_dlsearch_path='`$ECHO "$configure_time_dlsearch_path" | $SED "$delay_single_quote_subst"`' configure_time_lt_sys_library_path='`$ECHO "$configure_time_lt_sys_library_path" | $SED "$delay_single_quote_subst"`' hardcode_action='`$ECHO "$hardcode_action" | $SED "$delay_single_quote_subst"`' enable_dlopen='`$ECHO "$enable_dlopen" | $SED "$delay_single_quote_subst"`' enable_dlopen_self='`$ECHO "$enable_dlopen_self" | $SED "$delay_single_quote_subst"`' enable_dlopen_self_static='`$ECHO "$enable_dlopen_self_static" | $SED "$delay_single_quote_subst"`' old_striplib='`$ECHO "$old_striplib" | $SED "$delay_single_quote_subst"`' striplib='`$ECHO "$striplib" | $SED "$delay_single_quote_subst"`' compiler_lib_search_dirs='`$ECHO "$compiler_lib_search_dirs" | $SED "$delay_single_quote_subst"`' predep_objects='`$ECHO "$predep_objects" | $SED "$delay_single_quote_subst"`' postdep_objects='`$ECHO "$postdep_objects" | $SED "$delay_single_quote_subst"`' predeps='`$ECHO "$predeps" | $SED "$delay_single_quote_subst"`' postdeps='`$ECHO "$postdeps" | $SED "$delay_single_quote_subst"`' compiler_lib_search_path='`$ECHO "$compiler_lib_search_path" | $SED "$delay_single_quote_subst"`' LD_CXX='`$ECHO "$LD_CXX" | $SED "$delay_single_quote_subst"`' reload_flag_CXX='`$ECHO "$reload_flag_CXX" | $SED "$delay_single_quote_subst"`' reload_cmds_CXX='`$ECHO "$reload_cmds_CXX" | $SED "$delay_single_quote_subst"`' old_archive_cmds_CXX='`$ECHO "$old_archive_cmds_CXX" | $SED "$delay_single_quote_subst"`' compiler_CXX='`$ECHO "$compiler_CXX" | $SED "$delay_single_quote_subst"`' GCC_CXX='`$ECHO "$GCC_CXX" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_no_builtin_flag_CXX='`$ECHO "$lt_prog_compiler_no_builtin_flag_CXX" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_pic_CXX='`$ECHO "$lt_prog_compiler_pic_CXX" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_wl_CXX='`$ECHO "$lt_prog_compiler_wl_CXX" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_static_CXX='`$ECHO "$lt_prog_compiler_static_CXX" | $SED "$delay_single_quote_subst"`' lt_cv_prog_compiler_c_o_CXX='`$ECHO "$lt_cv_prog_compiler_c_o_CXX" | $SED "$delay_single_quote_subst"`' archive_cmds_need_lc_CXX='`$ECHO "$archive_cmds_need_lc_CXX" | $SED "$delay_single_quote_subst"`' enable_shared_with_static_runtimes_CXX='`$ECHO "$enable_shared_with_static_runtimes_CXX" | $SED "$delay_single_quote_subst"`' export_dynamic_flag_spec_CXX='`$ECHO "$export_dynamic_flag_spec_CXX" | $SED "$delay_single_quote_subst"`' whole_archive_flag_spec_CXX='`$ECHO "$whole_archive_flag_spec_CXX" | $SED "$delay_single_quote_subst"`' compiler_needs_object_CXX='`$ECHO "$compiler_needs_object_CXX" | $SED "$delay_single_quote_subst"`' old_archive_from_new_cmds_CXX='`$ECHO "$old_archive_from_new_cmds_CXX" | $SED "$delay_single_quote_subst"`' old_archive_from_expsyms_cmds_CXX='`$ECHO "$old_archive_from_expsyms_cmds_CXX" | $SED "$delay_single_quote_subst"`' archive_cmds_CXX='`$ECHO "$archive_cmds_CXX" | $SED "$delay_single_quote_subst"`' archive_expsym_cmds_CXX='`$ECHO "$archive_expsym_cmds_CXX" | $SED "$delay_single_quote_subst"`' module_cmds_CXX='`$ECHO "$module_cmds_CXX" | $SED "$delay_single_quote_subst"`' module_expsym_cmds_CXX='`$ECHO "$module_expsym_cmds_CXX" | $SED "$delay_single_quote_subst"`' with_gnu_ld_CXX='`$ECHO "$with_gnu_ld_CXX" | $SED "$delay_single_quote_subst"`' allow_undefined_flag_CXX='`$ECHO "$allow_undefined_flag_CXX" | $SED "$delay_single_quote_subst"`' no_undefined_flag_CXX='`$ECHO "$no_undefined_flag_CXX" | $SED "$delay_single_quote_subst"`' hardcode_libdir_flag_spec_CXX='`$ECHO "$hardcode_libdir_flag_spec_CXX" | $SED "$delay_single_quote_subst"`' hardcode_libdir_separator_CXX='`$ECHO "$hardcode_libdir_separator_CXX" | $SED "$delay_single_quote_subst"`' hardcode_direct_CXX='`$ECHO "$hardcode_direct_CXX" | $SED "$delay_single_quote_subst"`' hardcode_direct_absolute_CXX='`$ECHO "$hardcode_direct_absolute_CXX" | $SED "$delay_single_quote_subst"`' hardcode_minus_L_CXX='`$ECHO "$hardcode_minus_L_CXX" | $SED "$delay_single_quote_subst"`' hardcode_shlibpath_var_CXX='`$ECHO "$hardcode_shlibpath_var_CXX" | $SED "$delay_single_quote_subst"`' hardcode_automatic_CXX='`$ECHO "$hardcode_automatic_CXX" | $SED "$delay_single_quote_subst"`' inherit_rpath_CXX='`$ECHO "$inherit_rpath_CXX" | $SED "$delay_single_quote_subst"`' link_all_deplibs_CXX='`$ECHO "$link_all_deplibs_CXX" | $SED "$delay_single_quote_subst"`' always_export_symbols_CXX='`$ECHO "$always_export_symbols_CXX" | $SED "$delay_single_quote_subst"`' export_symbols_cmds_CXX='`$ECHO "$export_symbols_cmds_CXX" | $SED "$delay_single_quote_subst"`' exclude_expsyms_CXX='`$ECHO "$exclude_expsyms_CXX" | $SED "$delay_single_quote_subst"`' include_expsyms_CXX='`$ECHO "$include_expsyms_CXX" | $SED "$delay_single_quote_subst"`' prelink_cmds_CXX='`$ECHO "$prelink_cmds_CXX" | $SED "$delay_single_quote_subst"`' postlink_cmds_CXX='`$ECHO "$postlink_cmds_CXX" | $SED "$delay_single_quote_subst"`' file_list_spec_CXX='`$ECHO "$file_list_spec_CXX" | $SED "$delay_single_quote_subst"`' hardcode_action_CXX='`$ECHO "$hardcode_action_CXX" | $SED "$delay_single_quote_subst"`' compiler_lib_search_dirs_CXX='`$ECHO "$compiler_lib_search_dirs_CXX" | $SED "$delay_single_quote_subst"`' predep_objects_CXX='`$ECHO "$predep_objects_CXX" | $SED "$delay_single_quote_subst"`' postdep_objects_CXX='`$ECHO "$postdep_objects_CXX" | $SED "$delay_single_quote_subst"`' predeps_CXX='`$ECHO "$predeps_CXX" | $SED "$delay_single_quote_subst"`' postdeps_CXX='`$ECHO "$postdeps_CXX" | $SED "$delay_single_quote_subst"`' compiler_lib_search_path_CXX='`$ECHO "$compiler_lib_search_path_CXX" | $SED "$delay_single_quote_subst"`' LTCC='$LTCC' LTCFLAGS='$LTCFLAGS' compiler='$compiler_DEFAULT' # A function that is used when there is no print builtin or printf. func_fallback_echo () { eval 'cat <<_LTECHO_EOF \$1 _LTECHO_EOF' } # Quote evaled strings. for var in SHELL \ ECHO \ PATH_SEPARATOR \ SED \ GREP \ EGREP \ FGREP \ LD \ NM \ LN_S \ lt_SP2NL \ lt_NL2SP \ reload_flag \ OBJDUMP \ deplibs_check_method \ file_magic_cmd \ file_magic_glob \ want_nocaseglob \ DLLTOOL \ sharedlib_from_linklib_cmd \ AR \ AR_FLAGS \ archiver_list_spec \ STRIP \ RANLIB \ CC \ CFLAGS \ compiler \ lt_cv_sys_global_symbol_pipe \ lt_cv_sys_global_symbol_to_cdecl \ lt_cv_sys_global_symbol_to_import \ lt_cv_sys_global_symbol_to_c_name_address \ lt_cv_sys_global_symbol_to_c_name_address_lib_prefix \ lt_cv_nm_interface \ nm_file_list_spec \ lt_cv_truncate_bin \ lt_prog_compiler_no_builtin_flag \ lt_prog_compiler_pic \ lt_prog_compiler_wl \ lt_prog_compiler_static \ lt_cv_prog_compiler_c_o \ need_locks \ MANIFEST_TOOL \ DSYMUTIL \ NMEDIT \ LIPO \ OTOOL \ OTOOL64 \ shrext_cmds \ export_dynamic_flag_spec \ whole_archive_flag_spec \ compiler_needs_object \ with_gnu_ld \ allow_undefined_flag \ no_undefined_flag \ hardcode_libdir_flag_spec \ hardcode_libdir_separator \ exclude_expsyms \ include_expsyms \ file_list_spec \ variables_saved_for_relink \ libname_spec \ library_names_spec \ soname_spec \ install_override_mode \ finish_eval \ old_striplib \ striplib \ compiler_lib_search_dirs \ predep_objects \ postdep_objects \ predeps \ postdeps \ compiler_lib_search_path \ LD_CXX \ reload_flag_CXX \ compiler_CXX \ lt_prog_compiler_no_builtin_flag_CXX \ lt_prog_compiler_pic_CXX \ lt_prog_compiler_wl_CXX \ lt_prog_compiler_static_CXX \ lt_cv_prog_compiler_c_o_CXX \ export_dynamic_flag_spec_CXX \ whole_archive_flag_spec_CXX \ compiler_needs_object_CXX \ with_gnu_ld_CXX \ allow_undefined_flag_CXX \ no_undefined_flag_CXX \ hardcode_libdir_flag_spec_CXX \ hardcode_libdir_separator_CXX \ exclude_expsyms_CXX \ include_expsyms_CXX \ file_list_spec_CXX \ compiler_lib_search_dirs_CXX \ predep_objects_CXX \ postdep_objects_CXX \ predeps_CXX \ postdeps_CXX \ compiler_lib_search_path_CXX; do case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in *[\\\\\\\`\\"\\\$]*) eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac done # Double-quote double-evaled strings. for var in reload_cmds \ old_postinstall_cmds \ old_postuninstall_cmds \ old_archive_cmds \ extract_expsyms_cmds \ old_archive_from_new_cmds \ old_archive_from_expsyms_cmds \ archive_cmds \ archive_expsym_cmds \ module_cmds \ module_expsym_cmds \ export_symbols_cmds \ prelink_cmds \ postlink_cmds \ postinstall_cmds \ postuninstall_cmds \ finish_cmds \ sys_lib_search_path_spec \ configure_time_dlsearch_path \ configure_time_lt_sys_library_path \ reload_cmds_CXX \ old_archive_cmds_CXX \ old_archive_from_new_cmds_CXX \ old_archive_from_expsyms_cmds_CXX \ archive_cmds_CXX \ archive_expsym_cmds_CXX \ module_cmds_CXX \ module_expsym_cmds_CXX \ export_symbols_cmds_CXX \ prelink_cmds_CXX \ postlink_cmds_CXX; do case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in *[\\\\\\\`\\"\\\$]*) eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac done ac_aux_dir='$ac_aux_dir' # See if we are running on zsh, and set the options that allow our # commands through without removal of \ escapes INIT. if test -n "\${ZSH_VERSION+set}"; then setopt NO_GLOB_SUBST fi PACKAGE='$PACKAGE' VERSION='$VERSION' RM='$RM' ofile='$ofile' _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Handling of arguments. for ac_config_target in $ac_config_targets do case $ac_config_target in "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; "libopenmpt/libopenmpt.pc") CONFIG_FILES="$CONFIG_FILES libopenmpt/libopenmpt.pc" ;; "Doxyfile") CONFIG_FILES="$CONFIG_FILES Doxyfile" ;; "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;; "libtool") CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason against having it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: # after its creation but before its name has been assigned to `$tmp'. $debug || { tmp= ac_tmp= trap 'exit_status=$? : "${ac_tmp:=$tmp}" { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status ' 0 trap 'as_fn_exit 1' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 ac_tmp=$tmp # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. # This happens for instance with `./config.status config.h'. if test -n "$CONFIG_FILES"; then ac_cr=`echo X | tr X '\015'` # On cygwin, bash can eat \r inside `` if the user requested igncr. # But we know of no other shell where ac_cr would be empty at this # point, so we can use a bashism as a fallback. if test "x$ac_cr" = x; then eval ac_cr=\$\'\\r\' fi ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then ac_cs_awk_cr='\\r' else ac_cs_awk_cr=$ac_cr fi echo 'BEGIN {' >"$ac_tmp/subs1.awk" && _ACEOF { echo "cat >conf$$subs.awk <<_ACEOF" && echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && echo "_ACEOF" } >conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do . ./conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done rm -f conf$$subs.sh cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && _ACEOF sed -n ' h s/^/S["/; s/!.*/"]=/ p g s/^[^!]*!// :repl t repl s/'"$ac_delim"'$// t delim :nl h s/\(.\{148\}\)..*/\1/ t more1 s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ p n b repl :more1 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t nl :delim h s/\(.\{148\}\)..*/\1/ t more2 s/["\\]/\\&/g; s/^/"/; s/$/"/ p b :more2 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t delim ' >$CONFIG_STATUS || ac_write_fail=1 rm -f conf$$subs.awk cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACAWK cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && for (key in S) S_is_set[key] = 1 FS = "" } { line = $ 0 nfields = split(line, field, "@") substed = 0 len = length(field[1]) for (i = 2; i < nfields; i++) { key = field[i] keylen = length(key) if (S_is_set[key]) { value = S[key] line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) len += length(value) + length(field[++i]) substed = 1 } else len += 1 + keylen } print line } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" else cat fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 _ACEOF # VPATH may cause trouble with some makes, so we remove sole $(srcdir), # ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ h s/// s/^/:/ s/[ ]*$/:/ s/:\$(srcdir):/:/g s/:\${srcdir}:/:/g s/:@srcdir@:/:/g s/^:*// s/:*$// x s/\(=[ ]*\).*/\1/ G s/\n// s/^[^=]*=[ ]*$// }' fi cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 fi # test -n "$CONFIG_FILES" eval set X " :F $CONFIG_FILES :C $CONFIG_COMMANDS" shift for ac_tag do case $ac_tag in :[FHLC]) ac_mode=$ac_tag; continue;; esac case $ac_mode$ac_tag in :[FHL]*:*);; :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac ac_save_IFS=$IFS IFS=: set x $ac_tag IFS=$ac_save_IFS shift ac_file=$1 shift case $ac_mode in :L) ac_source=$1;; :[FH]) ac_file_inputs= for ac_f do case $ac_f in -) ac_f="$ac_tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain `:'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; esac case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" done # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input='Generated from '` $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 $as_echo "$as_me: creating $ac_file" >&6;} fi # Neutralize special characters interpreted by sed in replacement strings. case $configure_input in #( *\&* | *\|* | *\\* ) ac_sed_conf_input=`$as_echo "$configure_input" | sed 's/[\\\\&|]/\\\\&/g'`;; #( *) ac_sed_conf_input=$configure_input;; esac case $ac_tag in *:-:* | *:-) cat >"$ac_tmp/stdin" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac ac_dir=`$as_dirname -- "$ac_file" || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir="$ac_dir"; as_fn_mkdir_p ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix case $ac_mode in :F) # # CONFIG_FILE # case $INSTALL in [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; esac ac_MKDIR_P=$MKDIR_P case $MKDIR_P in [\\/$]* | ?:[\\/]* ) ;; */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;; esac _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # If the template does not know about datarootdir, expand it. # FIXME: This hack should be removed a few years after 2.60. ac_datarootdir_hack=; ac_datarootdir_seen= ac_sed_dataroot=' /datarootdir/ { p q } /@datadir@/p /@docdir@/p /@infodir@/p /@localedir@/p /@mandir@/p' case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 $as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_datarootdir_hack=' s&@datadir@&$datadir&g s&@docdir@&$docdir&g s&@infodir@&$infodir&g s&@localedir@&$localedir&g s&@mandir@&$mandir&g s&\\\${datarootdir}&$datarootdir&g' ;; esac _ACEOF # Neutralize VPATH when `$srcdir' = `.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_sed_extra="$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s|@configure_input@|$ac_sed_conf_input|;t t s&@top_builddir@&$ac_top_builddir_sub&;t t s&@top_build_prefix@&$ac_top_build_prefix&;t t s&@srcdir@&$ac_srcdir&;t t s&@abs_srcdir@&$ac_abs_srcdir&;t t s&@top_srcdir@&$ac_top_srcdir&;t t s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t s&@builddir@&$ac_builddir&;t t s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t s&@INSTALL@&$ac_INSTALL&;t t s&@MKDIR_P@&$ac_MKDIR_P&;t t $ac_datarootdir_hack " eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ "$ac_tmp/out"`; test -z "$ac_out"; } && { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&5 $as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&2;} rm -f "$ac_tmp/stdin" case $ac_file in -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; esac \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; :C) { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5 $as_echo "$as_me: executing $ac_file commands" >&6;} ;; esac case $ac_file$ac_mode in "depfiles":C) test x"$AMDEP_TRUE" != x"" || { # Older Autoconf quotes --file arguments for eval, but not when files # are listed without --file. Let's play safe and only enable the eval # if we detect the quoting. # TODO: see whether this extra hack can be removed once we start # requiring Autoconf 2.70 or later. case $CONFIG_FILES in #( *\'*) : eval set x "$CONFIG_FILES" ;; #( *) : set x $CONFIG_FILES ;; #( *) : ;; esac shift # Used to flag and report bootstrapping failures. am_rc=0 for am_mf do # Strip MF so we end up with the name of the file. am_mf=`$as_echo "$am_mf" | sed -e 's/:.*$//'` # Check whether this is an Automake generated Makefile which includes # dependency-tracking related rules and includes. # Grep'ing the whole file directly is not great: AIX grep has a line # limit of 2048, but all sed's we know have understand at least 4000. sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \ || continue am_dirpart=`$as_dirname -- "$am_mf" || $as_expr X"$am_mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$am_mf" : 'X\(//\)[^/]' \| \ X"$am_mf" : 'X\(//\)$' \| \ X"$am_mf" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$am_mf" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` am_filepart=`$as_basename -- "$am_mf" || $as_expr X/"$am_mf" : '.*/\([^/][^/]*\)/*$' \| \ X"$am_mf" : 'X\(//\)$' \| \ X"$am_mf" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$am_mf" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` { echo "$as_me:$LINENO: cd "$am_dirpart" \ && sed -e '/# am--include-marker/d' "$am_filepart" \ | $MAKE -f - am--depfiles" >&5 (cd "$am_dirpart" \ && sed -e '/# am--include-marker/d' "$am_filepart" \ | $MAKE -f - am--depfiles) >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } || am_rc=$? done if test $am_rc -ne 0; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "Something went wrong bootstrapping makefile fragments for automatic dependency tracking. If GNU make was not used, consider re-running the configure script with MAKE=\"gmake\" (or whatever is necessary). You can also try re-running configure with the '--disable-dependency-tracking' option to at least be able to build the package (albeit without support for automatic dependency tracking). See \`config.log' for more details" "$LINENO" 5; } fi { am_dirpart=; unset am_dirpart;} { am_filepart=; unset am_filepart;} { am_mf=; unset am_mf;} { am_rc=; unset am_rc;} rm -f conftest-deps.mk } ;; "libtool":C) # See if we are running on zsh, and set the options that allow our # commands through without removal of \ escapes. if test -n "${ZSH_VERSION+set}"; then setopt NO_GLOB_SUBST fi cfgfile=${ofile}T trap "$RM \"$cfgfile\"; exit 1" 1 2 15 $RM "$cfgfile" cat <<_LT_EOF >> "$cfgfile" #! $SHELL # Generated automatically by $as_me ($PACKAGE) $VERSION # NOTE: Changes made to this file will be lost: look at ltmain.sh. # Provide generalized library-building support services. # Written by Gordon Matzigkeit, 1996 # Copyright (C) 2014 Free Software Foundation, Inc. # This is free software; see the source for copying conditions. There is NO # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # GNU Libtool is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of of the License, or # (at your option) any later version. # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program or library that is built # using GNU Libtool, you may include this file under the same # distribution terms that you use for the rest of that program. # # GNU Libtool is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # The names of the tagged configurations supported by this script. available_tags='CXX ' # Configured defaults for sys_lib_dlsearch_path munging. : \${LT_SYS_LIBRARY_PATH="$configure_time_lt_sys_library_path"} # ### BEGIN LIBTOOL CONFIG # Which release of libtool.m4 was used? macro_version=$macro_version macro_revision=$macro_revision # Whether or not to build shared libraries. build_libtool_libs=$enable_shared # Whether or not to build static libraries. build_old_libs=$enable_static # What type of objects to build. pic_mode=$pic_mode # Whether or not to optimize for fast installation. fast_install=$enable_fast_install # Shared archive member basename,for filename based shared library versioning on AIX. shared_archive_member_spec=$shared_archive_member_spec # Shell to use when invoking shell scripts. SHELL=$lt_SHELL # An echo program that protects backslashes. ECHO=$lt_ECHO # The PATH separator for the build system. PATH_SEPARATOR=$lt_PATH_SEPARATOR # The host system. host_alias=$host_alias host=$host host_os=$host_os # The build system. build_alias=$build_alias build=$build build_os=$build_os # A sed program that does not truncate output. SED=$lt_SED # Sed that helps us avoid accidentally triggering echo(1) options like -n. Xsed="\$SED -e 1s/^X//" # A grep program that handles long lines. GREP=$lt_GREP # An ERE matcher. EGREP=$lt_EGREP # A literal string matcher. FGREP=$lt_FGREP # A BSD- or MS-compatible name lister. NM=$lt_NM # Whether we need soft or hard links. LN_S=$lt_LN_S # What is the maximum length of a command? max_cmd_len=$max_cmd_len # Object file suffix (normally "o"). objext=$ac_objext # Executable file suffix (normally ""). exeext=$exeext # whether the shell understands "unset". lt_unset=$lt_unset # turn spaces into newlines. SP2NL=$lt_lt_SP2NL # turn newlines into spaces. NL2SP=$lt_lt_NL2SP # convert \$build file names to \$host format. to_host_file_cmd=$lt_cv_to_host_file_cmd # convert \$build files to toolchain format. to_tool_file_cmd=$lt_cv_to_tool_file_cmd # An object symbol dumper. OBJDUMP=$lt_OBJDUMP # Method to check whether dependent libraries are shared objects. deplibs_check_method=$lt_deplibs_check_method # Command to use when deplibs_check_method = "file_magic". file_magic_cmd=$lt_file_magic_cmd # How to find potential files when deplibs_check_method = "file_magic". file_magic_glob=$lt_file_magic_glob # Find potential files using nocaseglob when deplibs_check_method = "file_magic". want_nocaseglob=$lt_want_nocaseglob # DLL creation program. DLLTOOL=$lt_DLLTOOL # Command to associate shared and link libraries. sharedlib_from_linklib_cmd=$lt_sharedlib_from_linklib_cmd # The archiver. AR=$lt_AR # Flags to create an archive. AR_FLAGS=$lt_AR_FLAGS # How to feed a file listing to the archiver. archiver_list_spec=$lt_archiver_list_spec # A symbol stripping program. STRIP=$lt_STRIP # Commands used to install an old-style archive. RANLIB=$lt_RANLIB old_postinstall_cmds=$lt_old_postinstall_cmds old_postuninstall_cmds=$lt_old_postuninstall_cmds # Whether to use a lock for old archive extraction. lock_old_archive_extraction=$lock_old_archive_extraction # A C compiler. LTCC=$lt_CC # LTCC compiler flags. LTCFLAGS=$lt_CFLAGS # Take the output of nm and produce a listing of raw symbols and C names. global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe # Transform the output of nm in a proper C declaration. global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl # Transform the output of nm into a list of symbols to manually relocate. global_symbol_to_import=$lt_lt_cv_sys_global_symbol_to_import # Transform the output of nm in a C name address pair. global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address # Transform the output of nm in a C name address pair when lib prefix is needed. global_symbol_to_c_name_address_lib_prefix=$lt_lt_cv_sys_global_symbol_to_c_name_address_lib_prefix # The name lister interface. nm_interface=$lt_lt_cv_nm_interface # Specify filename containing input files for \$NM. nm_file_list_spec=$lt_nm_file_list_spec # The root where to search for dependent libraries,and where our libraries should be installed. lt_sysroot=$lt_sysroot # Command to truncate a binary pipe. lt_truncate_bin=$lt_lt_cv_truncate_bin # The name of the directory that contains temporary libtool files. objdir=$objdir # Used to examine libraries when file_magic_cmd begins with "file". MAGIC_CMD=$MAGIC_CMD # Must we lock files when doing compilation? need_locks=$lt_need_locks # Manifest tool. MANIFEST_TOOL=$lt_MANIFEST_TOOL # Tool to manipulate archived DWARF debug symbol files on Mac OS X. DSYMUTIL=$lt_DSYMUTIL # Tool to change global to local symbols on Mac OS X. NMEDIT=$lt_NMEDIT # Tool to manipulate fat objects and archives on Mac OS X. LIPO=$lt_LIPO # ldd/readelf like tool for Mach-O binaries on Mac OS X. OTOOL=$lt_OTOOL # ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4. OTOOL64=$lt_OTOOL64 # Old archive suffix (normally "a"). libext=$libext # Shared library suffix (normally ".so"). shrext_cmds=$lt_shrext_cmds # The commands to extract the exported symbol list from a shared archive. extract_expsyms_cmds=$lt_extract_expsyms_cmds # Variables whose values should be saved in libtool wrapper scripts and # restored at link time. variables_saved_for_relink=$lt_variables_saved_for_relink # Do we need the "lib" prefix for modules? need_lib_prefix=$need_lib_prefix # Do we need a version for libraries? need_version=$need_version # Library versioning type. version_type=$version_type # Shared library runtime path variable. runpath_var=$runpath_var # Shared library path variable. shlibpath_var=$shlibpath_var # Is shlibpath searched before the hard-coded library search path? shlibpath_overrides_runpath=$shlibpath_overrides_runpath # Format of library name prefix. libname_spec=$lt_libname_spec # List of archive names. First name is the real one, the rest are links. # The last name is the one that the linker finds with -lNAME library_names_spec=$lt_library_names_spec # The coded name of the library, if different from the real name. soname_spec=$lt_soname_spec # Permission mode override for installation of shared libraries. install_override_mode=$lt_install_override_mode # Command to use after installation of a shared archive. postinstall_cmds=$lt_postinstall_cmds # Command to use after uninstallation of a shared archive. postuninstall_cmds=$lt_postuninstall_cmds # Commands used to finish a libtool library installation in a directory. finish_cmds=$lt_finish_cmds # As "finish_cmds", except a single script fragment to be evaled but # not shown. finish_eval=$lt_finish_eval # Whether we should hardcode library paths into libraries. hardcode_into_libs=$hardcode_into_libs # Compile-time system search path for libraries. sys_lib_search_path_spec=$lt_sys_lib_search_path_spec # Detected run-time system search path for libraries. sys_lib_dlsearch_path_spec=$lt_configure_time_dlsearch_path # Explicit LT_SYS_LIBRARY_PATH set during ./configure time. configure_time_lt_sys_library_path=$lt_configure_time_lt_sys_library_path # Whether dlopen is supported. dlopen_support=$enable_dlopen # Whether dlopen of programs is supported. dlopen_self=$enable_dlopen_self # Whether dlopen of statically linked programs is supported. dlopen_self_static=$enable_dlopen_self_static # Commands to strip libraries. old_striplib=$lt_old_striplib striplib=$lt_striplib # The linker used to build libraries. LD=$lt_LD # How to create reloadable object files. reload_flag=$lt_reload_flag reload_cmds=$lt_reload_cmds # Commands used to build an old-style archive. old_archive_cmds=$lt_old_archive_cmds # A language specific compiler. CC=$lt_compiler # Is the compiler the GNU compiler? with_gcc=$GCC # Compiler flag to turn off builtin functions. no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag # Additional compiler flags for building library objects. pic_flag=$lt_lt_prog_compiler_pic # How to pass a linker flag through the compiler. wl=$lt_lt_prog_compiler_wl # Compiler flag to prevent dynamic linking. link_static_flag=$lt_lt_prog_compiler_static # Does compiler simultaneously support -c and -o options? compiler_c_o=$lt_lt_cv_prog_compiler_c_o # Whether or not to add -lc for building shared libraries. build_libtool_need_lc=$archive_cmds_need_lc # Whether or not to disallow shared libs when runtime libs are static. allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes # Compiler flag to allow reflexive dlopens. export_dynamic_flag_spec=$lt_export_dynamic_flag_spec # Compiler flag to generate shared objects directly from archives. whole_archive_flag_spec=$lt_whole_archive_flag_spec # Whether the compiler copes with passing no objects directly. compiler_needs_object=$lt_compiler_needs_object # Create an old-style archive from a shared archive. old_archive_from_new_cmds=$lt_old_archive_from_new_cmds # Create a temporary old-style archive to link instead of a shared archive. old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds # Commands used to build a shared archive. archive_cmds=$lt_archive_cmds archive_expsym_cmds=$lt_archive_expsym_cmds # Commands used to build a loadable module if different from building # a shared archive. module_cmds=$lt_module_cmds module_expsym_cmds=$lt_module_expsym_cmds # Whether we are building with GNU ld or not. with_gnu_ld=$lt_with_gnu_ld # Flag that allows shared libraries with undefined symbols to be built. allow_undefined_flag=$lt_allow_undefined_flag # Flag that enforces no undefined symbols. no_undefined_flag=$lt_no_undefined_flag # Flag to hardcode \$libdir into a binary during linking. # This must work even if \$libdir does not exist hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec # Whether we need a single "-rpath" flag with a separated argument. hardcode_libdir_separator=$lt_hardcode_libdir_separator # Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes # DIR into the resulting binary. hardcode_direct=$hardcode_direct # Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes # DIR into the resulting binary and the resulting library dependency is # "absolute",i.e impossible to change by setting \$shlibpath_var if the # library is relocated. hardcode_direct_absolute=$hardcode_direct_absolute # Set to "yes" if using the -LDIR flag during linking hardcodes DIR # into the resulting binary. hardcode_minus_L=$hardcode_minus_L # Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR # into the resulting binary. hardcode_shlibpath_var=$hardcode_shlibpath_var # Set to "yes" if building a shared library automatically hardcodes DIR # into the library and all subsequent libraries and executables linked # against it. hardcode_automatic=$hardcode_automatic # Set to yes if linker adds runtime paths of dependent libraries # to runtime path list. inherit_rpath=$inherit_rpath # Whether libtool must link a program against all its dependency libraries. link_all_deplibs=$link_all_deplibs # Set to "yes" if exported symbols are required. always_export_symbols=$always_export_symbols # The commands to list exported symbols. export_symbols_cmds=$lt_export_symbols_cmds # Symbols that should not be listed in the preloaded symbols. exclude_expsyms=$lt_exclude_expsyms # Symbols that must always be exported. include_expsyms=$lt_include_expsyms # Commands necessary for linking programs (against libraries) with templates. prelink_cmds=$lt_prelink_cmds # Commands necessary for finishing linking programs. postlink_cmds=$lt_postlink_cmds # Specify filename containing input files. file_list_spec=$lt_file_list_spec # How to hardcode a shared library path into an executable. hardcode_action=$hardcode_action # The directories searched by this compiler when creating a shared library. compiler_lib_search_dirs=$lt_compiler_lib_search_dirs # Dependencies to place before and after the objects being linked to # create a shared library. predep_objects=$lt_predep_objects postdep_objects=$lt_postdep_objects predeps=$lt_predeps postdeps=$lt_postdeps # The library search path used internally by the compiler when linking # a shared library. compiler_lib_search_path=$lt_compiler_lib_search_path # ### END LIBTOOL CONFIG _LT_EOF cat <<'_LT_EOF' >> "$cfgfile" # ### BEGIN FUNCTIONS SHARED WITH CONFIGURE # func_munge_path_list VARIABLE PATH # ----------------------------------- # VARIABLE is name of variable containing _space_ separated list of # directories to be munged by the contents of PATH, which is string # having a format: # "DIR[:DIR]:" # string "DIR[ DIR]" will be prepended to VARIABLE # ":DIR[:DIR]" # string "DIR[ DIR]" will be appended to VARIABLE # "DIRP[:DIRP]::[DIRA:]DIRA" # string "DIRP[ DIRP]" will be prepended to VARIABLE and string # "DIRA[ DIRA]" will be appended to VARIABLE # "DIR[:DIR]" # VARIABLE will be replaced by "DIR[ DIR]" func_munge_path_list () { case x$2 in x) ;; *:) eval $1=\"`$ECHO $2 | $SED 's/:/ /g'` \$$1\" ;; x:*) eval $1=\"\$$1 `$ECHO $2 | $SED 's/:/ /g'`\" ;; *::*) eval $1=\"\$$1\ `$ECHO $2 | $SED -e 's/.*:://' -e 's/:/ /g'`\" eval $1=\"`$ECHO $2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \$$1\" ;; *) eval $1=\"`$ECHO $2 | $SED 's/:/ /g'`\" ;; esac } # Calculate cc_basename. Skip known compiler wrappers and cross-prefix. func_cc_basename () { for cc_temp in $*""; do case $cc_temp in compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; \-*) ;; *) break;; esac done func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` } # ### END FUNCTIONS SHARED WITH CONFIGURE _LT_EOF case $host_os in aix3*) cat <<\_LT_EOF >> "$cfgfile" # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. if test set != "${COLLECT_NAMES+set}"; then COLLECT_NAMES= export COLLECT_NAMES fi _LT_EOF ;; esac ltmain=$ac_aux_dir/ltmain.sh # We use sed instead of cat because bash on DJGPP gets confused if # if finds mixed CR/LF and LF-only lines. Since sed operates in # text mode, it properly converts lines to CR/LF. This bash problem # is reportedly fixed, but why not run on old versions too? sed '$q' "$ltmain" >> "$cfgfile" \ || (rm -f "$cfgfile"; exit 1) mv -f "$cfgfile" "$ofile" || (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") chmod +x "$ofile" cat <<_LT_EOF >> "$ofile" # ### BEGIN LIBTOOL TAG CONFIG: CXX # The linker used to build libraries. LD=$lt_LD_CXX # How to create reloadable object files. reload_flag=$lt_reload_flag_CXX reload_cmds=$lt_reload_cmds_CXX # Commands used to build an old-style archive. old_archive_cmds=$lt_old_archive_cmds_CXX # A language specific compiler. CC=$lt_compiler_CXX # Is the compiler the GNU compiler? with_gcc=$GCC_CXX # Compiler flag to turn off builtin functions. no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag_CXX # Additional compiler flags for building library objects. pic_flag=$lt_lt_prog_compiler_pic_CXX # How to pass a linker flag through the compiler. wl=$lt_lt_prog_compiler_wl_CXX # Compiler flag to prevent dynamic linking. link_static_flag=$lt_lt_prog_compiler_static_CXX # Does compiler simultaneously support -c and -o options? compiler_c_o=$lt_lt_cv_prog_compiler_c_o_CXX # Whether or not to add -lc for building shared libraries. build_libtool_need_lc=$archive_cmds_need_lc_CXX # Whether or not to disallow shared libs when runtime libs are static. allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes_CXX # Compiler flag to allow reflexive dlopens. export_dynamic_flag_spec=$lt_export_dynamic_flag_spec_CXX # Compiler flag to generate shared objects directly from archives. whole_archive_flag_spec=$lt_whole_archive_flag_spec_CXX # Whether the compiler copes with passing no objects directly. compiler_needs_object=$lt_compiler_needs_object_CXX # Create an old-style archive from a shared archive. old_archive_from_new_cmds=$lt_old_archive_from_new_cmds_CXX # Create a temporary old-style archive to link instead of a shared archive. old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds_CXX # Commands used to build a shared archive. archive_cmds=$lt_archive_cmds_CXX archive_expsym_cmds=$lt_archive_expsym_cmds_CXX # Commands used to build a loadable module if different from building # a shared archive. module_cmds=$lt_module_cmds_CXX module_expsym_cmds=$lt_module_expsym_cmds_CXX # Whether we are building with GNU ld or not. with_gnu_ld=$lt_with_gnu_ld_CXX # Flag that allows shared libraries with undefined symbols to be built. allow_undefined_flag=$lt_allow_undefined_flag_CXX # Flag that enforces no undefined symbols. no_undefined_flag=$lt_no_undefined_flag_CXX # Flag to hardcode \$libdir into a binary during linking. # This must work even if \$libdir does not exist hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec_CXX # Whether we need a single "-rpath" flag with a separated argument. hardcode_libdir_separator=$lt_hardcode_libdir_separator_CXX # Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes # DIR into the resulting binary. hardcode_direct=$hardcode_direct_CXX # Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes # DIR into the resulting binary and the resulting library dependency is # "absolute",i.e impossible to change by setting \$shlibpath_var if the # library is relocated. hardcode_direct_absolute=$hardcode_direct_absolute_CXX # Set to "yes" if using the -LDIR flag during linking hardcodes DIR # into the resulting binary. hardcode_minus_L=$hardcode_minus_L_CXX # Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR # into the resulting binary. hardcode_shlibpath_var=$hardcode_shlibpath_var_CXX # Set to "yes" if building a shared library automatically hardcodes DIR # into the library and all subsequent libraries and executables linked # against it. hardcode_automatic=$hardcode_automatic_CXX # Set to yes if linker adds runtime paths of dependent libraries # to runtime path list. inherit_rpath=$inherit_rpath_CXX # Whether libtool must link a program against all its dependency libraries. link_all_deplibs=$link_all_deplibs_CXX # Set to "yes" if exported symbols are required. always_export_symbols=$always_export_symbols_CXX # The commands to list exported symbols. export_symbols_cmds=$lt_export_symbols_cmds_CXX # Symbols that should not be listed in the preloaded symbols. exclude_expsyms=$lt_exclude_expsyms_CXX # Symbols that must always be exported. include_expsyms=$lt_include_expsyms_CXX # Commands necessary for linking programs (against libraries) with templates. prelink_cmds=$lt_prelink_cmds_CXX # Commands necessary for finishing linking programs. postlink_cmds=$lt_postlink_cmds_CXX # Specify filename containing input files. file_list_spec=$lt_file_list_spec_CXX # How to hardcode a shared library path into an executable. hardcode_action=$hardcode_action_CXX # The directories searched by this compiler when creating a shared library. compiler_lib_search_dirs=$lt_compiler_lib_search_dirs_CXX # Dependencies to place before and after the objects being linked to # create a shared library. predep_objects=$lt_predep_objects_CXX postdep_objects=$lt_postdep_objects_CXX predeps=$lt_predeps_CXX postdeps=$lt_postdeps_CXX # The library search path used internally by the compiler when linking # a shared library. compiler_lib_search_path=$lt_compiler_lib_search_path_CXX # ### END LIBTOOL TAG CONFIG: CXX _LT_EOF ;; esac done # for ac_tag as_fn_exit 0 _ACEOF ac_clean_files=$ac_clean_files_save test $ac_write_fail = 0 || as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || as_fn_exit 1 fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi libopenmpt-0.6.1+release.autotools/configure.ac0000644000175000017500000002133514175541513016554 00000000000000AC_INIT([libopenmpt], [0.6.1+release.autotools], [https://bugs.openmpt.org/], [libopenmpt], [https://lib.openmpt.org/]) AC_PREREQ([2.68]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_FILES([Makefile libopenmpt/libopenmpt.pc Doxyfile]) AM_INIT_AUTOMAKE([1.11 -Wall -Werror foreign subdir-objects]) AM_PROG_AR LT_INIT AC_SYS_LARGEFILE PKG_PROG_PKG_CONFIG([0.24]) AC_PROG_CC AM_PROG_CC_C_O AC_PROG_CXX AC_PROG_INSTALL LIBOPENMPT_LTVER_CURRENT=3 LIBOPENMPT_LTVER_REVISION=3 LIBOPENMPT_LTVER_AGE=3 AC_SUBST([LIBOPENMPT_LTVER_CURRENT]) AC_SUBST([LIBOPENMPT_LTVER_REVISION]) AC_SUBST([LIBOPENMPT_LTVER_AGE]) AC_DEFINE([MPT_SVNURL], ["https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.6.1"], [svn version]) AC_DEFINE([MPT_SVNVERSION], ["16764"], [svn version]) AC_DEFINE([MPT_SVNDATE], ["2022-01-30T16:49:19.812343Z"], [svn date]) AC_DEFINE([MPT_PACKAGE], [true], [is package]) AC_ARG_VAR(CXXSTDLIB_PCLIBSPRIVATE, [C++ standard library (or libraries) required for static linking. This will be put in the pkg-config file libopenmpt.pc Libs.private field and used for nothing else.]) AC_CANONICAL_HOST case $host_os in mingw32*) LIBOPENMPT_WIN32_LIBS="-lole32 -lrpcrt4" LIBOPENMPT_LIBS_PRIVATE_WIN32="-lole32 -lrpcrt4" OPENMPT123_WIN32_LIBS=-lwinmm CXXFLAGS="$CXXFLAGS -municode -mthreads" CFLAGS="$CFLAGS -municode -mthreads" WIN32_CONSOLE_CXXFLAGS=-mconsole WIN32_CONSOLE_CFLAGS=-mconsole ;; *) LIBOPENMPT_WIN32_LIBS= LIBOPENMPT_LIBS_PRIVATE_WIN32= OPENMPT123_WIN32_LIBS= WIN32_CONSOLE_CXXFLAGS= WIN32_CONSOLE_CFLAGS= ;; esac AC_SUBST([LIBOPENMPT_WIN32_LIBS]) AC_SUBST([OPENMPT123_WIN32_LIBS]) AC_SUBST([WIN32_CONSOLE_CXXFLAGS]) AC_SUBST([WIN32_CONSOLE_CFLAGS]) LIBOPENMPT_REQUIRES_PRIVATE= LIBOPENMPT_LIBS_PRIVATE= # Required libopenmpt dependency: zlib ZLIB_PKG= AC_ARG_WITH([zlib], AS_HELP_STRING([--without-zlib], [Disable use of zlib.])) AS_IF([test "x$with_zlib" != "xno"], [ PKG_CHECK_MODULES([ZLIB], [zlib], [ ZLIB_PKG=zlib AC_DEFINE([MPT_WITH_ZLIB], [], [with zlib]) ], [AC_MSG_ERROR([Unable to find zlib.])]) ] ) # Required libopenmpt dependency: mpg123 MPG123_PKG= AC_ARG_WITH([mpg123], AS_HELP_STRING([--without-mpg123], [Disable use of libmpg123.])) AS_IF([test "x$with_mpg123" != "xno"], [ PKG_CHECK_MODULES([MPG123], [libmpg123 >= 1.14.0], [ MPG123_PKG=libmpg123 AC_DEFINE([MPT_WITH_MPG123], [], [with mpg123]) ], [AC_MSG_ERROR([Unable to find libmpg123.])]) ] ) # Required libopenmpt dependency: ogg OGG_PKG= AC_ARG_WITH([ogg], AS_HELP_STRING([--without-ogg], [Disable use of libogg.])) AS_IF([test "x$with_ogg" != "xno"], [ PKG_CHECK_MODULES([OGG], [ogg], [ OGG_PKG=ogg AC_DEFINE([MPT_WITH_OGG], [], [with ogg]) ], [AC_MSG_ERROR([Unable to find libogg.])]) ] ) # Required libopenmpt dependency: vorbis VORBIS_PKG= AC_ARG_WITH([vorbis], AS_HELP_STRING([--without-vorbis], [Disable use of libvorbis.])) AS_IF([test "x$with_vorbis" != "xno"], [ PKG_CHECK_MODULES([VORBIS], [vorbis], [ VORBIS_PKG=vorbis AC_DEFINE([MPT_WITH_VORBIS], [], [with vorbis]) ], [AC_MSG_ERROR([Unable to find libvorbis.])]) ] ) # Required libopenmpt dependency: vorbisfile VORBISFILE_PKG= AC_ARG_WITH([vorbisfile], AS_HELP_STRING([--without-vorbisfile], [Disable use of libvorbisfile.])) AS_IF([test "x$with_vorbisfile" != "xno"], [ PKG_CHECK_MODULES([VORBISFILE], [vorbisfile], [ VORBISFILE_PKG=vorbisfile AC_DEFINE([MPT_WITH_VORBISFILE], [], [with vorbisfile]) ], [AC_MSG_ERROR([Unable to find libvorbisfile.])]) ] ) LIBOPENMPT_REQUIRES_PRIVATE="$ZLIB_PKG $MPG123_PKG $OGG_PKG $VORBIS_PKG $VORBISFILE_PKG" LIBOPENMPT_LIBS_PRIVATE="$CXXSTDLIB_PCLIBSPRIVATE $LIBOPENMPT_LIBS_PRIVATE_WIN32" AC_SUBST([LIBOPENMPT_REQUIRES_PRIVATE]) AC_SUBST([LIBOPENMPT_LIBS_PRIVATE]) # openmpt123 AC_ARG_ENABLE([openmpt123], AS_HELP_STRING([--disable-openmpt123], [Disable the openmpt123 command line player.])) AM_CONDITIONAL([ENABLE_OPENMPT123], [test "x$enable_openmpt123" != "xno"]) # examples AC_ARG_ENABLE([examples], AS_HELP_STRING([--disable-examples], [Disable the example programs.])) AM_CONDITIONAL([ENABLE_EXAMPLES], [test "x$enable_examples" != "xno"]) # tests AC_ARG_ENABLE([tests], AS_HELP_STRING([--disable-tests], [Disable the test suite.])) AM_CONDITIONAL([ENABLE_TESTS], [test "x$enable_tests" != "xno"]) # Optional openmpt123 dependency AC_ARG_WITH([pulseaudio], AS_HELP_STRING([--with-pulseaudio], [Enable use of libpulse and libpulse-simple (enabled by default on Linux).])) AS_IF([test "x$enable_openmpt123" != "xno"],[ case $host_os in linux*) AS_IF([test "x$with_pulseaudio" != "xno"], [ PKG_CHECK_MODULES([PULSEAUDIO], [libpulse libpulse-simple], [ have_pulseaudio=1 AC_DEFINE([MPT_WITH_PULSEAUDIO], [], [with libpulseaudio]) ], [ have_pulseaudio=0 AC_MSG_ERROR([Unable to find libpulse and/or libpulse-simple.]) ] ) ], [ have_pulseaudio=0 ] ) ;; *) AS_IF([test "x$with_pulseaudio" = "xyes"], [ PKG_CHECK_MODULES([PULSEAUDIO], [libpulse libpulse-simple], [ have_pulseaudio=1 AC_DEFINE([MPT_WITH_PULSEAUDIO], [], [with libpulseaudio]) ], [ have_pulseaudio=0 AC_MSG_ERROR([Unable to find libpulse and/or libpulse-simple.]) ] ) ], [ have_pulseaudio=0 ] ) ;; esac ],[have_pulseaudio=0]) # Optional openmpt123 and examples dependency AC_ARG_WITH([portaudio], AS_HELP_STRING([--without-portaudio], [Disable use of libportaudio.])) AS_IF([test "x$enable_openmpt123$enable_examples" != "xnono"],[ AS_IF([test "x$with_portaudio" != "xno"], [ PKG_CHECK_MODULES([PORTAUDIO], [portaudio-2.0], [ have_portaudio=1 AC_DEFINE([MPT_WITH_PORTAUDIO], [], [with libportaudio]) ], [ have_portaudio=0 AC_MSG_ERROR([Unable to find libportaudio.]) ] ) ], [ have_portaudio=0 ] ) ],[have_portaudio=0]) AM_CONDITIONAL([HAVE_PORTAUDIO], [test x$have_portaudio = x1]) # Optional examples dependency: PortAudio C++ AC_ARG_WITH([portaudiocpp], AS_HELP_STRING([--without-portaudiocpp], [Disable use of libportaudiocpp.])) AS_IF([test "x$enable_examples" != "xno"],[ AS_IF([test "x$with_portaudiocpp" != "xno"], [ PKG_CHECK_MODULES([PORTAUDIOCPP], [portaudiocpp], [ have_portaudiocpp=1 AC_DEFINE([MPT_WITH_PORTAUDIOCPP], [], [with libportaudiocpp]) ], [ have_portaudiocpp=0 AC_MSG_ERROR([Unable to find libportaudiocpp.]) ] ) ], [ have_portaudiocpp=0 ] ) ],[have_portaudiocpp=0]) AM_CONDITIONAL([HAVE_PORTAUDIOCPP], [test x$have_portaudiocpp = x1]) # Optional disabled openmpt123 dependency: libsdl2 AC_ARG_WITH([sdl2], AS_HELP_STRING([--with-sdl2], [Enable use of libsdl2.])) AS_IF([test "x$enable_openmpt123" != "xno"],[ AS_IF([test "x$with_sdl2" = "xyes"], [ PKG_CHECK_MODULES([SDL2], [sdl2 >= 2.0.4], [AC_DEFINE([MPT_WITH_SDL2], [], [with libsdl2])], [AC_MSG_ERROR([Unable to find libsdl2.])]) ] ) ]) # Optional openmpt123 dependency: libsndfile AC_ARG_WITH([sndfile], AS_HELP_STRING([--without-sndfile], [Disable use of libsndfile.])) AS_IF([test "x$enable_openmpt123" != "xno"],[ AS_IF([test "x$with_sndfile" != "xno"], [ PKG_CHECK_MODULES([SNDFILE], [sndfile], [AC_DEFINE([MPT_WITH_SNDFILE], [], [with libsndfile])], [AC_MSG_ERROR([Unable to find libsndfile.])]) ] ) ]) # Optional openmpt123 dependency: libFLAC AC_ARG_WITH([flac], AS_HELP_STRING([--without-flac], [Disable use of libflac.])) AS_IF([test "x$enable_openmpt123" != "xno"],[ AS_IF([test "x$with_flac" != "xno"], [ PKG_CHECK_MODULES([FLAC], [flac >= 1.3.0], [AC_DEFINE([MPT_WITH_FLAC], [], [with libflac])], [AC_MSG_ERROR([Unable to find libflac >= 1.3.0.])]) ] ) ]) # We want a modern C compiler AC_PROG_CC_STDC #AC_PROG_CC_C99 # We need C++17 support #AX_CXX_COMPILE_STDCXX(20, [noext], [optional]) #AS_IF([test "x$HAVE_CXX20" != "x1"], # [ # AX_CXX_COMPILE_STDCXX(17, [noext], [mandatory]) # ],[] #) AX_CXX_COMPILE_STDCXX(17, [noext], [mandatory]) AC_LANG_PUSH([C]) AX_CHECK_COMPILE_FLAG([-fvisibility=hidden], [CFLAGS="$CFLAGS -fvisibility=hidden"]) AX_CFLAGS_WARN_ALL AC_LANG_POP([C]) AC_LANG_PUSH([C++]) AX_CHECK_COMPILE_FLAG([-fvisibility=hidden], [CXXFLAGS="$CXXFLAGS -fvisibility=hidden"]) AX_CXXFLAGS_WARN_ALL AC_LANG_POP([C++]) # mingw c++ thread AC_LANG_PUSH([C++]) case $host_os in mingw32*) AC_CHECK_HEADER([mingw.thread.h], [ have_mingwstdtthreads=1 ], [ have_mingwstdtthreads=0 ] ) ;; *) have_mingwstdtthreads=0 ;; esac AC_LANG_POP([C++]) AS_IF([test "x$have_mingwstdtthreads" = "x1"], [ MINGWSTDTHREADS_CPPFLAGS=-DMPT_WITH_MINGWSTDTHREADS ], [ MINGWSTDTHREADS_CPPFLAGS= ] ) AC_SUBST([MINGWSTDTHREADS_CPPFLAGS]) DX_DOXYGEN_FEATURE(ON) DX_DOT_FEATURE(OFF) DX_HTML_FEATURE(ON) DX_MAN_FEATURE(OFF) DX_CHM_FEATURE(OFF) DX_CHI_FEATURE(OFF) DX_RTF_FEATURE(OFF) DX_XML_FEATURE(OFF) DX_PDF_FEATURE(OFF) DX_PS_FEATURE(OFF) DX_INIT_DOXYGEN([libopenmpt], [Doxyfile], [doxygen-doc]) AC_OUTPUT libopenmpt-0.6.1+release.autotools/aclocal.m40000644000175000017500000012643314175541530016132 00000000000000# generated automatically by aclocal 1.16.3 -*- Autoconf -*- # Copyright (C) 1996-2020 Free Software Foundation, Inc. # This file 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. m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.69],, [m4_warning([this file was generated for autoconf 2.69. You have another version of autoconf. It may work, but is not guaranteed to. If you have problems, you may need to regenerate the build system entirely. To do so, use the procedure documented by the package, typically 'autoreconf'.])]) # Copyright (C) 2002-2020 Free Software Foundation, Inc. # # This file 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. # AM_AUTOMAKE_VERSION(VERSION) # ---------------------------- # Automake X.Y traces this macro to ensure aclocal.m4 has been # generated from the m4 files accompanying Automake X.Y. # (This private macro should not be called outside this file.) AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version='1.16' dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to dnl require some minimum version. Point them to the right macro. m4_if([$1], [1.16.3], [], [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl ]) # _AM_AUTOCONF_VERSION(VERSION) # ----------------------------- # aclocal traces this macro to find the Autoconf version. # This is a private macro too. Using m4_define simplifies # the logic in aclocal, which can simply ignore this definition. m4_define([_AM_AUTOCONF_VERSION], []) # AM_SET_CURRENT_AUTOMAKE_VERSION # ------------------------------- # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. # This function is AC_REQUIREd by AM_INIT_AUTOMAKE. AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], [AM_AUTOMAKE_VERSION([1.16.3])dnl m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) # Copyright (C) 2011-2020 Free Software Foundation, Inc. # # This file 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. # AM_PROG_AR([ACT-IF-FAIL]) # ------------------------- # Try to determine the archiver interface, and trigger the ar-lib wrapper # if it is needed. If the detection of archiver interface fails, run # ACT-IF-FAIL (default is to abort configure with a proper error message). AC_DEFUN([AM_PROG_AR], [AC_BEFORE([$0], [LT_INIT])dnl AC_BEFORE([$0], [AC_PROG_LIBTOOL])dnl AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([ar-lib])dnl AC_CHECK_TOOLS([AR], [ar lib "link -lib"], [false]) : ${AR=ar} AC_CACHE_CHECK([the archiver ($AR) interface], [am_cv_ar_interface], [AC_LANG_PUSH([C]) am_cv_ar_interface=ar AC_COMPILE_IFELSE([AC_LANG_SOURCE([[int some_variable = 0;]])], [am_ar_try='$AR cru libconftest.a conftest.$ac_objext >&AS_MESSAGE_LOG_FD' AC_TRY_EVAL([am_ar_try]) if test "$ac_status" -eq 0; then am_cv_ar_interface=ar else am_ar_try='$AR -NOLOGO -OUT:conftest.lib conftest.$ac_objext >&AS_MESSAGE_LOG_FD' AC_TRY_EVAL([am_ar_try]) if test "$ac_status" -eq 0; then am_cv_ar_interface=lib else am_cv_ar_interface=unknown fi fi rm -f conftest.lib libconftest.a ]) AC_LANG_POP([C])]) case $am_cv_ar_interface in ar) ;; lib) # Microsoft lib, so override with the ar-lib wrapper script. # FIXME: It is wrong to rewrite AR. # But if we don't then we get into trouble of one sort or another. # A longer-term fix would be to have automake use am__AR in this case, # and then we could set am__AR="$am_aux_dir/ar-lib \$(AR)" or something # similar. AR="$am_aux_dir/ar-lib $AR" ;; unknown) m4_default([$1], [AC_MSG_ERROR([could not determine $AR interface])]) ;; esac AC_SUBST([AR])dnl ]) # AM_AUX_DIR_EXPAND -*- Autoconf -*- # Copyright (C) 2001-2020 Free Software Foundation, Inc. # # This file 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. # For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets # $ac_aux_dir to '$srcdir/foo'. In other projects, it is set to # '$srcdir', '$srcdir/..', or '$srcdir/../..'. # # Of course, Automake must honor this variable whenever it calls a # tool from the auxiliary directory. The problem is that $srcdir (and # therefore $ac_aux_dir as well) can be either absolute or relative, # depending on how configure is run. This is pretty annoying, since # it makes $ac_aux_dir quite unusable in subdirectories: in the top # source directory, any form will work fine, but in subdirectories a # relative path needs to be adjusted first. # # $ac_aux_dir/missing # fails when called from a subdirectory if $ac_aux_dir is relative # $top_srcdir/$ac_aux_dir/missing # fails if $ac_aux_dir is absolute, # fails when called from a subdirectory in a VPATH build with # a relative $ac_aux_dir # # The reason of the latter failure is that $top_srcdir and $ac_aux_dir # are both prefixed by $srcdir. In an in-source build this is usually # harmless because $srcdir is '.', but things will broke when you # start a VPATH build or use an absolute $srcdir. # # So we could use something similar to $top_srcdir/$ac_aux_dir/missing, # iff we strip the leading $srcdir from $ac_aux_dir. That would be: # am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` # and then we would define $MISSING as # MISSING="\${SHELL} $am_aux_dir/missing" # This will work as long as MISSING is not called from configure, because # unfortunately $(top_srcdir) has no meaning in configure. # However there are other variables, like CC, which are often used in # configure, and could therefore not use this "fixed" $ac_aux_dir. # # Another solution, used here, is to always expand $ac_aux_dir to an # absolute PATH. The drawback is that using absolute paths prevent a # configured tree to be moved without reconfiguration. AC_DEFUN([AM_AUX_DIR_EXPAND], [AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl # Expand $ac_aux_dir to an absolute path. am_aux_dir=`cd "$ac_aux_dir" && pwd` ]) # AM_CONDITIONAL -*- Autoconf -*- # Copyright (C) 1997-2020 Free Software Foundation, Inc. # # This file 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. # AM_CONDITIONAL(NAME, SHELL-CONDITION) # ------------------------------------- # Define a conditional. AC_DEFUN([AM_CONDITIONAL], [AC_PREREQ([2.52])dnl m4_if([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl AC_SUBST([$1_TRUE])dnl AC_SUBST([$1_FALSE])dnl _AM_SUBST_NOTMAKE([$1_TRUE])dnl _AM_SUBST_NOTMAKE([$1_FALSE])dnl m4_define([_AM_COND_VALUE_$1], [$2])dnl if $2; then $1_TRUE= $1_FALSE='#' else $1_TRUE='#' $1_FALSE= fi AC_CONFIG_COMMANDS_PRE( [if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then AC_MSG_ERROR([[conditional "$1" was never defined. Usually this means the macro was only invoked conditionally.]]) fi])]) # Copyright (C) 1999-2020 Free Software Foundation, Inc. # # This file 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. # There are a few dirty hacks below to avoid letting 'AC_PROG_CC' be # written in clear, in which case automake, when reading aclocal.m4, # will think it sees a *use*, and therefore will trigger all it's # C support machinery. Also note that it means that autoscan, seeing # CC etc. in the Makefile, will ask for an AC_PROG_CC use... # _AM_DEPENDENCIES(NAME) # ---------------------- # See how the compiler implements dependency checking. # NAME is "CC", "CXX", "OBJC", "OBJCXX", "UPC", or "GJC". # We try a few techniques and use that to set a single cache variable. # # We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was # modified to invoke _AM_DEPENDENCIES(CC); we would have a circular # dependency, and given that the user is not expected to run this macro, # just rely on AC_PROG_CC. AC_DEFUN([_AM_DEPENDENCIES], [AC_REQUIRE([AM_SET_DEPDIR])dnl AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl AC_REQUIRE([AM_MAKE_INCLUDE])dnl AC_REQUIRE([AM_DEP_TRACK])dnl m4_if([$1], [CC], [depcc="$CC" am_compiler_list=], [$1], [CXX], [depcc="$CXX" am_compiler_list=], [$1], [OBJC], [depcc="$OBJC" am_compiler_list='gcc3 gcc'], [$1], [OBJCXX], [depcc="$OBJCXX" am_compiler_list='gcc3 gcc'], [$1], [UPC], [depcc="$UPC" am_compiler_list=], [$1], [GCJ], [depcc="$GCJ" am_compiler_list='gcc3 gcc'], [depcc="$$1" am_compiler_list=]) AC_CACHE_CHECK([dependency style of $depcc], [am_cv_$1_dependencies_compiler_type], [if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For # instance it was reported that on HP-UX the gcc test will end up # making a dummy file named 'D' -- because '-MD' means "put the output # in D". rm -rf conftest.dir mkdir conftest.dir # Copy depcomp to subdir because otherwise we won't find it if we're # using a relative directory. cp "$am_depcomp" conftest.dir cd conftest.dir # We will build objects and dependencies in a subdirectory because # it helps to detect inapplicable dependency modes. For instance # both Tru64's cc and ICC support -MD to output dependencies as a # side effect of compilation, but ICC will put the dependencies in # the current directory while Tru64 will put them in the object # directory. mkdir sub am_cv_$1_dependencies_compiler_type=none if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp` fi am__universal=false m4_case([$1], [CC], [case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac], [CXX], [case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac]) for depmode in $am_compiler_list; do # Setup a source with many dependencies, because some compilers # like to wrap large dependency lists on column 80 (with \), and # we should not choose a depcomp mode which is confused by this. # # We need to recreate these files for each test, as the compiler may # overwrite some of them when testing with obscure command lines. # This happens at least with the AIX C compiler. : > sub/conftest.c for i in 1 2 3 4 5 6; do echo '#include "conftst'$i'.h"' >> sub/conftest.c # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with # Solaris 10 /bin/sh. echo '/* dummy */' > sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf # We check with '-c' and '-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly # handle '-M -o', and we need to detect this. Also, some Intel # versions had trouble with output in subdirs. am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in gcc) # This depmode causes a compiler race in universal mode. test "$am__universal" = false || continue ;; nosideeffect) # After this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested. if test "x$enable_dependency_tracking" = xyes; then continue else break fi ;; msvc7 | msvc7msys | msvisualcpp | msvcmsys) # This compiler won't grok '-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} am__minus_obj= ;; none) break ;; esac if depmode=$depmode \ source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && grep $am__obj sub/conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then # icc doesn't choke on unknown options, it will just issue warnings # or remarks (even with -Werror). So we grep stderr for any message # that says an option was ignored or not supported. # When given -MP, icc 7.0 and 7.1 complain thusly: # icc: Command line warning: ignoring option '-M'; no argument required # The diagnosis changed in icc 8.0: # icc: Command line remark: option '-MP' not supported if (grep 'ignoring option' conftest.err || grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else am_cv_$1_dependencies_compiler_type=$depmode break fi fi done cd .. rm -rf conftest.dir else am_cv_$1_dependencies_compiler_type=none fi ]) AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type]) AM_CONDITIONAL([am__fastdep$1], [ test "x$enable_dependency_tracking" != xno \ && test "$am_cv_$1_dependencies_compiler_type" = gcc3]) ]) # AM_SET_DEPDIR # ------------- # Choose a directory name for dependency files. # This macro is AC_REQUIREd in _AM_DEPENDENCIES. AC_DEFUN([AM_SET_DEPDIR], [AC_REQUIRE([AM_SET_LEADING_DOT])dnl AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl ]) # AM_DEP_TRACK # ------------ AC_DEFUN([AM_DEP_TRACK], [AC_ARG_ENABLE([dependency-tracking], [dnl AS_HELP_STRING( [--enable-dependency-tracking], [do not reject slow dependency extractors]) AS_HELP_STRING( [--disable-dependency-tracking], [speeds up one-time build])]) if test "x$enable_dependency_tracking" != xno; then am_depcomp="$ac_aux_dir/depcomp" AMDEPBACKSLASH='\' am__nodep='_no' fi AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) AC_SUBST([AMDEPBACKSLASH])dnl _AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl AC_SUBST([am__nodep])dnl _AM_SUBST_NOTMAKE([am__nodep])dnl ]) # Generate code to set up dependency tracking. -*- Autoconf -*- # Copyright (C) 1999-2020 Free Software Foundation, Inc. # # This file 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. # _AM_OUTPUT_DEPENDENCY_COMMANDS # ------------------------------ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], [{ # Older Autoconf quotes --file arguments for eval, but not when files # are listed without --file. Let's play safe and only enable the eval # if we detect the quoting. # TODO: see whether this extra hack can be removed once we start # requiring Autoconf 2.70 or later. AS_CASE([$CONFIG_FILES], [*\'*], [eval set x "$CONFIG_FILES"], [*], [set x $CONFIG_FILES]) shift # Used to flag and report bootstrapping failures. am_rc=0 for am_mf do # Strip MF so we end up with the name of the file. am_mf=`AS_ECHO(["$am_mf"]) | sed -e 's/:.*$//'` # Check whether this is an Automake generated Makefile which includes # dependency-tracking related rules and includes. # Grep'ing the whole file directly is not great: AIX grep has a line # limit of 2048, but all sed's we know have understand at least 4000. sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \ || continue am_dirpart=`AS_DIRNAME(["$am_mf"])` am_filepart=`AS_BASENAME(["$am_mf"])` AM_RUN_LOG([cd "$am_dirpart" \ && sed -e '/# am--include-marker/d' "$am_filepart" \ | $MAKE -f - am--depfiles]) || am_rc=$? done if test $am_rc -ne 0; then AC_MSG_FAILURE([Something went wrong bootstrapping makefile fragments for automatic dependency tracking. If GNU make was not used, consider re-running the configure script with MAKE="gmake" (or whatever is necessary). You can also try re-running configure with the '--disable-dependency-tracking' option to at least be able to build the package (albeit without support for automatic dependency tracking).]) fi AS_UNSET([am_dirpart]) AS_UNSET([am_filepart]) AS_UNSET([am_mf]) AS_UNSET([am_rc]) rm -f conftest-deps.mk } ])# _AM_OUTPUT_DEPENDENCY_COMMANDS # AM_OUTPUT_DEPENDENCY_COMMANDS # ----------------------------- # This macro should only be invoked once -- use via AC_REQUIRE. # # This code is only required when automatic dependency tracking is enabled. # This creates each '.Po' and '.Plo' makefile fragment that we'll need in # order to bootstrap the dependency handling code. AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], [AC_CONFIG_COMMANDS([depfiles], [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], [AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}"])]) # Do all the work for Automake. -*- Autoconf -*- # Copyright (C) 1996-2020 Free Software Foundation, Inc. # # This file 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 macro actually does too much. Some checks are only needed if # your package does certain things. But this isn't really a big deal. dnl Redefine AC_PROG_CC to automatically invoke _AM_PROG_CC_C_O. m4_define([AC_PROG_CC], m4_defn([AC_PROG_CC]) [_AM_PROG_CC_C_O ]) # AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) # AM_INIT_AUTOMAKE([OPTIONS]) # ----------------------------------------------- # The call with PACKAGE and VERSION arguments is the old style # call (pre autoconf-2.50), which is being phased out. PACKAGE # and VERSION should now be passed to AC_INIT and removed from # the call to AM_INIT_AUTOMAKE. # We support both call styles for the transition. After # the next Automake release, Autoconf can make the AC_INIT # arguments mandatory, and then we can depend on a new Autoconf # release and drop the old call support. AC_DEFUN([AM_INIT_AUTOMAKE], [AC_PREREQ([2.65])dnl dnl Autoconf wants to disallow AM_ names. We explicitly allow dnl the ones we care about. m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl AC_REQUIRE([AC_PROG_INSTALL])dnl if test "`cd $srcdir && pwd`" != "`pwd`"; then # Use -I$(srcdir) only when $(srcdir) != ., so that make's output # is not polluted with repeated "-I." AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl # test to see if srcdir already configured if test -f $srcdir/config.status; then AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) fi fi # test whether we have cygpath if test -z "$CYGPATH_W"; then if (cygpath --version) >/dev/null 2>/dev/null; then CYGPATH_W='cygpath -w' else CYGPATH_W=echo fi fi AC_SUBST([CYGPATH_W]) # Define the identity of the package. dnl Distinguish between old-style and new-style calls. m4_ifval([$2], [AC_DIAGNOSE([obsolete], [$0: two- and three-arguments forms are deprecated.]) m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl AC_SUBST([PACKAGE], [$1])dnl AC_SUBST([VERSION], [$2])], [_AM_SET_OPTIONS([$1])dnl dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT. m4_if( m4_ifdef([AC_PACKAGE_NAME], [ok]):m4_ifdef([AC_PACKAGE_VERSION], [ok]), [ok:ok],, [m4_fatal([AC_INIT should be called with package and version arguments])])dnl AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl _AM_IF_OPTION([no-define],, [AC_DEFINE_UNQUOTED([PACKAGE], ["$PACKAGE"], [Name of package]) AC_DEFINE_UNQUOTED([VERSION], ["$VERSION"], [Version number of package])])dnl # Some tools Automake needs. AC_REQUIRE([AM_SANITY_CHECK])dnl AC_REQUIRE([AC_ARG_PROGRAM])dnl AM_MISSING_PROG([ACLOCAL], [aclocal-${am__api_version}]) AM_MISSING_PROG([AUTOCONF], [autoconf]) AM_MISSING_PROG([AUTOMAKE], [automake-${am__api_version}]) AM_MISSING_PROG([AUTOHEADER], [autoheader]) AM_MISSING_PROG([MAKEINFO], [makeinfo]) AC_REQUIRE([AM_PROG_INSTALL_SH])dnl AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl AC_REQUIRE([AC_PROG_MKDIR_P])dnl # For better backward compatibility. To be removed once Automake 1.9.x # dies out for good. For more background, see: # # AC_SUBST([mkdir_p], ['$(MKDIR_P)']) # We need awk for the "check" target (and possibly the TAP driver). The # system "awk" is bad on some platforms. AC_REQUIRE([AC_PROG_AWK])dnl AC_REQUIRE([AC_PROG_MAKE_SET])dnl AC_REQUIRE([AM_SET_LEADING_DOT])dnl _AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], [_AM_PROG_TAR([v7])])]) _AM_IF_OPTION([no-dependencies],, [AC_PROVIDE_IFELSE([AC_PROG_CC], [_AM_DEPENDENCIES([CC])], [m4_define([AC_PROG_CC], m4_defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl AC_PROVIDE_IFELSE([AC_PROG_CXX], [_AM_DEPENDENCIES([CXX])], [m4_define([AC_PROG_CXX], m4_defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl AC_PROVIDE_IFELSE([AC_PROG_OBJC], [_AM_DEPENDENCIES([OBJC])], [m4_define([AC_PROG_OBJC], m4_defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl AC_PROVIDE_IFELSE([AC_PROG_OBJCXX], [_AM_DEPENDENCIES([OBJCXX])], [m4_define([AC_PROG_OBJCXX], m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl ]) AC_REQUIRE([AM_SILENT_RULES])dnl dnl The testsuite driver may need to know about EXEEXT, so add the dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This dnl macro is hooked onto _AC_COMPILER_EXEEXT early, see below. AC_CONFIG_COMMANDS_PRE(dnl [m4_provide_if([_AM_COMPILER_EXEEXT], [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl # POSIX will say in a future version that running "rm -f" with no argument # is OK; and we want to be able to make that assumption in our Makefile # recipes. So use an aggressive probe to check that the usage we want is # actually supported "in the wild" to an acceptable degree. # See automake bug#10828. # To make any issue more visible, cause the running configure to be aborted # by default if the 'rm' program in use doesn't match our expectations; the # user can still override this though. if rm -f && rm -fr && rm -rf; then : OK; else cat >&2 <<'END' Oops! Your 'rm' program seems unable to run without file operands specified on the command line, even when the '-f' option is present. This is contrary to the behaviour of most rm programs out there, and not conforming with the upcoming POSIX standard: Please tell bug-automake@gnu.org about your system, including the value of your $PATH and any error possibly output before this message. This can help us improve future automake versions. END if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then echo 'Configuration will proceed anyway, since you have set the' >&2 echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 echo >&2 else cat >&2 <<'END' Aborting the configuration process, to ensure you take notice of the issue. You can download and install GNU coreutils to get an 'rm' implementation that behaves properly: . If you want to complete the configuration process using your problematic 'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM to "yes", and re-run configure. END AC_MSG_ERROR([Your 'rm' program is bad, sorry.]) fi fi dnl The trailing newline in this macro's definition is deliberate, for dnl backward compatibility and to allow trailing 'dnl'-style comments dnl after the AM_INIT_AUTOMAKE invocation. See automake bug#16841. ]) dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion. Do not dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further dnl mangled by Autoconf and run in a shell conditional statement. m4_define([_AC_COMPILER_EXEEXT], m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])]) # When config.status generates a header, we must update the stamp-h file. # This file resides in the same directory as the config header # that is generated. The stamp files are numbered to have different names. # Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the # loop where config.status creates the headers, so we can generate # our stamp files there. AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], [# Compute $1's index in $config_headers. _am_arg=$1 _am_stamp_count=1 for _am_header in $config_headers :; do case $_am_header in $_am_arg | $_am_arg:* ) break ;; * ) _am_stamp_count=`expr $_am_stamp_count + 1` ;; esac done echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) # Copyright (C) 2001-2020 Free Software Foundation, Inc. # # This file 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. # AM_PROG_INSTALL_SH # ------------------ # Define $install_sh. AC_DEFUN([AM_PROG_INSTALL_SH], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl if test x"${install_sh+set}" != xset; then case $am_aux_dir in *\ * | *\ *) install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; *) install_sh="\${SHELL} $am_aux_dir/install-sh" esac fi AC_SUBST([install_sh])]) # Copyright (C) 2003-2020 Free Software Foundation, Inc. # # This file 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. # Check whether the underlying file-system supports filenames # with a leading dot. For instance MS-DOS doesn't. AC_DEFUN([AM_SET_LEADING_DOT], [rm -rf .tst 2>/dev/null mkdir .tst 2>/dev/null if test -d .tst; then am__leading_dot=. else am__leading_dot=_ fi rmdir .tst 2>/dev/null AC_SUBST([am__leading_dot])]) # Check to see how 'make' treats includes. -*- Autoconf -*- # Copyright (C) 2001-2020 Free Software Foundation, Inc. # # This file 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. # AM_MAKE_INCLUDE() # ----------------- # Check whether make has an 'include' directive that can support all # the idioms we need for our automatic dependency tracking code. AC_DEFUN([AM_MAKE_INCLUDE], [AC_MSG_CHECKING([whether ${MAKE-make} supports the include directive]) cat > confinc.mk << 'END' am__doit: @echo this is the am__doit target >confinc.out .PHONY: am__doit END am__include="#" am__quote= # BSD make does it like this. echo '.include "confinc.mk" # ignored' > confmf.BSD # Other make implementations (GNU, Solaris 10, AIX) do it like this. echo 'include confinc.mk # ignored' > confmf.GNU _am_result=no for s in GNU BSD; do AM_RUN_LOG([${MAKE-make} -f confmf.$s && cat confinc.out]) AS_CASE([$?:`cat confinc.out 2>/dev/null`], ['0:this is the am__doit target'], [AS_CASE([$s], [BSD], [am__include='.include' am__quote='"'], [am__include='include' am__quote=''])]) if test "$am__include" != "#"; then _am_result="yes ($s style)" break fi done rm -f confinc.* confmf.* AC_MSG_RESULT([${_am_result}]) AC_SUBST([am__include])]) AC_SUBST([am__quote])]) # Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- # Copyright (C) 1997-2020 Free Software Foundation, Inc. # # This file 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. # AM_MISSING_PROG(NAME, PROGRAM) # ------------------------------ AC_DEFUN([AM_MISSING_PROG], [AC_REQUIRE([AM_MISSING_HAS_RUN]) $1=${$1-"${am_missing_run}$2"} AC_SUBST($1)]) # AM_MISSING_HAS_RUN # ------------------ # Define MISSING if not defined so far and test if it is modern enough. # If it is, set am_missing_run to use it, otherwise, to nothing. AC_DEFUN([AM_MISSING_HAS_RUN], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([missing])dnl if test x"${MISSING+set}" != xset; then MISSING="\${SHELL} '$am_aux_dir/missing'" fi # Use eval to expand $SHELL if eval "$MISSING --is-lightweight"; then am_missing_run="$MISSING " else am_missing_run= AC_MSG_WARN(['missing' script is too old or missing]) fi ]) # Helper functions for option handling. -*- Autoconf -*- # Copyright (C) 2001-2020 Free Software Foundation, Inc. # # This file 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. # _AM_MANGLE_OPTION(NAME) # ----------------------- AC_DEFUN([_AM_MANGLE_OPTION], [[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) # _AM_SET_OPTION(NAME) # -------------------- # Set option NAME. Presently that only means defining a flag for this option. AC_DEFUN([_AM_SET_OPTION], [m4_define(_AM_MANGLE_OPTION([$1]), [1])]) # _AM_SET_OPTIONS(OPTIONS) # ------------------------ # OPTIONS is a space-separated list of Automake options. AC_DEFUN([_AM_SET_OPTIONS], [m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) # _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) # ------------------------------------------- # Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. AC_DEFUN([_AM_IF_OPTION], [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) # Copyright (C) 1999-2020 Free Software Foundation, Inc. # # This file 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. # _AM_PROG_CC_C_O # --------------- # Like AC_PROG_CC_C_O, but changed for automake. We rewrite AC_PROG_CC # to automatically call this. AC_DEFUN([_AM_PROG_CC_C_O], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([compile])dnl AC_LANG_PUSH([C])dnl AC_CACHE_CHECK( [whether $CC understands -c and -o together], [am_cv_prog_cc_c_o], [AC_LANG_CONFTEST([AC_LANG_PROGRAM([])]) # Make sure it works both with $CC and with simple cc. # Following AC_PROG_CC_C_O, we do the test twice because some # compilers refuse to overwrite an existing .o file with -o, # though they will create one. am_cv_prog_cc_c_o=yes for am_i in 1 2; do if AM_RUN_LOG([$CC -c conftest.$ac_ext -o conftest2.$ac_objext]) \ && test -f conftest2.$ac_objext; then : OK else am_cv_prog_cc_c_o=no break fi done rm -f core conftest* unset am_i]) if test "$am_cv_prog_cc_c_o" != yes; then # Losing compiler, so override with the script. # FIXME: It is wrong to rewrite CC. # But if we don't then we get into trouble of one sort or another. # A longer-term fix would be to have automake use am__CC in this case, # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" CC="$am_aux_dir/compile $CC" fi AC_LANG_POP([C])]) # For backward compatibility. AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])]) # Copyright (C) 2001-2020 Free Software Foundation, Inc. # # This file 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. # AM_RUN_LOG(COMMAND) # ------------------- # Run COMMAND, save the exit status in ac_status, and log it. # (This has been adapted from Autoconf's _AC_RUN_LOG macro.) AC_DEFUN([AM_RUN_LOG], [{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD (exit $ac_status); }]) # Check to make sure that the build environment is sane. -*- Autoconf -*- # Copyright (C) 1996-2020 Free Software Foundation, Inc. # # This file 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. # AM_SANITY_CHECK # --------------- AC_DEFUN([AM_SANITY_CHECK], [AC_MSG_CHECKING([whether build environment is sane]) # Reject unsafe characters in $srcdir or the absolute working directory # name. Accept space and tab only in the latter. am_lf=' ' case `pwd` in *[[\\\"\#\$\&\'\`$am_lf]]*) AC_MSG_ERROR([unsafe absolute working directory name]);; esac case $srcdir in *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*) AC_MSG_ERROR([unsafe srcdir value: '$srcdir']);; esac # Do 'set' in a subshell so we don't clobber the current shell's # arguments. Must try -L first in case configure is actually a # symlink; some systems play weird games with the mod time of symlinks # (eg FreeBSD returns the mod time of the symlink's containing # directory). if ( am_has_slept=no for am_try in 1 2; do echo "timestamp, slept: $am_has_slept" > conftest.file set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` if test "$[*]" = "X"; then # -L didn't work. set X `ls -t "$srcdir/configure" conftest.file` fi if test "$[*]" != "X $srcdir/configure conftest.file" \ && test "$[*]" != "X conftest.file $srcdir/configure"; then # If neither matched, then we have a broken ls. This can happen # if, for instance, CONFIG_SHELL is bash and it inherits a # broken ls alias from the environment. This has actually # happened. Such a system could not be considered "sane". AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken alias in your environment]) fi if test "$[2]" = conftest.file || test $am_try -eq 2; then break fi # Just in case. sleep 1 am_has_slept=yes done test "$[2]" = conftest.file ) then # Ok. : else AC_MSG_ERROR([newly created file is older than distributed files! Check your system clock]) fi AC_MSG_RESULT([yes]) # If we didn't sleep, we still need to ensure time stamps of config.status and # generated files are strictly newer. am_sleep_pid= if grep 'slept: no' conftest.file >/dev/null 2>&1; then ( sleep 1 ) & am_sleep_pid=$! fi AC_CONFIG_COMMANDS_PRE( [AC_MSG_CHECKING([that generated files are newer than configure]) if test -n "$am_sleep_pid"; then # Hide warnings about reused PIDs. wait $am_sleep_pid 2>/dev/null fi AC_MSG_RESULT([done])]) rm -f conftest.file ]) # Copyright (C) 2009-2020 Free Software Foundation, Inc. # # This file 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. # AM_SILENT_RULES([DEFAULT]) # -------------------------- # Enable less verbose build rules; with the default set to DEFAULT # ("yes" being less verbose, "no" or empty being verbose). AC_DEFUN([AM_SILENT_RULES], [AC_ARG_ENABLE([silent-rules], [dnl AS_HELP_STRING( [--enable-silent-rules], [less verbose build output (undo: "make V=1")]) AS_HELP_STRING( [--disable-silent-rules], [verbose build output (undo: "make V=0")])dnl ]) case $enable_silent_rules in @%:@ ((( yes) AM_DEFAULT_VERBOSITY=0;; no) AM_DEFAULT_VERBOSITY=1;; *) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);; esac dnl dnl A few 'make' implementations (e.g., NonStop OS and NextStep) dnl do not support nested variable expansions. dnl See automake bug#9928 and bug#10237. am_make=${MAKE-make} AC_CACHE_CHECK([whether $am_make supports nested variables], [am_cv_make_support_nested_variables], [if AS_ECHO([['TRUE=$(BAR$(V)) BAR0=false BAR1=true V=1 am__doit: @$(TRUE) .PHONY: am__doit']]) | $am_make -f - >/dev/null 2>&1; then am_cv_make_support_nested_variables=yes else am_cv_make_support_nested_variables=no fi]) if test $am_cv_make_support_nested_variables = yes; then dnl Using '$V' instead of '$(V)' breaks IRIX make. AM_V='$(V)' AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' else AM_V=$AM_DEFAULT_VERBOSITY AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY fi AC_SUBST([AM_V])dnl AM_SUBST_NOTMAKE([AM_V])dnl AC_SUBST([AM_DEFAULT_V])dnl AM_SUBST_NOTMAKE([AM_DEFAULT_V])dnl AC_SUBST([AM_DEFAULT_VERBOSITY])dnl AM_BACKSLASH='\' AC_SUBST([AM_BACKSLASH])dnl _AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl ]) # Copyright (C) 2001-2020 Free Software Foundation, Inc. # # This file 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. # AM_PROG_INSTALL_STRIP # --------------------- # One issue with vendor 'install' (even GNU) is that you can't # specify the program used to strip binaries. This is especially # annoying in cross-compiling environments, where the build's strip # is unlikely to handle the host's binaries. # Fortunately install-sh will honor a STRIPPROG variable, so we # always use install-sh in "make install-strip", and initialize # STRIPPROG with the value of the STRIP variable (set by the user). AC_DEFUN([AM_PROG_INSTALL_STRIP], [AC_REQUIRE([AM_PROG_INSTALL_SH])dnl # Installed binaries are usually stripped using 'strip' when the user # run "make install-strip". However 'strip' might not be the right # tool to use in cross-compilation environments, therefore Automake # will honor the 'STRIP' environment variable to overrule this program. dnl Don't test for $cross_compiling = yes, because it might be 'maybe'. if test "$cross_compiling" != no; then AC_CHECK_TOOL([STRIP], [strip], :) fi INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" AC_SUBST([INSTALL_STRIP_PROGRAM])]) # Copyright (C) 2006-2020 Free Software Foundation, Inc. # # This file 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. # _AM_SUBST_NOTMAKE(VARIABLE) # --------------------------- # Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in. # This macro is traced by Automake. AC_DEFUN([_AM_SUBST_NOTMAKE]) # AM_SUBST_NOTMAKE(VARIABLE) # -------------------------- # Public sister of _AM_SUBST_NOTMAKE. AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) # Check how to create a tarball. -*- Autoconf -*- # Copyright (C) 2004-2020 Free Software Foundation, Inc. # # This file 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. # _AM_PROG_TAR(FORMAT) # -------------------- # Check how to create a tarball in format FORMAT. # FORMAT should be one of 'v7', 'ustar', or 'pax'. # # Substitute a variable $(am__tar) that is a command # writing to stdout a FORMAT-tarball containing the directory # $tardir. # tardir=directory && $(am__tar) > result.tar # # Substitute a variable $(am__untar) that extract such # a tarball read from stdin. # $(am__untar) < result.tar # AC_DEFUN([_AM_PROG_TAR], [# Always define AMTAR for backward compatibility. Yes, it's still used # in the wild :-( We should find a proper way to deprecate it ... AC_SUBST([AMTAR], ['$${TAR-tar}']) # We'll loop over all known methods to create a tar archive until one works. _am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' m4_if([$1], [v7], [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'], [m4_case([$1], [ustar], [# The POSIX 1988 'ustar' format is defined with fixed-size fields. # There is notably a 21 bits limit for the UID and the GID. In fact, # the 'pax' utility can hang on bigger UID/GID (see automake bug#8343 # and bug#13588). am_max_uid=2097151 # 2^21 - 1 am_max_gid=$am_max_uid # The $UID and $GID variables are not portable, so we need to resort # to the POSIX-mandated id(1) utility. Errors in the 'id' calls # below are definitely unexpected, so allow the users to see them # (that is, avoid stderr redirection). am_uid=`id -u || echo unknown` am_gid=`id -g || echo unknown` AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format]) if test $am_uid -le $am_max_uid; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) _am_tools=none fi AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format]) if test $am_gid -le $am_max_gid; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) _am_tools=none fi], [pax], [], [m4_fatal([Unknown tar format])]) AC_MSG_CHECKING([how to create a $1 tar archive]) # Go ahead even if we have the value already cached. We do so because we # need to set the values for the 'am__tar' and 'am__untar' variables. _am_tools=${am_cv_prog_tar_$1-$_am_tools} for _am_tool in $_am_tools; do case $_am_tool in gnutar) for _am_tar in tar gnutar gtar; do AM_RUN_LOG([$_am_tar --version]) && break done am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' am__untar="$_am_tar -xf -" ;; plaintar) # Must skip GNU tar: if it does not support --format= it doesn't create # ustar tarball either. (tar --version) >/dev/null 2>&1 && continue am__tar='tar chf - "$$tardir"' am__tar_='tar chf - "$tardir"' am__untar='tar xf -' ;; pax) am__tar='pax -L -x $1 -w "$$tardir"' am__tar_='pax -L -x $1 -w "$tardir"' am__untar='pax -r' ;; cpio) am__tar='find "$$tardir" -print | cpio -o -H $1 -L' am__tar_='find "$tardir" -print | cpio -o -H $1 -L' am__untar='cpio -i -H $1 -d' ;; none) am__tar=false am__tar_=false am__untar=false ;; esac # If the value was cached, stop now. We just wanted to have am__tar # and am__untar set. test -n "${am_cv_prog_tar_$1}" && break # tar/untar a dummy directory, and stop if the command works. rm -rf conftest.dir mkdir conftest.dir echo GrepMe > conftest.dir/file AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) rm -rf conftest.dir if test -s conftest.tar; then AM_RUN_LOG([$am__untar /dev/null 2>&1 && break fi done rm -rf conftest.dir AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) AC_MSG_RESULT([$am_cv_prog_tar_$1])]) AC_SUBST([am__tar]) AC_SUBST([am__untar]) ]) # _AM_PROG_TAR m4_include([m4/ax_append_flag.m4]) m4_include([m4/ax_cflags_warn_all.m4]) m4_include([m4/ax_check_compile_flag.m4]) m4_include([m4/ax_cxx_compile_stdcxx.m4]) m4_include([m4/ax_prog_doxygen.m4]) m4_include([m4/ax_require_defined.m4]) m4_include([m4/libtool.m4]) m4_include([m4/ltoptions.m4]) m4_include([m4/ltsugar.m4]) m4_include([m4/ltversion.m4]) m4_include([m4/lt~obsolete.m4]) m4_include([m4/pkg.m4]) libopenmpt-0.6.1+release.autotools/LICENSE0000644000175000017500000000312514164006754015271 00000000000000Copyright (c) 2004-2022, OpenMPT Project Developers and Contributors Copyright (c) 1997-2003, Olivier Lapicque All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the OpenMPT project nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. libopenmpt-0.6.1+release.autotools/README.md0000644000175000017500000000610114170042301015521 00000000000000 README ====== OpenMPT and libopenmpt ====================== This repository contains OpenMPT, a free Windows/Wine-based [tracker](https://en.wikipedia.org/wiki/Music_tracker) and libopenmpt, a library to render tracker music (MOD, XM, S3M, IT MPTM and dozens of other legacy formats) to a PCM audio stream. libopenmpt is directly based on OpenMPT, offering the same playback quality and format support, and development of the two happens in parallel. License ------- The OpenMPT/libopenmpt project is distributed under the *BSD-3-Clause* License. See [LICENSE](LICENSE) for the full license text. Files below the `include/` (external projects) and `contrib/` (related assets not directly considered integral part of the project) folders may be subject to other licenses. See the respective subfolders for license information. These folders are not distributed in all source packages, and in particular they are not distributed in the Autotools packages. How to compile -------------- ### OpenMPT - Supported Visual Studio versions: - Visual Studio 2017, 2019, and 2022 Community/Professional/Enterprise To compile the project, open `build/vsVERSIONwin7/OpenMPT.sln` (VERSION being 2017, 2019, or 2022) and hit the compile button. Other target systems can be found in the `vs2017*`, `vs2019*`, and `vs2022` sibling folders. Note that you have to build the `PluginBridge` and `PluginBridgeLegacy` projects manually for architectures other than the one you are building OpenMPT for, as Visual Studio only builds one architecture configuration at a time. Please note that we do not support building with a later Visual Studio installation with an earlier compiler version. This is because, while later Visual Studio versions allow installing earlier compilers to be available via the later version's environment, in this configuration, the earlier compiler will still use the later C and C++ runtime's headers and implementation, which significantly increases the matrix of possible configurations to test. - OpenMPT requires the compile host system to be Windows 8.1 (or later) amd64, or Windows 11 (or later) ARM64. - In order to build OpenMPT for Windows XP, the Visual Studio 2017 XP targetting toolset as well as the Windows 8.1 SDK need to be installed. The SDK is optionally included with Visual Studio 2017, but must be separately installed with later Visual Studio versions. The Windows 8.1 SDK is available from or directly from . - Microsoft Foundation Classes (MFC) are required to build OpenMPT. ### libopenmpt and openmpt123 See [Dependencies](libopenmpt/dox/dependencies.md) and [Getting Started](libopenmpt/dox/gettingstarted.md). Contributing to OpenMPT/libopenmpt ---------------------------------- See [contributing](doc/contributing.md). libopenmpt-0.6.1+release.autotools/Doxyfile.in0000644000175000017500000034305414175541513016406 00000000000000# Doxyfile 1.9.1 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a double hash (##) is considered a comment and is placed in # front of the TAG it is preceding. # # All text after a single hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists, items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (\" \"). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the configuration # file that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # https://www.gnu.org/software/libiconv/ for the list of possible encodings. # The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded by # double-quotes, unless you are using Doxywizard) that should identify the # project for which the documentation is generated. This name is used in the # title of most generated pages and in a few other places. # The default value is: My Project. PROJECT_NAME = libopenmpt # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version # control system is used. # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = "cross-platform C++ and C library to decode tracked music files" # With the PROJECT_LOGO tag one can specify a logo or an icon that is included # in the documentation. The maximum height of the logo should not exceed 55 # pixels and the maximum width should not exceed 200 pixels. Doxygen will copy # the logo to the output directory. PROJECT_LOGO = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. OUTPUT_DIRECTORY = bin/docs # If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and # will distribute the generated files over these directories. Enabling this # option can be useful when feeding doxygen a huge amount of source files, where # putting all generated files in the same directory would otherwise causes # performance problems for the file system. # The default value is: NO. CREATE_SUBDIRS = NO # If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII # characters to appear in the names of generated files. If set to NO, non-ASCII # characters will be escaped, for example _xE3_x81_x84 will be used for Unicode # U+3044. # The default value is: NO. ALLOW_UNICODE_NAMES = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, # Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), # Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, # Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), # Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, # Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, # Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, # Ukrainian and Vietnamese. # The default value is: English. OUTPUT_LANGUAGE = English # The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all generated output in the proper direction. # Possible values are: None, LTR, RTL and Context. # The default value is: None. OUTPUT_TEXT_DIRECTION = None # If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. # The default value is: YES. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief # description of a member or function before the detailed description # # Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. # The default value is: YES. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator that is # used to form the text in various listings. Each string in this list, if found # as the leading text of the brief description, will be stripped from the text # and the result, after processing the whole list, is used as the annotated # text. Otherwise, the brief description is used as-is. If left blank, the # following values are used ($name is automatically replaced with the name of # the entity):The $name class, The $name widget, The $name file, is, provides, # specifies, contains, represents, a, an and the. ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # doxygen will generate a detailed section even if there is only a brief # description. # The default value is: NO. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. # The default value is: NO. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path # before files name in the file list and in the header files. If set to NO the # shortest path that makes the file name unique will be used # The default value is: YES. FULL_PATH_NAMES = YES # The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. # Stripping is only done if one of the specified strings matches the left-hand # part of the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the path to # strip. # # Note that you can specify absolute paths here, but also relative paths, which # will be relative from the directory where doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which # header file to include in order to use a class. If left blank only the name of # the header file containing the class definition is used. Otherwise one should # specify the list of include paths that are normally passed to the compiler # using the -I flag. STRIP_FROM_INC_PATH = . # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but # less readable) file names. This can be useful is your file systems doesn't # support long names like on DOS, Mac, or CD-ROM. # The default value is: NO. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the # first line (until the first dot) of a Javadoc-style comment as the brief # description. If set to NO, the Javadoc-style will behave just like regular Qt- # style comments (thus requiring an explicit @brief command for a brief # description.) # The default value is: NO. JAVADOC_AUTOBRIEF = NO # If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line # such as # /*************** # as being the beginning of a Javadoc-style comment "banner". If set to NO, the # Javadoc-style will behave just like regular comments and it will not be # interpreted by doxygen. # The default value is: NO. JAVADOC_BANNER = NO # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first # line (until the first dot) of a Qt-style comment as the brief description. If # set to NO, the Qt-style will behave just like regular Qt-style comments (thus # requiring an explicit \brief command for a brief description.) # The default value is: NO. QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a # multi-line C++ special comment block (i.e. a block of //! or /// comments) as # a brief description. This used to be the default behavior. The new default is # to treat a multi-line C++ comment block as a detailed description. Set this # tag to YES if you prefer the old behavior instead. # # Note that setting this tag to YES also means that rational rose comments are # not recognized any more. # The default value is: NO. MULTILINE_CPP_IS_BRIEF = NO # By default Python docstrings are displayed as preformatted text and doxygen's # special commands cannot be used. By setting PYTHON_DOCSTRING to NO the # doxygen's special commands can be used and the contents of the docstring # documentation blocks is shown as doxygen documentation. # The default value is: YES. PYTHON_DOCSTRING = YES # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the # documentation from any documented member that it re-implements. # The default value is: YES. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new # page for each member. If set to NO, the documentation of a member will be part # of the file/class/namespace that contains it. # The default value is: NO. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen # uses this value to replace tabs by spaces in code fragments. # Minimum value: 1, maximum value: 16, default value: 4. TAB_SIZE = 2 # This tag can be used to specify a number of aliases that act as commands in # the documentation. An alias has the form: # name=value # For example adding # "sideeffect=@par Side Effects:\n" # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading # "Side Effects:". You can put \n's in the value part of an alias to insert # newlines (in the resulting output). You can put ^^ in the value part of an # alias to insert a newline as if a physical newline was in the original file. # When you need a literal { or } or , in the value part of an alias you have to # escape them by means of a backslash (\), this can lead to conflicts with the # commands \{ and \} for these it is advised to use the version @{ and @} or use # a double escape (\\{ and \\}) ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For # instance, some of the names that are used will be different. The list of all # members will be omitted, etc. # The default value is: NO. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or # Python sources only. Doxygen will then generate output that is more tailored # for that language. For instance, namespaces will be presented as packages, # qualified scopes will look different, etc. # The default value is: NO. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources. Doxygen will then generate output that is tailored for Fortran. # The default value is: NO. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for VHDL. # The default value is: NO. OPTIMIZE_OUTPUT_VHDL = NO # Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice # sources only. Doxygen will then generate output that is more tailored for that # language. For instance, namespaces will be presented as modules, types will be # separated into more groups, etc. # The default value is: NO. OPTIMIZE_OUTPUT_SLICE = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and # language is one of the parsers supported by doxygen: IDL, Java, JavaScript, # Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL, # Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: # FortranFree, unknown formatted Fortran: Fortran. In the later case the parser # tries to guess whether the code is fixed or free formatted code, this is the # default for Fortran type files). For instance to make doxygen treat .inc files # as Fortran files (default is PHP), and .f files as C (default is Fortran), # use: inc=Fortran f=C. # # Note: For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise # the files are not read by doxygen. When specifying no_extension you should add # * to the FILE_PATTERNS. # # Note see also the list of default file extension mappings. EXTENSION_MAPPING = # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments # according to the Markdown format, which allows for more readable # documentation. See https://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you can # mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. # The default value is: YES. MARKDOWN_SUPPORT = YES # When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up # to that level are automatically included in the table of contents, even if # they do not have an id attribute. # Note: This feature currently applies only to Markdown headings. # Minimum value: 0, maximum value: 99, default value: 5. # This tag requires that the tag MARKDOWN_SUPPORT is set to YES. TOC_INCLUDE_HEADINGS = 0 # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by putting a % sign in front of the word or # globally by setting AUTOLINK_SUPPORT to NO. # The default value is: YES. AUTOLINK_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should set this # tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); # versus func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. # The default value is: NO. BUILTIN_STL_SUPPORT = YES # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. # The default value is: NO. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: # https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen # will parse them like normal C++ but will assume all classes use public instead # of private inheritance when no explicit protection keyword is present. # The default value is: NO. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate # getter and setter methods for a property. Setting this option to YES will make # doxygen to replace the get and set methods by a property in the documentation. # This will only work if the methods are indeed getting or setting a simple # type. If this is not the case, or you want to show the methods anyway, you # should set this option to NO. # The default value is: YES. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. # The default value is: NO. DISTRIBUTE_GROUP_DOC = NO # If one adds a struct or class to a group and this option is enabled, then also # any nested class or struct is added to the same group. By default this option # is disabled and one has to add nested compounds explicitly via \ingroup. # The default value is: NO. GROUP_NESTED_COMPOUNDS = NO # Set the SUBGROUPING tag to YES to allow class member groups of the same type # (for instance a group of public functions) to be put as a subgroup of that # type (e.g. under the Public Functions section). Set it to NO to prevent # subgrouping. Alternatively, this can be done per class using the # \nosubgrouping command. # The default value is: YES. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions # are shown inside the group in which they are included (e.g. using \ingroup) # instead of on a separate page (for HTML and Man pages) or section (for LaTeX # and RTF). # # Note that this feature does not work in combination with # SEPARATE_MEMBER_PAGES. # The default value is: NO. INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions # with only public data fields or simple typedef fields will be shown inline in # the documentation of the scope in which they are defined (i.e. file, # namespace, or group documentation), provided this scope is documented. If set # to NO, structs, classes, and unions are shown on a separate page (for HTML and # Man pages) or section (for LaTeX and RTF). # The default value is: NO. INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or # enum is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically be # useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. # The default value is: NO. TYPEDEF_HIDES_STRUCT = NO # The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This # cache is used to resolve symbols given their name and scope. Since this can be # an expensive process and often the same symbol appears multiple times in the # code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small # doxygen will become slower. If the cache is too large, memory is wasted. The # cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range # is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 # symbols. At the end of a run doxygen will report the cache usage and suggest # the optimal cache size from a speed point of view. # Minimum value: 0, maximum value: 9, default value: 0. LOOKUP_CACHE_SIZE = 0 # The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use # during processing. When set to 0 doxygen will based this on the number of # cores available in the system. You can set it explicitly to a value larger # than 0 to get more control over the balance between CPU load and processing # speed. At this moment only the input processing can be done using multiple # threads. Since this is still an experimental feature the default is set to 1, # which efficively disables parallel processing. Please report any issues you # encounter. Generating dot graphs in parallel is controlled by the # DOT_NUM_THREADS setting. # Minimum value: 0, maximum value: 32, default value: 1. NUM_PROC_THREADS = 1 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in # documentation are documented, even if no documentation was available. Private # class members and static file members will be hidden unless the # EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. # Note: This will also disable the warnings about undocumented members that are # normally produced when WARNINGS is set to YES. # The default value is: NO. EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will # be included in the documentation. # The default value is: NO. EXTRACT_PRIVATE = NO # If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual # methods of a class will be included in the documentation. # The default value is: NO. EXTRACT_PRIV_VIRTUAL = NO # If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal # scope will be included in the documentation. # The default value is: NO. EXTRACT_PACKAGE = NO # If the EXTRACT_STATIC tag is set to YES, all static members of a file will be # included in the documentation. # The default value is: NO. EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined # locally in source files will be included in the documentation. If set to NO, # only classes defined in header files are included. Does not have any effect # for Java sources. # The default value is: YES. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. If set to YES, local methods, # which are defined in the implementation section but not in the interface are # included in the documentation. If set to NO, only methods in the interface are # included. # The default value is: NO. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base name of # the file that contains the anonymous namespace. By default anonymous namespace # are hidden. # The default value is: NO. EXTRACT_ANON_NSPACES = NO # If this flag is set to YES, the name of an unnamed parameter in a declaration # will be determined by the corresponding definition. By default unnamed # parameters remain unnamed in the output. # The default value is: YES. RESOLVE_UNNAMED_PARAMS = YES # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation # section is generated. This option has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set # to NO, these classes will be included in the various overviews. This option # has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend # declarations. If set to NO, these declarations will be included in the # documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any # documentation blocks found inside the body of a function. If set to NO, these # blocks will be appended to the function's detailed documentation block. # The default value is: NO. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation that is typed after a # \internal command is included. If the tag is set to NO then the documentation # will be excluded. Set it to YES to include the internal documentation. # The default value is: NO. INTERNAL_DOCS = NO # With the correct setting of option CASE_SENSE_NAMES doxygen will better be # able to match the capabilities of the underlying filesystem. In case the # filesystem is case sensitive (i.e. it supports files in the same directory # whose names only differ in casing), the option must be set to YES to properly # deal with such files in case they appear in the input. For filesystems that # are not case sensitive the option should be be set to NO to properly deal with # output files written for symbols that only differ in casing, such as for two # classes, one named CLASS and the other named Class, and to also support # references to files without having to specify the exact matching casing. On # Windows (including Cygwin) and MacOS, users should typically set this option # to NO, whereas on Linux or other Unix flavors it should typically be set to # YES. # The default value is: system dependent. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with # their full class and namespace scopes in the documentation. If set to YES, the # scope will be hidden. # The default value is: NO. HIDE_SCOPE_NAMES = NO # If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will # append additional text to a page's title, such as Class Reference. If set to # YES the compound reference will be hidden. # The default value is: NO. HIDE_COMPOUND_REFERENCE= NO # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. SHOW_INCLUDE_FILES = YES # If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each # grouped member an include statement to the documentation, telling the reader # which file to include in order to use the member. # The default value is: NO. SHOW_GROUPED_MEMB_INC = NO # If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include # files with double quotes in the documentation rather than with sharp brackets. # The default value is: NO. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the # documentation for inline members. # The default value is: YES. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the # (detailed) documentation of file and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. # The default value is: YES. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. Note that # this will also influence the order of the classes in the class list. # The default value is: NO. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the # (brief and detailed) documentation of class members so that constructors and # destructors are listed first. If set to NO the constructors will appear in the # respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. # Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief # member documentation. # Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting # detailed member documentation. # The default value is: NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy # of group names into alphabetical order. If set to NO the group names will # appear in their defined order. # The default value is: NO. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by # fully-qualified names, including namespaces. If set to NO, the class list will # be sorted only by class name, not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the alphabetical # list. # The default value is: NO. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper # type resolution of all parameters of a function it will reject a match between # the prototype and the implementation of a member function even if there is # only one candidate or it is obvious which candidate to choose by doing a # simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still # accept a match between prototype and implementation in such cases. # The default value is: NO. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo # list. This list is created by putting \todo commands in the documentation. # The default value is: YES. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test # list. This list is created by putting \test commands in the documentation. # The default value is: YES. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug # list. This list is created by putting \bug commands in the documentation. # The default value is: YES. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) # the deprecated list. This list is created by putting \deprecated commands in # the documentation. # The default value is: YES. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional documentation # sections, marked by \if ... \endif and \cond # ... \endcond blocks. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the # initial value of a variable or macro / define can have for it to appear in the # documentation. If the initializer consists of more lines than specified here # it will be hidden. Use a value of 0 to hide initializers completely. The # appearance of the value of individual variables and macros / defines can be # controlled using \showinitializer or \hideinitializer command in the # documentation regardless of this setting. # Minimum value: 0, maximum value: 10000, default value: 30. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated at # the bottom of the documentation of classes and structs. If set to YES, the # list will mention the files that were used to generate the documentation. # The default value is: YES. SHOW_USED_FILES = YES # Set the SHOW_FILES tag to NO to disable the generation of the Files page. This # will remove the Files entry from the Quick Index and from the Folder Tree View # (if specified). # The default value is: YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces # page. This will remove the Namespaces entry from the Quick Index and from the # Folder Tree View (if specified). # The default value is: YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command command input-file, where command is the value of the # FILE_VERSION_FILTER tag, and input-file is the name of an input file provided # by doxygen. Whatever the program writes to standard output is used as the file # version. For an example see the documentation. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. You can # optionally specify a file name after the option, if omitted DoxygenLayout.xml # will be used as the name of the layout file. # # Note that if you run doxygen from a directory containing a file called # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE # tag is left empty. LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib # extension is automatically appended if omitted. This requires the bibtex tool # to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # search path. See also \cite for info how to create references. CITE_BIB_FILES = #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated to # standard output by doxygen. If QUIET is set to YES this implies that the # messages are off. # The default value is: NO. QUIET = YES # The WARNINGS tag can be used to turn on/off the warning messages that are # generated to standard error (stderr) by doxygen. If WARNINGS is set to YES # this implies that the warnings are on. # # Tip: Turn warnings on while writing the documentation. # The default value is: YES. WARNINGS = YES # If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: YES. WARN_IF_UNDOCUMENTED = YES # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some parameters # in a documented function, or documenting parameters that don't exist or using # markup commands wrongly. # The default value is: YES. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return # value. If set to NO, doxygen will only warn about wrong or incomplete # parameter documentation, but not about the absence of documentation. If # EXTRACT_ALL is set to YES then this flag will automatically be disabled. # The default value is: NO. WARN_NO_PARAMDOC = NO # If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when # a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS # then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but # at the end of the doxygen process doxygen will return with a non-zero status. # Possible values are: NO, YES and FAIL_ON_WARNINGS. # The default value is: NO. WARN_AS_ERROR = NO # The WARN_FORMAT tag determines the format of the warning messages that doxygen # can produce. The string should contain the $file, $line, and $text tags, which # will be replaced by the file and line number from which the warning originated # and the warning text. Optionally the format may contain $version, which will # be replaced by the version of the file (if it could be obtained via # FILE_VERSION_FILTER) # The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning and error # messages should be written. If left blank the output is written to standard # error (stderr). WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag is used to specify the files and/or directories that contain # documented source files. You may enter file names like myfile.cpp or # directories like /usr/src/myproject. Separate the files or directories with # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. INPUT = libopenmpt/dox/index.dox \ README.md \ libopenmpt/dox/dependencies.md \ libopenmpt/dox/gettingstarted.md \ libopenmpt/dox/packaging.md \ doc/contributing.md \ doc/libopenmpt_styleguide.md \ doc/openmpt_styleguide.md \ libopenmpt/dox/tests.md \ libopenmpt/dox/changelog.md \ doc/module_formats.md \ libopenmpt/libopenmpt.hpp \ libopenmpt/libopenmpt.h \ libopenmpt/libopenmpt_stream_callbacks_buffer.h \ libopenmpt/libopenmpt_stream_callbacks_fd.h \ libopenmpt/libopenmpt_stream_callbacks_file.h \ libopenmpt/libopenmpt_config.h \ libopenmpt/libopenmpt_version.h \ libopenmpt/libopenmpt_ext.hpp \ libopenmpt/libopenmpt_ext.h # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv # documentation (see: # https://www.gnu.org/software/libiconv/) for the list of possible encodings. # The default value is: UTF-8. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and # *.h) to filter out the source-files in the directories. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # read by doxygen. # # Note the list of default checked file patterns might differ from the list of # default file extension mappings. # # If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, # *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, # *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, # *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment), # *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, *.vhdl, # *.ucf, *.qsf and *.ice. FILE_PATTERNS = # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. # The default value is: NO. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. # The default value is: NO. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories use the pattern */test/* EXCLUDE_SYMBOLS = LIBOPENMPT_API \ LIBOPENMPT_CXX_API \ LIBOPENMPT_API_HELPER_EXPORT \ LIBOPENMPT_API_HELPER_IMPORT \ LIBOPENMPT_API_HELPER_PUBLIC \ LIBOPENMPT_API_HELPER_LOCAL \ OPENMPT_API_VERSION_HELPER_STRINGIZE \ OPENMPT_API_VERSION_STRINGIZE \ openmpt::detail # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include # command). EXAMPLE_PATH = examples/ \ LICENSE # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and # *.h) to filter out the source-files in the directories. If left blank all # files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude commands # irrespective of the value of the RECURSIVE tag. # The default value is: NO. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or directories # that contain images that are to be included in the documentation (see the # \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command: # # # # where is the value of the INPUT_FILTER tag, and is the # name of an input file. Doxygen will then use the output that the filter # program writes to standard output. If FILTER_PATTERNS is specified, this tag # will be ignored. # # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: pattern=filter # (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how # filters are used. If the FILTER_PATTERNS tag is empty or if none of the # patterns match the file name, INPUT_FILTER is applied. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will also be used to filter the input files that are used for # producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). # The default value is: NO. FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) and # it is also possible to disable source filtering for a specific pattern using # *.ext= (so without naming a filter). # This tag requires that the tag FILTER_SOURCE_FILES is set to YES. FILTER_SOURCE_PATTERNS = # If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page # (index.html). This can be useful if you have a project on for instance GitHub # and want to reuse the introduction page also for the doxygen output. USE_MDFILE_AS_MAINPAGE = #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will be # generated. Documented entities will be cross-referenced with these sources. # # Note: To get rid of all source code in the generated output, make sure that # also VERBATIM_HEADERS is set to NO. # The default value is: NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body of functions, # classes and enums directly into the documentation. # The default value is: NO. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any # special comment blocks from generated source code fragments. Normal C, C++ and # Fortran comments will always remain visible. # The default value is: YES. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES then for each documented # entity all documented functions referencing it will be listed. # The default value is: NO. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES then for each documented function # all documented entities called/used by that function will be listed. # The default value is: NO. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set # to YES then the hyperlinks from functions in REFERENCES_RELATION and # REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will # link to the documentation. # The default value is: YES. REFERENCES_LINK_SOURCE = YES # If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the # source code will show a tooltip with additional information such as prototype, # brief description and links to the definition and documentation. Since this # will make the HTML file larger and loading of large files a bit slower, you # can opt to disable this feature. # The default value is: YES. # This tag requires that the tag SOURCE_BROWSER is set to YES. SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will # point to the HTML generated by the htags(1) tool instead of doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system # (see https://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. # # To use it do the following: # - Install the latest version of global # - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file # - Make sure the INPUT points to the root of the source tree # - Run doxygen as normal # # Doxygen will invoke htags (and that will in turn invoke gtags), so these # tools must be available from the command line (i.e. in the search path). # # The result: instead of the source browser generated by doxygen, the links to # source code will now point to the output of htags. # The default value is: NO. # This tag requires that the tag SOURCE_BROWSER is set to YES. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a # verbatim copy of the header file for each class for which an include is # specified. Set to NO to disable this. # See also: Section \class. # The default value is: YES. VERBATIM_HEADERS = YES # If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the # clang parser (see: # http://clang.llvm.org/) for more accurate parsing at the cost of reduced # performance. This can be particularly helpful with template rich C++ code for # which doxygen's built-in parser lacks the necessary type information. # Note: The availability of this option depends on whether or not doxygen was # generated with the -Duse_libclang=ON option for CMake. # The default value is: NO. CLANG_ASSISTED_PARSING = NO # If clang assisted parsing is enabled and the CLANG_ADD_INC_PATHS tag is set to # YES then doxygen will add the directory of each input to the include path. # The default value is: YES. CLANG_ADD_INC_PATHS = YES # If clang assisted parsing is enabled you can provide the compiler with command # line options that you would normally use when invoking the compiler. Note that # the include paths will already be set by doxygen for the files and directories # specified with INPUT and INCLUDE_PATH. # This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. CLANG_OPTIONS = # If clang assisted parsing is enabled you can provide the clang parser with the # path to the directory containing a file called compile_commands.json. This # file is the compilation database (see: # http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the # options used when the source files were built. This is equivalent to # specifying the -p option to a clang tool, such as clang-check. These options # will then be passed to the parser. Any options specified with CLANG_OPTIONS # will be added as well. # Note: The availability of this option depends on whether or not doxygen was # generated with the -Duse_libclang=ON option for CMake. CLANG_DATABASE_PATH = #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all # compounds will be generated. Enable this if the project contains a lot of # classes, structs, unions or interfaces. # The default value is: YES. ALPHABETICAL_INDEX = YES # In case all classes in a project start with a common prefix, all classes will # be put under the same header in the alphabetical index. The IGNORE_PREFIX tag # can be used to specify a prefix (or a list of prefixes) that should be ignored # while generating the index headers. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. IGNORE_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output # The default value is: YES. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of # it. # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # generated HTML page (for example: .htm, .php, .asp). # The default value is: .html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a user-defined HTML header file for # each generated HTML page. If the tag is left blank doxygen will generate a # standard header. # # To get valid HTML the header file that includes any scripts and style sheets # that doxygen needs, which is dependent on the configuration options used (e.g. # the setting GENERATE_TREEVIEW). It is highly recommended to start with a # default header using # doxygen -w html new_header.html new_footer.html new_stylesheet.css # YourConfigFile # and then modify the file new_header.html. See also section "Doxygen usage" # for information on how to generate the default header that doxygen normally # uses. # Note: The header is subject to change so you typically have to regenerate the # default header when upgrading to a newer version of doxygen. For a description # of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each # generated HTML page. If the tag is left blank doxygen will generate a standard # footer. See HTML_HEADER for more information on how to generate a default # footer and what special commands can be used inside the footer. See also # section "Doxygen usage" for information on how to generate the default footer # that doxygen normally uses. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style # sheet that is used by each HTML page. It can be used to fine-tune the look of # the HTML output. If left blank doxygen will generate a default style sheet. # See also section "Doxygen usage" for information on how to generate the style # sheet that doxygen normally uses. # Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as # it is more robust and this tag (HTML_STYLESHEET) will in the future become # obsolete. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined # cascading style sheets that are included after the standard style sheets # created by doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the # standard style sheet and is therefore more robust against future updates. # Doxygen will copy the style sheet files to the output directory. # Note: The order of the extra style sheet files is of importance (e.g. the last # style sheet in the list overrules the setting of the previous ones in the # list). For an example see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that the # files will be copied as-is; there are no commands or markers available. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the style sheet and background images according to # this color. Hue is specified as an angle on a colorwheel, see # https://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors # in the HTML output. For a value of 0 the output will use grayscales only. A # value of 255 will produce the most vivid colors. # Minimum value: 0, maximum value: 255, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the # luminance component of the colors in the HTML output. Values below 100 # gradually make the output lighter, whereas values above 100 make the output # darker. The value divided by 100 is the actual gamma applied, so 80 represents # a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not # change the gamma. # Minimum value: 40, maximum value: 240, default value: 80. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting this # to YES can help to show when doxygen was last run and thus if the # documentation is up to date. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_TIMESTAMP = YES # If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML # documentation will contain a main index with vertical navigation menus that # are dynamically created via JavaScript. If disabled, the navigation index will # consists of multiple levels of tabs that are statically embedded in every HTML # page. Disable this option to support browsers that do not have JavaScript, # like the Qt help browser. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_MENUS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_SECTIONS = NO # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries # shown in the various tree structured indices initially; the user can expand # and collapse entries dynamically later on. Doxygen will expand the tree to # such a level that at most the specified number of entries are visible (unless # a fully collapsed tree already exceeds this amount). So setting the number of # entries 1 will produce a full collapsed tree by default. 0 is a special value # representing an infinite number of entries and will result in a full expanded # tree by default. # Minimum value: 0, maximum value: 9999, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development # environment (see: # https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To # create a documentation set, doxygen will generate a Makefile in the HTML # output directory. Running make will produce the docset in that directory and # running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at # startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy # genXcode/_index.html for more information. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_DOCSET = NO # This tag determines the name of the docset feed. A documentation feed provides # an umbrella under which multiple documentation sets from a single provider # (such as a company or product suite) can be grouped. # The default value is: Doxygen generated docs. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_FEEDNAME = "Doxygen generated docs" # This tag specifies a string that should uniquely identify the documentation # set bundle. This should be a reverse domain-name style string, e.g. # com.mycompany.MyDocSet. Doxygen will append .docset to the name. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_BUNDLE_ID = org.doxygen.Project # The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. # The default value is: org.doxygen.Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. # The default value is: Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop # (see: # https://www.microsoft.com/en-us/download/details.aspx?id=21138) on Windows. # # The HTML Help Workshop contains a compiler that can convert all HTML output # generated by doxygen into a single compiled HTML file (.chm). Compiled HTML # files are now used as the Windows 98 help format, and will replace the old # Windows help format (.hlp) on all Windows platforms in the future. Compressed # HTML files also contain an index, a table of contents, and you can search for # words in the documentation. The HTML workshop also contains a viewer for # compressed HTML files. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_HTMLHELP = NO # The CHM_FILE tag can be used to specify the file name of the resulting .chm # file. You can add a path in front of the file if the result should not be # written to the html output directory. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_FILE = # The HHC_LOCATION tag can be used to specify the location (absolute path # including file name) of the HTML help compiler (hhc.exe). If non-empty, # doxygen will try to run the HTML help compiler on the generated index.hhp. # The file has to be specified with full path. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. HHC_LOCATION = # The GENERATE_CHI flag controls if a separate .chi index file is generated # (YES) or that it should be included in the main .chm file (NO). # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. GENERATE_CHI = NO # The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) # and project file content. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_INDEX_ENCODING = # The BINARY_TOC flag controls whether a binary table of contents is generated # (YES) or a normal table of contents (NO) in the .chm file. Furthermore it # enables the Previous and Next buttons. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members to # the table of contents of the HTML help documentation and to the tree view. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that # can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help # (.qch) of the generated HTML documentation. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify # the file name of the resulting .qch file. The path specified is relative to # the HTML output folder. # This tag requires that the tag GENERATE_QHP is set to YES. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace # (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual # Folders (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom # Filters (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom # Filters (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = # The QHG_LOCATION tag can be used to specify the location (absolute path # including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to # run qhelpgenerator on the generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be # generated, together with the HTML files, they form an Eclipse help plugin. To # install this plugin and make it available under the help contents menu in # Eclipse, the contents of the directory containing the HTML and XML files needs # to be copied into the plugins directory of eclipse. The name of the directory # within the plugins directory should be the same as the ECLIPSE_DOC_ID value. # After copying Eclipse needs to be restarted before the help appears. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_ECLIPSEHELP = NO # A unique identifier for the Eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have this # name. Each documentation set should have its own identifier. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. ECLIPSE_DOC_ID = org.doxygen.Project # If you want full control over the layout of the generated HTML pages it might # be necessary to disable the index and replace it with your own. The # DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top # of each HTML page. A value of NO enables the index and the value YES disables # it. Since the tabs in the index contain the same information as the navigation # tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. DISABLE_INDEX = NO # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. If the tag # value is set to YES, a side panel will be generated containing a tree-like # index structure (just like the one that is generated for HTML Help). For this # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the # HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can # further fine-tune the look of the index. As an example, the default style # sheet generated by doxygen has an example that shows how to put an image at # the root of the tree instead of the PROJECT_NAME. Since the tree basically has # the same information as the tab index, you could consider setting # DISABLE_INDEX to YES when enabling this option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = YES # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # doxygen will group on one line in the generated HTML documentation. # # Note that a value of 0 will completely suppress the enum values from appearing # in the overview section. # Minimum value: 0, maximum value: 20, default value: 4. # This tag requires that the tag GENERATE_HTML is set to YES. ENUM_VALUES_PER_LINE = 4 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used # to set the initial width (in pixels) of the frame in which the tree is shown. # Minimum value: 0, maximum value: 1500, default value: 250. # This tag requires that the tag GENERATE_HTML is set to YES. TREEVIEW_WIDTH = 250 # If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to # external symbols imported via tag files in a separate window. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. EXT_LINKS_IN_WINDOW = NO # If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg # tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see # https://inkscape.org) to generate formulas as SVG images instead of PNGs for # the HTML output. These images will generally look nicer at scaled resolutions. # Possible values are: png (the default) and svg (looks nicer but requires the # pdf2svg or inkscape tool). # The default value is: png. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FORMULA_FORMAT = png # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful # doxygen run you need to manually remove any form_*.png images from the HTML # output directory to force them to be regenerated. # Minimum value: 8, maximum value: 50, default value: 10. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANSPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are not # supported properly for IE 6.0, but are supported on all modern browsers. # # Note that when changing this option you need to delete any form_*.png files in # the HTML output directory before the changes have effect. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_TRANSPARENT = YES # The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands # to create new LaTeX commands to be used in formulas as building blocks. See # the section "Including formulas" for details. FORMULA_MACROFILE = # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see # https://www.mathjax.org) which uses client side JavaScript for the rendering # instead of using pre-rendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path # to it using the MATHJAX_RELPATH option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. USE_MATHJAX = NO # When MathJax is enabled you can set the default output format to be used for # the MathJax output. See the MathJax site (see: # http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. # Possible values are: HTML-CSS (which is slower, but has the best # compatibility), NativeMML (i.e. MathML) and SVG. # The default value is: HTML-CSS. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_FORMAT = HTML-CSS # When MathJax is enabled you need to specify the location relative to the HTML # output directory using the MATHJAX_RELPATH option. The destination directory # should contain the MathJax.js script. For instance, if the mathjax directory # is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of # MathJax from https://www.mathjax.org before deployment. # The default value is: https://cdn.jsdelivr.net/npm/mathjax@2. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = http://www.mathjax.org/mathjax # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # of code that will be used on startup of the MathJax code. See the MathJax site # (see: # http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_CODEFILE = # When the SEARCHENGINE tag is enabled doxygen will generate a search box for # the HTML output. The underlying search engine uses javascript and DHTML and # should work on any modern browser. Note that when using HTML help # (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) # there is already a search function so this one should typically be disabled. # For large projects the javascript based search engine can be slow, then # enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to # search using the keyboard; to jump to the search box use + S # (what the is depends on the OS and browser, but it is typically # , /